mirror of
https://github.com/lights0123/n-link.git
synced 2026-08-07 18:23:28 +00:00
fix(desktop): run blocking USB commands on the blocking pool
Plan A verification found close_device timing out despite doing no I/O: all commands were synchronous, Tauri runs sync commands on the main thread, so a hung list_dir blocked every subsequent command. The 11 libnspire-touching commands now spawn_blocking. enumerate and close_device stay sync — neither performs a transfer. Return types become concrete because impl Serialize cannot cross the async boundary. No change to command names or payloads.
This commit is contained in:
parent
81a77f1bcd
commit
15f219bd43
|
|
@ -122,25 +122,32 @@ pub fn enumerate<R: Runtime>(window: WebviewWindow<R>) -> Result<Vec<AddDevice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn open_device(bus_number: u8, address: u8) -> Result<impl Serialize, SerializedError> {
|
pub async fn open_device(
|
||||||
let device = if let Some(dev) = DEVICES.read().unwrap().get(&(bus_number, address)) {
|
bus_number: u8,
|
||||||
if !matches!(dev.state, DeviceState::Closed) {
|
address: u8,
|
||||||
return Err("Already open".into());
|
) -> Result<libnspire::info::Info, SerializedError> {
|
||||||
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
|
let device = if let Some(dev) = DEVICES.read().unwrap().get(&(bus_number, address)) {
|
||||||
|
if !matches!(dev.state, DeviceState::Closed) {
|
||||||
|
return Err("Already open".into());
|
||||||
|
};
|
||||||
|
dev.device.clone()
|
||||||
|
} else {
|
||||||
|
return Err("Failed to find device".into());
|
||||||
};
|
};
|
||||||
dev.device.clone()
|
let handle = libnspire::Handle::new(device.open()?)?;
|
||||||
} else {
|
let info = handle.info()?;
|
||||||
return Err("Failed to find device".into());
|
{
|
||||||
};
|
let mut guard = DEVICES.write().unwrap();
|
||||||
let handle = libnspire::Handle::new(device.open()?)?;
|
let device = guard
|
||||||
let info = handle.info()?;
|
.get_mut(&(bus_number, address))
|
||||||
{
|
.ok_or_else(|| anyhow::anyhow!("Device lost"))?;
|
||||||
let mut guard = DEVICES.write().unwrap();
|
device.state = DeviceState::Open(Arc::new(std::sync::Mutex::new(handle)), info.clone());
|
||||||
let device = guard
|
}
|
||||||
.get_mut(&(bus_number, address))
|
Ok(info)
|
||||||
.ok_or_else(|| anyhow::anyhow!("Device lost"))?;
|
})
|
||||||
device.state = DeviceState::Open(Arc::new(std::sync::Mutex::new(handle)), info.clone());
|
.await
|
||||||
}
|
.map_err(|e| SerializedError::from(e))?
|
||||||
Ok(info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -154,221 +161,261 @@ pub fn close_device(bus_number: u8, address: u8) -> Result<impl Serialize, Seria
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn update_device<R: Runtime>(
|
pub async fn update_device<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<libnspire::info::Info, SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
let info = err_wrap(handle.info(), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(info)
|
let info = err_wrap(handle.info(), dev, &window)?;
|
||||||
|
Ok(info)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn list_dir<R: Runtime>(
|
pub async fn list_dir<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: String,
|
path: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<Vec<FileInfo>, SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
let dir = err_wrap(handle.list_dir(&path), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
|
let dir = err_wrap(handle.list_dir(&path), dev, &window)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
dir
|
dir
|
||||||
.iter()
|
.iter()
|
||||||
.map(|file| FileInfo {
|
.map(|file| FileInfo {
|
||||||
path: file.name().to_string_lossy().to_string(),
|
path: file.name().to_string_lossy().to_string(),
|
||||||
is_dir: file.entry_type() == EntryType::Directory,
|
is_dir: file.entry_type() == EntryType::Directory,
|
||||||
date: file.date(),
|
date: file.date(),
|
||||||
size: file.size(),
|
size: file.size(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn download_file<R: Runtime>(
|
pub async fn download_file<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: (String, u64),
|
path: (String, u64),
|
||||||
dest: String,
|
dest: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let (file, size) = path;
|
};
|
||||||
let dest = PathBuf::from(dest);
|
let (file, size) = path;
|
||||||
let handle = get_open_dev(&dev)?;
|
let dest = PathBuf::from(dest);
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
let mut buf = vec![0; size as usize];
|
let handle = handle.lock().unwrap();
|
||||||
err_wrap(
|
let mut buf = vec![0; size as usize];
|
||||||
handle.read_file(
|
err_wrap(
|
||||||
&file,
|
handle.read_file(
|
||||||
&mut buf,
|
&file,
|
||||||
&mut progress_sender(&window, dev, size as usize),
|
&mut buf,
|
||||||
),
|
&mut progress_sender(&window, dev, size as usize),
|
||||||
dev,
|
),
|
||||||
&window,
|
dev,
|
||||||
)?;
|
&window,
|
||||||
if let Some(name) = file.split('/').last() {
|
)?;
|
||||||
File::create(dest.join(name))?.write_all(&buf)?;
|
if let Some(name) = file.split('/').last() {
|
||||||
}
|
File::create(dest.join(name))?.write_all(&buf)?;
|
||||||
Ok(())
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn upload_file<R: Runtime>(
|
pub async fn upload_file<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: String,
|
path: String,
|
||||||
src: String,
|
src: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let file = PathBuf::from(src);
|
};
|
||||||
let handle = get_open_dev(&dev)?;
|
let file = PathBuf::from(src);
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
let mut buf = vec![];
|
let handle = handle.lock().unwrap();
|
||||||
File::open(&file)?.read_to_end(&mut buf)?;
|
let mut buf = vec![];
|
||||||
let name = file
|
File::open(&file)?.read_to_end(&mut buf)?;
|
||||||
.file_name()
|
let name = file
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to get file name"))?
|
.file_name()
|
||||||
.to_string_lossy()
|
.ok_or_else(|| anyhow::anyhow!("Failed to get file name"))?
|
||||||
.to_string();
|
.to_string_lossy()
|
||||||
err_wrap(
|
.to_string();
|
||||||
handle.write_file(
|
err_wrap(
|
||||||
&format!("{}/{}", path, name),
|
handle.write_file(
|
||||||
&buf,
|
&format!("{}/{}", path, name),
|
||||||
&mut progress_sender(&window, dev, buf.len()),
|
&buf,
|
||||||
),
|
&mut progress_sender(&window, dev, buf.len()),
|
||||||
dev,
|
),
|
||||||
&window,
|
dev,
|
||||||
)?;
|
&window,
|
||||||
Ok(())
|
)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn upload_os<R: Runtime>(
|
pub async fn upload_os<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
src: String,
|
src: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
let mut buf = vec![];
|
let handle = handle.lock().unwrap();
|
||||||
File::open(&src)?.read_to_end(&mut buf)?;
|
let mut buf = vec![];
|
||||||
err_wrap(
|
File::open(&src)?.read_to_end(&mut buf)?;
|
||||||
handle.send_os(&buf, &mut progress_sender(&window, dev, buf.len())),
|
err_wrap(
|
||||||
dev,
|
handle.send_os(&buf, &mut progress_sender(&window, dev, buf.len())),
|
||||||
&window,
|
dev,
|
||||||
)?;
|
&window,
|
||||||
Ok(())
|
)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn delete_file<R: Runtime>(
|
pub async fn delete_file<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: String,
|
path: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
err_wrap(handle.delete_file(&path), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(())
|
err_wrap(handle.delete_file(&path), dev, &window)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn delete_dir<R: Runtime>(
|
pub async fn delete_dir<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: String,
|
path: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
err_wrap(handle.delete_dir(&path), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(())
|
err_wrap(handle.delete_dir(&path), dev, &window)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn create_nspire_dir<R: Runtime>(
|
pub async fn create_nspire_dir<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
path: String,
|
path: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
err_wrap(handle.create_dir(&path), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(())
|
err_wrap(handle.create_dir(&path), dev, &window)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn move_file<R: Runtime>(
|
pub async fn move_file<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
src: String,
|
src: String,
|
||||||
dest: String,
|
dest: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
err_wrap(handle.move_file(&src, &dest), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(())
|
err_wrap(handle.move_file(&src, &dest), dev, &window)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn copy<R: Runtime>(
|
pub async fn copy<R: Runtime>(
|
||||||
bus_number: u8,
|
bus_number: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
src: String,
|
src: String,
|
||||||
dest: String,
|
dest: String,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<impl Serialize, SerializedError> {
|
) -> Result<(), SerializedError> {
|
||||||
let dev = DevId {
|
tauri::async_runtime::spawn_blocking(move || {
|
||||||
bus_number,
|
let dev = DevId {
|
||||||
address,
|
bus_number,
|
||||||
};
|
address,
|
||||||
let handle = get_open_dev(&dev)?;
|
};
|
||||||
let handle = handle.lock().unwrap();
|
let handle = get_open_dev(&dev)?;
|
||||||
err_wrap(handle.copy_file(&src, &dest), dev, &window)?;
|
let handle = handle.lock().unwrap();
|
||||||
Ok(())
|
err_wrap(handle.copy_file(&src, &dest), dev, &window)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| SerializedError::from(e))?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue