use std::sync::{Arc, Mutex};
use tokio::sync::Semaphore;
use areev_core::error::{AreevError, Result};
use areev_store::{Areev, AreevOptions};
use crate::AreevFacade;
struct Shared {
facade: Mutex<Option<AreevFacade>>,
gate: Semaphore,
}
#[derive(Clone)]
pub struct AsyncFacade {
inner: Arc<Shared>,
}
impl AsyncFacade {
pub async fn open(path: &str, namespace: Option<&str>) -> Result<Self> {
let p = path.to_owned();
let ns = namespace.map(str::to_owned);
Self::built(move || {
let store = Areev::open(&p)?;
Ok(AreevFacade::with_session(store, ns, None))
})
.await
}
pub async fn open_with(
path: &str,
opts: AreevOptions,
namespace: Option<&str>,
) -> Result<Self> {
let p = path.to_owned();
let ns = namespace.map(str::to_owned);
Self::built(move || {
let store = Areev::open_with(&p, opts)?;
Ok(AreevFacade::with_session(store, ns, None))
})
.await
}
pub fn from_facade(facade: AreevFacade) -> Self {
Self {
inner: Arc::new(Shared {
facade: Mutex::new(Some(facade)),
gate: Semaphore::new(1),
}),
}
}
async fn built<F>(build: F) -> Result<Self>
where
F: FnOnce() -> Result<AreevFacade> + Send + 'static,
{
Ok(Self::from_facade(offload(build).await?))
}
pub async fn with<T, F>(&self, op: F) -> Result<T>
where
F: FnOnce(&AreevFacade) -> Result<T> + Send + 'static,
T: Send + 'static,
{
let _permit = self.inner.gate.acquire().await.map_err(|_| closed())?;
let inner = Arc::clone(&self.inner);
offload(move || {
let guard = inner.facade.lock().map_err(|_| poisoned())?;
let facade = guard.as_ref().ok_or_else(closed)?;
op(facade)
})
.await
}
pub async fn with_mut<T, F>(&self, op: F) -> Result<T>
where
F: FnOnce(&mut AreevFacade) -> Result<T> + Send + 'static,
T: Send + 'static,
{
let _permit = self.inner.gate.acquire().await.map_err(|_| closed())?;
let inner = Arc::clone(&self.inner);
offload(move || {
let mut guard = inner.facade.lock().map_err(|_| poisoned())?;
let facade = guard.as_mut().ok_or_else(closed)?;
op(facade)
})
.await
}
pub async fn close(self) -> Result<()> {
let _permit = self.inner.gate.acquire().await.map_err(|_| closed())?;
let taken = {
let mut guard = self.inner.facade.lock().map_err(|_| poisoned())?;
guard.take()
};
match taken {
Some(f) => {
offload(move || {
drop(f);
Ok(())
})
.await
}
None => Ok(()),
}
}
}
impl Drop for AsyncFacade {
fn drop(&mut self) {
if let Some(f) = Arc::get_mut(&mut self.inner)
.and_then(|s| s.facade.get_mut().ok())
.and_then(Option::take)
{
std::thread::spawn(move || drop(f));
}
}
}
async fn offload<T, F>(op: F) -> Result<T>
where
F: FnOnce() -> Result<T> + Send + 'static,
T: Send + 'static,
{
match tokio::task::spawn_blocking(op).await {
Ok(r) => r,
Err(e) => Err(AreevError::Storage(format!(
"areev blocking task failed: {e}"
))),
}
}
fn poisoned() -> AreevError {
AreevError::Storage("areev facade poisoned by a panic in another task".into())
}
fn closed() -> AreevError {
AreevError::Storage("areev facade already closed".into())
}