use std::{
future,
sync::{Arc, Mutex},
task::{Poll, Waker},
};
use crate::{Database, Transaction, migrate::Schema};
pub struct DatabaseAsync<S> {
inner: Arc<Database<S>>,
}
impl<S> Clone for DatabaseAsync<S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<S: 'static + Send + Sync + Schema> DatabaseAsync<S> {
pub fn new(db: Arc<Database<S>>) -> Self {
DatabaseAsync { inner: db }
}
#[doc = include_str!("database/transaction.md")]
pub async fn transaction<R: 'static + Send>(
&self,
f: impl 'static + Send + FnOnce(&'static Transaction<S>) -> R,
) -> R {
let db = self.inner.clone();
async_run(move || db.transaction_local(f)).await
}
#[doc = include_str!("database/transaction_mut.md")]
pub async fn transaction_mut<O: 'static + Send, E: 'static + Send>(
&self,
f: impl 'static + Send + FnOnce(&'static mut Transaction<S>) -> Result<O, E>,
) -> Result<O, E> {
let db = self.inner.clone();
async_run(move || db.transaction_mut_local(f)).await
}
#[doc = include_str!("database/transaction_mut_ok.md")]
pub async fn transaction_mut_ok<R: 'static + Send>(
&self,
f: impl 'static + Send + FnOnce(&'static mut Transaction<S>) -> R,
) -> R {
self.transaction_mut(|txn| Ok::<R, std::convert::Infallible>(f(txn)))
.await
.unwrap()
}
}
async fn async_run<R: 'static + Send>(f: impl 'static + Send + FnOnce() -> R) -> R {
pub struct WakeOnDrop {
waker: Mutex<Waker>,
}
impl Drop for WakeOnDrop {
#[cfg_attr(test, mutants::skip)] fn drop(&mut self) {
self.waker.lock().unwrap().wake_by_ref();
}
}
let wake_on_drop = Arc::new(WakeOnDrop {
waker: Mutex::new(Waker::noop().clone()),
});
let weak = Arc::downgrade(&wake_on_drop);
let handle = std::thread::spawn(move || {
let _wake_on_drop = wake_on_drop;
f()
});
future::poll_fn(|cx| {
if let Some(wake_on_drop) = weak.upgrade() {
wake_on_drop.waker.lock().unwrap().clone_from(cx.waker());
Poll::Pending
} else {
Poll::Ready(())
}
})
.await;
match handle.join() {
Ok(val) => val,
Err(err) => std::panic::resume_unwind(err),
}
}