1#![no_std]
2#![doc = include_str!("../README.md")]
3extern crate alloc;
4
5#[cfg(not(miri))]
6mod auxv;
7mod config;
8mod pool;
9mod utils;
10#[cfg_attr(miri, path = "vdso_miri.rs")]
11mod vdso;
12use core::ffi::c_uint;
13use linux_raw_sys::errno;
14pub use pool::Pool;
15use pool::Ptr;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Error {
20 PoolPoisoned,
23 NotSupported,
26 AllocationFailure,
28 Errno(i32),
30}
31
32impl core::fmt::Display for Error {
33 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34 match self {
35 Error::NotSupported => write!(f, "Operation not supported on this platform"),
36 Error::AllocationFailure => write!(f, "Failed to allocate memory"),
37 Error::Errno(e) => write!(f, "System call failed with error code: {e}"),
38 Error::PoolPoisoned => write!(f, "Memory pool has been poisoned"),
39 }
40 }
41}
42
43impl core::error::Error for Error {}
44
45pub struct LocalState<'a> {
74 state: Ptr,
75 pool: &'a Pool,
76 #[cfg(debug_assertions)]
77 inflight: bool,
78}
79
80impl<'a> LocalState<'a> {
81 pub fn new(pool: &'a Pool) -> Result<Self, Error> {
83 let state = pool.get()?;
84 Ok(Self {
85 state,
86 pool,
87 #[cfg(debug_assertions)]
88 inflight: false,
89 })
90 }
91 pub fn try_fill(&mut self, buf: &mut [u8], flag: c_uint) -> Result<usize, Error> {
94 let function = self.pool.config.function;
95 let state = self.state.0.as_ptr();
96 let state_length = self.pool.config.params.size_of_opaque_states as usize;
97 let buffer_len = buf.len();
98 #[cfg(debug_assertions)]
99 {
100 debug_assert!(
101 !self.inflight,
102 "LocalState is already in use, reentrancy detected"
103 );
104 self.inflight = true;
105 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
106 }
107 unsafe {
108 let result = function(
109 buf.as_mut_ptr() as *mut _,
110 buffer_len,
111 flag,
112 state,
113 state_length,
114 );
115 #[cfg(debug_assertions)]
116 {
117 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
119 self.inflight = false;
120 }
121 if result < 0 {
122 return Err(Error::Errno(-result));
123 }
124 Ok(result as usize)
125 }
126 }
127
128 pub fn fill(&mut self, mut buf: &mut [u8], flag: c_uint) -> Result<(), Error> {
131 while !buf.is_empty() {
132 match self.try_fill(buf, flag) {
133 Ok(filled) => {
134 buf = &mut buf[filled..];
135 continue;
136 }
137 Err(Error::Errno(e)) if e == errno::EAGAIN as i32 || e == errno::EINTR as i32 => {
138 continue;
139 }
140 Err(e) => {
141 return Err(e);
142 }
143 }
144 }
145 Ok(())
146 }
147}
148
149impl<'a> Drop for LocalState<'a> {
150 fn drop(&mut self) {
151 let state = self.state;
152 self.pool.recycle(state);
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 extern crate std;
159 use std::cell::RefCell;
160
161 use alloc::vec::Vec;
162
163 use super::*;
164 #[test]
165 fn get_local_state() {
166 let pool = Pool::new().expect("Failed to create shared pool");
167 _ = LocalState::new(&pool).expect("Failed to create local state");
168 }
169
170 #[test]
171 fn fill_local_state() {
172 let pool = Pool::new().expect("Failed to create shared pool");
173 let mut local_state = LocalState::new(&pool).unwrap();
174 let mut buf = [0u8; 64];
175 let res = local_state.fill(&mut buf, 0);
176 assert!(res.is_ok(), "Failed to fill local state: {:?}", res);
177 assert!(buf.iter().any(|&x| x != 0), "Buffer should not be empty");
178 }
179
180 #[test]
181 fn multi_local_state() {
182 let pool = Pool::new().expect("Failed to create shared pool");
183 let mut states = Vec::new();
184 for _ in 0..128 {
185 let local_state = LocalState::new(&pool).unwrap();
186 states.push(local_state);
187 }
188 for state in states.iter_mut() {
189 let mut buf = [0u8; 64];
190 let res = state.fill(&mut buf, 0);
191 assert!(res.is_ok(), "Failed to fill local state: {:?}", res);
192 assert!(buf.iter().any(|&x| x != 0), "Buffer should not be empty");
193 }
194 }
195
196 #[test]
197 fn parallel_local_state() {
198 let pool = Pool::new().expect("Failed to create shared pool");
199 std::thread::scope(|scope| {
200 let pool = &pool;
201 for _ in 0..16 {
202 scope.spawn(|| {
203 for _ in 0..16 {
204 let mut local_state = LocalState::new(pool).unwrap();
205 let mut buf = [0u8; 64];
206 let res = local_state.fill(&mut buf, 0);
207 assert!(res.is_ok(), "Failed to fill local state: {:?}", res);
208 assert!(buf.iter().any(|&x| x != 0), "Buffer should not be empty");
209 }
210 });
211 }
212 });
213 }
214
215 #[test]
216 fn global_state_test() {
217 fn global_pool() -> &'static Pool {
218 static GLOBAL_STATE: std::sync::LazyLock<Pool> =
219 std::sync::LazyLock::new(|| Pool::new().expect("Failed to create global pool"));
220 &GLOBAL_STATE
221 }
222 fn fill(buf: &mut [u8], flag: c_uint) -> Result<(), Error> {
223 std::thread_local! {
224 static LOCAL_STATE: RefCell<LocalState<'static>> = RefCell::new(LocalState::new(global_pool()).expect("Failed to create local state"));
225 }
226 LOCAL_STATE.with(|local_state| {
227 let mut state = local_state.borrow_mut();
228 state.fill(buf, flag)
229 })
230 }
231
232 std::thread::scope(|scope| {
233 for _ in 0..16 {
234 scope.spawn(|| {
235 for _ in 0..16 {
236 let mut buf = [0u8; 64];
237 let res = fill(&mut buf, 0);
238 assert!(res.is_ok(), "Failed to fill global state: {:?}", res);
239 assert!(buf.iter().any(|&x| x != 0), "Buffer should not be empty");
240 }
241 });
242 }
243 });
244 }
245}