feat(python): summon black magic to allow possible release of the GIL between .awaits

plus nice static runtime singleton fetcher/instantiator
This commit is contained in:
cschen 2024-08-17 01:12:35 +02:00
parent 5acca5ce3e
commit 3fe6d224e3
2 changed files with 38 additions and 9 deletions

View file

@ -16,22 +16,22 @@ impl CursorController {
buffer: path, buffer: path,
user: None, user: None,
}; };
self.send(pos).await super::AllowThreads(self.send(pos)).await
} }
#[pyo3(name = "try_recv")] #[pyo3(name = "try_recv")]
async fn pytry_recv(&self) -> crate::Result<Option<Cursor>> { async fn pytry_recv(&self) -> crate::Result<Option<Cursor>> {
self.try_recv().await super::AllowThreads(self.try_recv()).await
} }
#[pyo3(name = "recv")] #[pyo3(name = "recv")]
async fn pyrecv(&self) -> crate::Result<Cursor> { async fn pyrecv(&self) -> crate::Result<Cursor> {
self.recv().await super::AllowThreads(self.recv()).await
} }
#[pyo3(name = "poll")] #[pyo3(name = "poll")]
async fn pypoll(&self) -> crate::Result<()> { async fn pypoll(&self) -> crate::Result<()> {
self.poll().await super::AllowThreads(self.poll()).await
} }
#[pyo3(name = "stop")] #[pyo3(name = "stop")]
@ -45,7 +45,7 @@ impl CursorController {
impl BufferController { impl BufferController {
#[pyo3(name = "content")] #[pyo3(name = "content")]
async fn pycontent(&self) -> crate::Result<String> { async fn pycontent(&self) -> crate::Result<String> {
self.content().await super::AllowThreads(Box::pin(self.content())).await
} }
#[pyo3(name = "send")] #[pyo3(name = "send")]
@ -56,22 +56,22 @@ impl BufferController {
content: txt, content: txt,
hash: None, hash: None,
}; };
self.send(op).await super::AllowThreads(self.send(op)).await
} }
#[pyo3(name = "try_recv")] #[pyo3(name = "try_recv")]
async fn pytry_recv(&self) -> crate::Result<Option<TextChange>> { async fn pytry_recv(&self) -> crate::Result<Option<TextChange>> {
self.try_recv().await super::AllowThreads(self.try_recv()).await
} }
#[pyo3(name = "recv")] #[pyo3(name = "recv")]
async fn pyrecv(&self) -> crate::Result<TextChange> { async fn pyrecv(&self) -> crate::Result<TextChange> {
self.recv().await super::AllowThreads(self.recv()).await
} }
#[pyo3(name = "poll")] #[pyo3(name = "poll")]
async fn pypoll(&self) -> crate::Result<()> { async fn pypoll(&self) -> crate::Result<()> {
self.poll().await super::AllowThreads(self.poll()).await
} }
} }

View file

@ -3,6 +3,11 @@ pub mod controllers;
pub mod workspace; pub mod workspace;
use std::sync::Arc; use std::sync::Arc;
use std::{
future::Future,
pin::{pin, Pin},
task::{Context, Poll},
};
use crate::{ use crate::{
api::{Cursor, TextChange}, api::{Cursor, TextChange},
@ -14,6 +19,30 @@ use pyo3::exceptions::{PyConnectionError, PyRuntimeError, PySystemError};
use pyo3::prelude::*; use pyo3::prelude::*;
use tokio::sync::{mpsc, Mutex}; use tokio::sync::{mpsc, Mutex};
fn tokio() -> &'static tokio::runtime::Runtime {
use std::sync::OnceLock;
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
RT.get_or_init(|| tokio::runtime::Runtime::new().unwrap())
}
// workaround to allow the GIL to be released across awaits
struct AllowThreads<F>(F);
impl<F> Future for AllowThreads<F>
where
F: Future + Unpin + Send,
F::Output: Send,
{
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let waker = cx.waker();
Python::with_gil(|gil| {
gil.allow_threads(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker)))
})
}
}
impl From<crate::Error> for PyErr { impl From<crate::Error> for PyErr {
fn from(value: crate::Error) -> Self { fn from(value: crate::Error) -> Self {
match value { match value {