diff --git a/src/ffi/python/controllers.rs b/src/ffi/python/controllers.rs index 359c9de..fadab5c 100644 --- a/src/ffi/python/controllers.rs +++ b/src/ffi/python/controllers.rs @@ -16,22 +16,22 @@ impl CursorController { buffer: path, user: None, }; - self.send(pos).await + super::AllowThreads(self.send(pos)).await } #[pyo3(name = "try_recv")] async fn pytry_recv(&self) -> crate::Result> { - self.try_recv().await + super::AllowThreads(self.try_recv()).await } #[pyo3(name = "recv")] async fn pyrecv(&self) -> crate::Result { - self.recv().await + super::AllowThreads(self.recv()).await } #[pyo3(name = "poll")] async fn pypoll(&self) -> crate::Result<()> { - self.poll().await + super::AllowThreads(self.poll()).await } #[pyo3(name = "stop")] @@ -45,7 +45,7 @@ impl CursorController { impl BufferController { #[pyo3(name = "content")] async fn pycontent(&self) -> crate::Result { - self.content().await + super::AllowThreads(Box::pin(self.content())).await } #[pyo3(name = "send")] @@ -56,22 +56,22 @@ impl BufferController { content: txt, hash: None, }; - self.send(op).await + super::AllowThreads(self.send(op)).await } #[pyo3(name = "try_recv")] async fn pytry_recv(&self) -> crate::Result> { - self.try_recv().await + super::AllowThreads(self.try_recv()).await } #[pyo3(name = "recv")] async fn pyrecv(&self) -> crate::Result { - self.recv().await + super::AllowThreads(self.recv()).await } #[pyo3(name = "poll")] async fn pypoll(&self) -> crate::Result<()> { - self.poll().await + super::AllowThreads(self.poll()).await } } diff --git a/src/ffi/python/mod.rs b/src/ffi/python/mod.rs index a455b4f..a1c30ef 100644 --- a/src/ffi/python/mod.rs +++ b/src/ffi/python/mod.rs @@ -3,6 +3,11 @@ pub mod controllers; pub mod workspace; use std::sync::Arc; +use std::{ + future::Future, + pin::{pin, Pin}, + task::{Context, Poll}, +}; use crate::{ api::{Cursor, TextChange}, @@ -14,6 +19,30 @@ use pyo3::exceptions::{PyConnectionError, PyRuntimeError, PySystemError}; use pyo3::prelude::*; use tokio::sync::{mpsc, Mutex}; +fn tokio() -> &'static tokio::runtime::Runtime { + use std::sync::OnceLock; + static RT: OnceLock = OnceLock::new(); + RT.get_or_init(|| tokio::runtime::Runtime::new().unwrap()) +} + +// workaround to allow the GIL to be released across awaits +struct AllowThreads(F); + +impl Future for AllowThreads +where + F: Future + Unpin + Send, + F::Output: Send, +{ + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let waker = cx.waker(); + Python::with_gil(|gil| { + gil.allow_threads(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker))) + }) + } +} + impl From for PyErr { fn from(value: crate::Error) -> Self { match value {