Release GIL using py.allow_threads while long running rust operations are running

This commit is contained in:
Caelan Sayler
2025-09-24 23:29:45 +01:00
committed by Caelan
parent ea50914cea
commit 3df255b8a6

View File

@@ -40,8 +40,9 @@ impl UpdateManagerWrapper {
pending.map(Into::into) pending.map(Into::into)
} }
pub fn check_for_updates(&mut self) -> Result<Option<PyUpdateInfo>> { pub fn check_for_updates(&mut self, py: Python) -> Result<Option<PyUpdateInfo>> {
let update_check = self.inner.check_for_updates()?; // Release GIL during network operation
let update_check = py.allow_threads(|| self.inner.check_for_updates())?;
match update_check { match update_check {
UpdateCheck::UpdateAvailable(updates) => { UpdateCheck::UpdateAvailable(updates) => {
let py_updates = PyUpdateInfo::from(updates); let py_updates = PyUpdateInfo::from(updates);
@@ -53,7 +54,7 @@ impl UpdateManagerWrapper {
} }
#[pyo3(signature = (update_info, progress_callback = None))] #[pyo3(signature = (update_info, progress_callback = None))]
pub fn download_updates(&mut self, update_info: PyUpdateInfo, progress_callback: Option<PyObject>) -> Result<()> { pub fn download_updates(&mut self, py: Python, update_info: PyUpdateInfo, progress_callback: Option<PyObject>) -> Result<()> {
// Convert PyUpdateInfo back to rust UpdateInfo // Convert PyUpdateInfo back to rust UpdateInfo
let rust_update_info: UpdateInfo = update_info.into(); let rust_update_info: UpdateInfo = update_info.into();
@@ -61,27 +62,39 @@ impl UpdateManagerWrapper {
// Create a channel for progress updates // Create a channel for progress updates
let (sender, receiver) = mpsc::channel::<i16>(); let (sender, receiver) = mpsc::channel::<i16>();
// Spawn a thread to handle progress updates // Clone the callback for the thread
let progress_thread = thread::spawn(move || { let callback_clone = callback.clone_ref(py);
while let Ok(progress) = receiver.recv() {
Python::with_gil(|py| { // Release the GIL before starting the download
if let Err(e) = callback.call1(py, (progress,)) { py.allow_threads(|| {
// Log error but continue - don't break the download // Spawn a thread to handle progress updates
eprintln!("Progress callback error: {}", e); let progress_thread = thread::spawn(move || {
} while let Ok(progress) = receiver.recv() {
}); // Acquire GIL only when needed to call the callback
} Python::with_gil(|py| {
}); if let Err(e) = callback_clone.call1(py, (progress,)) {
// Log error but continue - don't break the download
eprintln!("Progress callback error: {}", e);
}
});
}
});
// Call download with the sender // Call download with the sender
let result = self.inner.download_updates(&rust_update_info, Some(sender))?; let result = self.inner.download_updates(&rust_update_info, Some(sender));
// Wait for the progress thread to finish // Wait for the progress thread to finish
let _ = progress_thread.join(); let _ = progress_thread.join();
Ok(result)
result
})?;
Ok(())
} else { } else {
// No progress callback provided // No progress callback provided - still release GIL for the download
self.inner.download_updates(&rust_update_info, None)?; py.allow_threads(|| {
self.inner.download_updates(&rust_update_info, None)
})?;
Ok(()) Ok(())
} }
} }