use std::borrow::Cow;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::sync::Arc;
use crate::envs::{Env, EnvInner};
use crate::mdb::error::mdb_result;
use crate::mdb::ffi;
use crate::Result;
#[repr(transparent)]
pub struct RoTxn<'e, T = AnyTls> {
inner: RoTxnInner<'e>,
_tls_marker: PhantomData<&'e T>,
}
struct RoTxnInner<'e> {
pub(crate) txn: Option<NonNull<ffi::MDB_txn>>,
env: Cow<'e, Arc<EnvInner>>,
}
impl<'e, T> RoTxn<'e, T> {
pub(crate) fn new(env: &'e Env<T>) -> Result<RoTxn<'e, T>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe {
mdb_result(ffi::mdb_txn_begin(
env.env_mut_ptr().as_mut(),
ptr::null_mut(),
ffi::MDB_RDONLY,
&mut txn,
))?
};
Ok(RoTxn {
inner: RoTxnInner { txn: NonNull::new(txn), env: Cow::Borrowed(&env.inner) },
_tls_marker: PhantomData,
})
}
pub(crate) fn static_read_txn(env: Env<T>) -> Result<RoTxn<'static, T>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe {
mdb_result(ffi::mdb_txn_begin(
env.env_mut_ptr().as_mut(),
ptr::null_mut(),
ffi::MDB_RDONLY,
&mut txn,
))?
};
Ok(RoTxn {
inner: RoTxnInner { txn: NonNull::new(txn), env: Cow::Owned(env.inner) },
_tls_marker: PhantomData,
})
}
pub(crate) fn txn_ptr(&self) -> NonNull<ffi::MDB_txn> {
self.inner.txn.unwrap()
}
pub(crate) fn env_mut_ptr(&self) -> NonNull<ffi::MDB_env> {
self.inner.env.env_mut_ptr()
}
pub fn id(&self) -> usize {
unsafe { ffi::mdb_txn_id(self.inner.txn.unwrap().as_ptr()) }
}
pub fn commit(mut self) -> Result<()> {
let mut txn = self.inner.txn.take().unwrap();
let result = unsafe { mdb_result(ffi::mdb_txn_commit(txn.as_mut())) };
result.map_err(Into::into)
}
}
impl<'a> Deref for RoTxn<'a, WithTls> {
type Target = RoTxn<'a, AnyTls>;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(self) }
}
}
#[cfg(master3)]
impl std::ops::DerefMut for RoTxn<'_, WithTls> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl<'a> Deref for RoTxn<'a, WithoutTls> {
type Target = RoTxn<'a, AnyTls>;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(self) }
}
}
#[cfg(master3)]
impl std::ops::DerefMut for RoTxn<'_, WithoutTls> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl<T> Drop for RoTxn<'_, T> {
fn drop(&mut self) {
if let Some(mut txn) = self.inner.txn.take() {
unsafe { ffi::mdb_txn_abort(txn.as_mut()) }
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum WithTls {}
#[derive(Debug, PartialEq, Eq)]
pub enum WithoutTls {}
pub enum AnyTls {}
pub trait TlsUsage {
const ENABLED: bool;
}
impl TlsUsage for WithTls {
const ENABLED: bool = true;
}
impl TlsUsage for WithoutTls {
const ENABLED: bool = false;
}
impl TlsUsage for AnyTls {
const ENABLED: bool = false;
}
unsafe impl Send for RoTxn<'_, WithoutTls> {}
pub struct RwTxn<'p> {
pub(crate) txn: RoTxn<'p, WithoutTls>,
}
impl<'p> RwTxn<'p> {
pub(crate) fn new<T>(env: &'p Env<T>) -> Result<RwTxn<'p>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe {
mdb_result(ffi::mdb_txn_begin(
env.env_mut_ptr().as_mut(),
ptr::null_mut(),
0,
&mut txn,
))?
};
Ok(RwTxn {
txn: RoTxn {
inner: RoTxnInner { txn: NonNull::new(txn), env: Cow::Borrowed(&env.inner) },
_tls_marker: PhantomData,
},
})
}
pub(crate) fn nested<T>(env: &'p Env<T>, parent: &'p mut RwTxn) -> Result<RwTxn<'p>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
let parent_ptr: *mut ffi::MDB_txn = unsafe { parent.txn.inner.txn.unwrap().as_mut() };
unsafe {
mdb_result(ffi::mdb_txn_begin(env.env_mut_ptr().as_mut(), parent_ptr, 0, &mut txn))?
};
Ok(RwTxn {
txn: RoTxn {
inner: RoTxnInner { txn: NonNull::new(txn), env: Cow::Borrowed(&env.inner) },
_tls_marker: PhantomData,
},
})
}
pub(crate) fn env_mut_ptr(&self) -> NonNull<ffi::MDB_env> {
self.txn.inner.env.env_mut_ptr()
}
pub fn nested_read_txn<'a>(&'a self) -> Result<RoTxn<'a, WithoutTls>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
let parent_ptr: *mut ffi::MDB_txn = unsafe { self.inner.txn.unwrap().as_mut() };
unsafe {
mdb_result(ffi::mdb_txn_begin(
self.inner.env.env_mut_ptr().as_mut(),
parent_ptr,
ffi::MDB_RDONLY,
&mut txn,
))?
};
Ok(RoTxn {
inner: RoTxnInner { txn: NonNull::new(txn), env: self.inner.env.clone() },
_tls_marker: PhantomData,
})
}
pub fn commit(mut self) -> Result<()> {
let mut txn = self.txn.inner.txn.take().unwrap();
let result = unsafe { mdb_result(ffi::mdb_txn_commit(txn.as_mut())) };
result.map_err(Into::into)
}
pub fn abort(mut self) {
let mut txn = self.txn.inner.txn.take().unwrap();
unsafe { ffi::mdb_txn_abort(txn.as_mut()) }
}
}
impl<'p> Deref for RwTxn<'p> {
type Target = RoTxn<'p, WithoutTls>;
fn deref(&self) -> &Self::Target {
&self.txn
}
}
#[cfg(master3)]
impl std::ops::DerefMut for RwTxn<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.txn
}
}
#[cfg(test)]
mod tests {
#[test]
fn ro_txns_are_send() {
use crate::{RoTxn, WithoutTls};
fn is_send<T: Send>() {}
is_send::<RoTxn<WithoutTls>>();
}
#[test]
fn rw_txns_are_send() {
use crate::RwTxn;
fn is_send<T: Send>() {}
is_send::<RwTxn>();
}
}