1use std::cell::Cell;
13use std::future::Future;
14
15type ClaimFn = unsafe fn(src: *const u8, size: usize) -> *mut u8;
24
25type TryClaimFn = unsafe fn() -> (*mut u8, usize);
28
29type FreeFn = unsafe fn(ptr: *mut u8);
31
32type ClaimFreeFn = unsafe fn(slab_ptr: *const u8, ptr: *mut u8, chunk_idx: usize);
34
35thread_local! {
36 static SLAB_PTR: Cell<*const u8> = const { Cell::new(std::ptr::null()) };
38
39 static SLAB_CLAIM: Cell<ClaimFn> = const { Cell::new(no_slab_claim) };
41
42 static SLAB_FREE: Cell<FreeFn> = const { Cell::new(no_slab_free) };
44
45 static SLAB_TRY_CLAIM: Cell<TryClaimFn> = const { Cell::new(no_slab_try_claim) };
47
48 static SLAB_CLAIM_FREE: Cell<ClaimFreeFn> = const { Cell::new(no_slab_claim_free) };
50
51 static SLAB_SLOT_SIZE: Cell<usize> = const { Cell::new(0) };
53}
54
55unsafe fn no_slab_claim(_src: *const u8, _size: usize) -> *mut u8 {
58 panic!(
59 "spawn_slab() called without a slab configured — \
60 use Runtime::builder().slab_unbounded(slab) or .slab_bounded(slab)"
61 )
62}
63
64unsafe fn no_slab_free(_ptr: *mut u8) {
65 panic!("slab free called without a slab configured")
66}
67
68unsafe fn no_slab_try_claim() -> (*mut u8, usize) {
69 panic!(
70 "try_claim_slab()/claim_slab() called without a slab configured — \
71 use Runtime::builder().slab_unbounded(slab) or .slab_bounded(slab)"
72 )
73}
74
75unsafe fn no_slab_claim_free(_slab_ptr: *const u8, _ptr: *mut u8, _chunk_idx: usize) {
76 panic!("slab claim free called without a slab configured")
77}
78
79pub(crate) struct SlabTlsConfig {
85 pub(crate) slab_ptr: *const u8,
86 pub(crate) claim_fn: ClaimFn,
87 pub(crate) free_fn: FreeFn,
88 pub(crate) try_claim_fn: TryClaimFn,
89 pub(crate) claim_free_fn: ClaimFreeFn,
90 pub(crate) slot_size: usize,
91}
92
93pub(crate) fn install_slab(config: &SlabTlsConfig) -> SlabGuard {
95 let prev_ptr = SLAB_PTR.with(|c| c.replace(config.slab_ptr));
96 let prev_claim = SLAB_CLAIM.with(|c| c.replace(config.claim_fn));
97 let prev_free = SLAB_FREE.with(|c| c.replace(config.free_fn));
98 let prev_try_claim = SLAB_TRY_CLAIM.with(|c| c.replace(config.try_claim_fn));
99 let prev_claim_free = SLAB_CLAIM_FREE.with(|c| c.replace(config.claim_free_fn));
100 let prev_slot_size = SLAB_SLOT_SIZE.with(|c| c.replace(config.slot_size));
101 SlabGuard {
102 prev_ptr,
103 prev_claim,
104 prev_free,
105 prev_try_claim,
106 prev_claim_free,
107 prev_slot_size,
108 }
109}
110
111#[allow(clippy::struct_field_names)]
112pub(crate) struct SlabGuard {
113 prev_ptr: *const u8,
114 prev_claim: ClaimFn,
115 prev_free: FreeFn,
116 prev_try_claim: TryClaimFn,
117 prev_claim_free: ClaimFreeFn,
118 prev_slot_size: usize,
119}
120
121impl Drop for SlabGuard {
122 fn drop(&mut self) {
123 SLAB_PTR.with(|c| c.set(self.prev_ptr));
124 SLAB_CLAIM.with(|c| c.set(self.prev_claim));
125 SLAB_FREE.with(|c| c.set(self.prev_free));
126 SLAB_TRY_CLAIM.with(|c| c.set(self.prev_try_claim));
127 SLAB_CLAIM_FREE.with(|c| c.set(self.prev_claim_free));
128 SLAB_SLOT_SIZE.with(|c| c.set(self.prev_slot_size));
129 }
130}
131
132pub(crate) fn slab_spawn<F>(future: F, tracker_key: u32) -> *mut u8
144where
145 F: Future + 'static,
146 F::Output: 'static,
147{
148 let task = crate::task::new_joinable_slab(future, tracker_key, slab_free_task);
149 let size = std::mem::size_of_val(&task);
150 let src = std::ptr::from_ref(&task).cast::<u8>();
151
152 let claim = SLAB_CLAIM.with(Cell::get);
153 let ptr = unsafe { claim(src, size) };
155 assert!(!ptr.is_null(), "slab full — spawn_slab failed");
156
157 std::mem::forget(task);
159
160 ptr
161}
162
163unsafe fn slab_free_task(ptr: *mut u8) {
165 let free = SLAB_FREE.with(Cell::get);
166 unsafe { free(ptr) };
167}
168
169pub struct SlabClaim {
183 ptr: *mut u8,
184 slab_ptr: *const u8,
185 free: ClaimFreeFn,
186 chunk_idx: usize,
187 slot_size: usize,
188 _not_send: std::marker::PhantomData<std::rc::Rc<()>>,
190}
191
192impl SlabClaim {
193 pub fn spawn<F>(self, future: F) -> crate::task::JoinHandle<F::Output>
198 where
199 F: Future + 'static,
200 F::Output: 'static,
201 {
202 crate::runtime::with_executor(|exec| {
203 let tracker_key = exec.next_tracker_key();
204 let task = crate::task::new_joinable_slab(future, tracker_key, slab_free_task);
205 let size = std::mem::size_of_val(&task);
206
207 assert!(
208 size <= self.slot_size,
209 "task size ({size} bytes) exceeds slab slot size ({} bytes)",
210 self.slot_size,
211 );
212
213 let src = std::ptr::from_ref(&task).cast::<u8>();
214 unsafe { std::ptr::copy_nonoverlapping(src, self.ptr, size) };
216 std::mem::forget(task);
217
218 let ptr = self.ptr;
219 std::mem::forget(self);
221
222 exec.spawn_raw(ptr);
223 crate::task::JoinHandle::new(ptr)
224 })
225 }
226
227 pub fn as_ptr(&self) -> *mut u8 {
229 self.ptr
230 }
231
232 pub fn slot_size(&self) -> usize {
234 self.slot_size
235 }
236}
237
238impl Drop for SlabClaim {
239 fn drop(&mut self) {
240 unsafe { (self.free)(self.slab_ptr, self.ptr, self.chunk_idx) };
244 }
245}
246
247impl std::fmt::Debug for SlabClaim {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 f.debug_struct("SlabClaim")
250 .field("ptr", &self.ptr)
251 .field("slot_size", &self.slot_size)
252 .finish()
253 }
254}
255
256pub(crate) fn try_claim() -> Option<SlabClaim> {
267 let try_claim_fn = SLAB_TRY_CLAIM.with(Cell::get);
268 let (ptr, chunk_idx) = unsafe { try_claim_fn() };
270 if ptr.is_null() {
271 return None;
272 }
273
274 let slab_ptr = SLAB_PTR.with(Cell::get);
275 let free = SLAB_CLAIM_FREE.with(Cell::get);
276 let slot_size = SLAB_SLOT_SIZE.with(Cell::get);
277
278 Some(SlabClaim {
279 ptr,
280 slab_ptr,
281 free,
282 chunk_idx,
283 slot_size,
284 _not_send: std::marker::PhantomData,
285 })
286}
287
288pub(crate) fn claim() -> SlabClaim {
296 try_claim().expect("slab full — claim_slab failed")
297}
298
299pub(crate) fn make_unbounded_config<const S: usize>(slab_ptr: *const u8) -> SlabTlsConfig {
305 SlabTlsConfig {
306 slab_ptr,
307 claim_fn: unbounded_claim::<S>,
308 free_fn: unbounded_free::<S>,
309 try_claim_fn: unbounded_try_claim::<S>,
310 claim_free_fn: unbounded_claim_free::<S>,
311 slot_size: S,
312 }
313}
314
315pub(crate) fn make_bounded_config<const S: usize>(slab_ptr: *const u8) -> SlabTlsConfig {
317 SlabTlsConfig {
318 slab_ptr,
319 claim_fn: bounded_claim::<S>,
320 free_fn: bounded_free::<S>,
321 try_claim_fn: bounded_try_claim::<S>,
322 claim_free_fn: bounded_claim_free::<S>,
323 slot_size: S,
324 }
325}
326
327unsafe fn unbounded_claim<const S: usize>(src: *const u8, size: usize) -> *mut u8 {
330 let slab_ptr = SLAB_PTR.with(Cell::get);
331 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
332 unsafe { slab.alloc_raw(src, size) }
333}
334
335unsafe fn unbounded_free<const S: usize>(ptr: *mut u8) {
336 let slab_ptr = SLAB_PTR.with(Cell::get);
337 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
338 let slot = unsafe { nexus_slab::byte::Slot::<u8>::from_raw(ptr) };
339 slab.free(slot);
340}
341
342unsafe fn unbounded_try_claim<const S: usize>() -> (*mut u8, usize) {
343 let slab_ptr = SLAB_PTR.with(Cell::get);
344 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
345 let claim = slab.claim();
346 let ptr = claim.as_ptr();
347 let chunk_idx = claim.chunk_idx();
348 std::mem::forget(claim);
350 (ptr, chunk_idx)
351}
352
353unsafe fn unbounded_claim_free<const S: usize>(
354 slab_ptr: *const u8,
355 ptr: *mut u8,
356 chunk_idx: usize,
357) {
358 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
359 unsafe { slab.free_raw_in_chunk(ptr, chunk_idx) };
361}
362
363unsafe fn bounded_claim<const S: usize>(src: *const u8, size: usize) -> *mut u8 {
366 let slab_ptr = SLAB_PTR.with(Cell::get);
367 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
368 unsafe { slab.alloc_raw(src, size) }
369}
370
371unsafe fn bounded_free<const S: usize>(ptr: *mut u8) {
372 let slab_ptr = SLAB_PTR.with(Cell::get);
373 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
374 let slot = unsafe { nexus_slab::byte::Slot::<u8>::from_raw(ptr) };
375 slab.free(slot);
376}
377
378unsafe fn bounded_try_claim<const S: usize>() -> (*mut u8, usize) {
379 let slab_ptr = SLAB_PTR.with(Cell::get);
380 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
381 slab.try_claim().map_or((std::ptr::null_mut(), 0), |claim| {
382 let ptr = claim.as_ptr();
383 std::mem::forget(claim);
384 (ptr, 0) })
386}
387
388unsafe fn bounded_claim_free<const S: usize>(slab_ptr: *const u8, ptr: *mut u8, _chunk_idx: usize) {
389 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
390 unsafe { slab.free_raw(ptr) };
391}