1use std::sync::Arc;
2use std::{fmt, io};
3
4type Callback = dyn Fn(i32, u64, usize) -> io::Result<()> + Send + Sync;
5
6struct Inner {
7 #[cfg_attr(not(unix), allow(dead_code))]
8 callback: Box<Callback>,
9}
10
11#[derive(Clone)]
23pub struct WriteAdmission(Arc<Inner>);
24
25impl WriteAdmission {
26 pub fn new<F>(callback: F) -> Self
28 where
29 F: Fn(i32, u64, usize) -> io::Result<()> + Send + Sync + 'static,
30 {
31 Self(Arc::new(Inner {
32 callback: Box::new(callback),
33 }))
34 }
35
36 #[cfg(unix)]
37 pub(crate) fn context(&self) -> *mut std::ffi::c_void {
38 Arc::as_ptr(&self.0).cast_mut().cast()
39 }
40}
41
42impl PartialEq for WriteAdmission {
43 fn eq(&self, other: &Self) -> bool {
44 Arc::ptr_eq(&self.0, &other.0)
45 }
46}
47
48impl fmt::Debug for WriteAdmission {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("WriteAdmission").finish_non_exhaustive()
51 }
52}
53
54#[cfg(unix)]
55pub(crate) unsafe extern "C" fn admit_write(
56 context: *mut std::ffi::c_void,
57 fd: crate::mdb::ffi::mdb_filehandle_t,
58 offset: u64,
59 length: usize,
60) -> libc::c_int {
61 if context.is_null() {
62 return libc::EIO;
63 }
64 let inner = unsafe { &*context.cast::<Inner>() };
65 let callback = &inner.callback;
66 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
67 callback(fd, offset, length)
68 })) {
69 Ok(Ok(())) => 0,
70 Ok(Err(error)) => error
71 .raw_os_error()
72 .filter(|code| *code > 0)
73 .unwrap_or(libc::EIO),
74 Err(_) => libc::EIO,
75 }
76}
77
78#[cfg(feature = "serde")]
80impl serde::Serialize for WriteAdmission {
81 fn serialize<S: serde::Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
82 Err(serde::ser::Error::custom(
83 "write admission callbacks cannot be serialized",
84 ))
85 }
86}
87
88#[cfg(feature = "serde")]
89impl<'de> serde::Deserialize<'de> for WriteAdmission {
90 fn deserialize<D: serde::Deserializer<'de>>(_: D) -> Result<Self, D::Error> {
91 Err(serde::de::Error::custom(
92 "write admission callbacks must be installed explicitly",
93 ))
94 }
95}
96
97#[cfg(all(test, unix))]
98mod tests;