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(slab: Box<dyn std::any::Any>, config: &SlabTlsConfig) -> SlabGuard {
102 let prev_ptr = SLAB_PTR.with(|c| c.replace(config.slab_ptr));
103 let prev_claim = SLAB_CLAIM.with(|c| c.replace(config.claim_fn));
104 let prev_free = SLAB_FREE.with(|c| c.replace(config.free_fn));
105 let prev_try_claim = SLAB_TRY_CLAIM.with(|c| c.replace(config.try_claim_fn));
106 let prev_claim_free = SLAB_CLAIM_FREE.with(|c| c.replace(config.claim_free_fn));
107 let prev_slot_size = SLAB_SLOT_SIZE.with(|c| c.replace(config.slot_size));
108 SlabGuard {
109 prev_ptr,
110 prev_claim,
111 prev_free,
112 prev_try_claim,
113 prev_claim_free,
114 prev_slot_size,
115 _slab: slab,
116 }
117}
118
119#[allow(clippy::struct_field_names)]
120pub(crate) struct SlabGuard {
121 prev_ptr: *const u8,
122 prev_claim: ClaimFn,
123 prev_free: FreeFn,
124 prev_try_claim: TryClaimFn,
125 prev_claim_free: ClaimFreeFn,
126 prev_slot_size: usize,
127
128 _slab: Box<dyn std::any::Any>,
134}
135
136impl Drop for SlabGuard {
137 fn drop(&mut self) {
138 SLAB_PTR.with(|c| c.set(self.prev_ptr));
141 SLAB_CLAIM.with(|c| c.set(self.prev_claim));
142 SLAB_FREE.with(|c| c.set(self.prev_free));
143 SLAB_TRY_CLAIM.with(|c| c.set(self.prev_try_claim));
144 SLAB_CLAIM_FREE.with(|c| c.set(self.prev_claim_free));
145 SLAB_SLOT_SIZE.with(|c| c.set(self.prev_slot_size));
146 }
147}
148
149pub(crate) fn slab_spawn<F>(future: F, tracker_key: u32) -> *mut u8
161where
162 F: Future + 'static,
163 F::Output: 'static,
164{
165 let cross_wake_ctx = crate::cross_wake::current_runtime_ctx();
167 let task = crate::task::new_joinable_slab(future, tracker_key, slab_free_task, cross_wake_ctx);
168 let size = std::mem::size_of_val(&task);
169 let src = std::ptr::from_ref(&task).cast::<u8>();
170
171 let claim = SLAB_CLAIM.with(Cell::get);
172 let ptr = unsafe { claim(src, size) };
174 assert!(!ptr.is_null(), "slab full — spawn_slab failed");
175
176 std::mem::forget(task);
178
179 ptr
180}
181
182unsafe fn slab_free_task(ptr: *mut u8) {
184 let free = SLAB_FREE.with(Cell::get);
185 unsafe { free(ptr) };
186}
187
188pub struct SlabClaim {
202 ptr: *mut u8,
203 slab_ptr: *const u8,
204 free: ClaimFreeFn,
205 chunk_idx: usize,
206 slot_size: usize,
207 _not_send: std::marker::PhantomData<std::rc::Rc<()>>,
209}
210
211impl SlabClaim {
212 pub fn spawn<F>(self, future: F) -> crate::task::JoinHandle<F::Output>
217 where
218 F: Future + 'static,
219 F::Output: 'static,
220 {
221 crate::runtime::with_executor(|exec| {
222 let tracker_key = exec.next_tracker_key();
223 let cross_wake_ctx = crate::cross_wake::current_runtime_ctx();
225 let task =
226 crate::task::new_joinable_slab(future, tracker_key, slab_free_task, cross_wake_ctx);
227 let size = std::mem::size_of_val(&task);
228
229 assert!(
230 size <= self.slot_size,
231 "task size ({size} bytes) exceeds slab slot size ({} bytes)",
232 self.slot_size,
233 );
234
235 let src = std::ptr::from_ref(&task).cast::<u8>();
236 unsafe { std::ptr::copy_nonoverlapping(src, self.ptr, size) };
238 std::mem::forget(task);
239
240 let ptr = self.ptr;
241 std::mem::forget(self);
243
244 exec.spawn_raw(ptr);
245 crate::task::JoinHandle::new(ptr)
246 })
247 }
248
249 pub fn as_ptr(&self) -> *mut u8 {
251 self.ptr
252 }
253
254 pub fn slot_size(&self) -> usize {
256 self.slot_size
257 }
258}
259
260impl Drop for SlabClaim {
261 fn drop(&mut self) {
262 unsafe { (self.free)(self.slab_ptr, self.ptr, self.chunk_idx) };
266 }
267}
268
269impl std::fmt::Debug for SlabClaim {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 f.debug_struct("SlabClaim")
272 .field("ptr", &self.ptr)
273 .field("slot_size", &self.slot_size)
274 .finish()
275 }
276}
277
278pub(crate) fn try_claim() -> Option<SlabClaim> {
289 let try_claim_fn = SLAB_TRY_CLAIM.with(Cell::get);
290 let (ptr, chunk_idx) = unsafe { try_claim_fn() };
292 if ptr.is_null() {
293 return None;
294 }
295
296 let slab_ptr = SLAB_PTR.with(Cell::get);
297 let free = SLAB_CLAIM_FREE.with(Cell::get);
298 let slot_size = SLAB_SLOT_SIZE.with(Cell::get);
299
300 Some(SlabClaim {
301 ptr,
302 slab_ptr,
303 free,
304 chunk_idx,
305 slot_size,
306 _not_send: std::marker::PhantomData,
307 })
308}
309
310pub(crate) fn claim() -> SlabClaim {
318 try_claim().expect("slab full — claim_slab failed")
319}
320
321pub(crate) fn make_unbounded_config<const S: usize>(slab_ptr: *const u8) -> SlabTlsConfig {
327 SlabTlsConfig {
328 slab_ptr,
329 claim_fn: unbounded_claim::<S>,
330 free_fn: unbounded_free::<S>,
331 try_claim_fn: unbounded_try_claim::<S>,
332 claim_free_fn: unbounded_claim_free::<S>,
333 slot_size: S,
334 }
335}
336
337pub(crate) fn make_bounded_config<const S: usize>(slab_ptr: *const u8) -> SlabTlsConfig {
339 SlabTlsConfig {
340 slab_ptr,
341 claim_fn: bounded_claim::<S>,
342 free_fn: bounded_free::<S>,
343 try_claim_fn: bounded_try_claim::<S>,
344 claim_free_fn: bounded_claim_free::<S>,
345 slot_size: S,
346 }
347}
348
349unsafe fn unbounded_claim<const S: usize>(src: *const u8, size: usize) -> *mut u8 {
352 let slab_ptr = SLAB_PTR.with(Cell::get);
353 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
354 unsafe { slab.alloc_raw(src, size) }
355}
356
357unsafe fn unbounded_free<const S: usize>(ptr: *mut u8) {
358 let slab_ptr = SLAB_PTR.with(Cell::get);
359 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
360 let slot = unsafe { nexus_slab::byte::Slot::<u8>::from_raw(ptr) };
361 slab.free(slot);
362}
363
364unsafe fn unbounded_try_claim<const S: usize>() -> (*mut u8, usize) {
365 let slab_ptr = SLAB_PTR.with(Cell::get);
366 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
367 let claim = slab.claim();
368 let ptr = claim.as_ptr();
369 let chunk_idx = claim.chunk_idx();
370 std::mem::forget(claim);
372 (ptr, chunk_idx)
373}
374
375unsafe fn unbounded_claim_free<const S: usize>(
376 slab_ptr: *const u8,
377 ptr: *mut u8,
378 chunk_idx: usize,
379) {
380 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::unbounded::Slab<S>) };
381 unsafe { slab.free_raw_in_chunk(ptr, chunk_idx) };
383}
384
385unsafe fn bounded_claim<const S: usize>(src: *const u8, size: usize) -> *mut u8 {
388 let slab_ptr = SLAB_PTR.with(Cell::get);
389 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
390 unsafe { slab.alloc_raw(src, size) }
391}
392
393unsafe fn bounded_free<const S: usize>(ptr: *mut u8) {
394 let slab_ptr = SLAB_PTR.with(Cell::get);
395 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
396 let slot = unsafe { nexus_slab::byte::Slot::<u8>::from_raw(ptr) };
397 slab.free(slot);
398}
399
400unsafe fn bounded_try_claim<const S: usize>() -> (*mut u8, usize) {
401 let slab_ptr = SLAB_PTR.with(Cell::get);
402 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
403 slab.try_claim().map_or((std::ptr::null_mut(), 0), |claim| {
404 let ptr = claim.as_ptr();
405 std::mem::forget(claim);
406 (ptr, 0) })
408}
409
410unsafe fn bounded_claim_free<const S: usize>(slab_ptr: *const u8, ptr: *mut u8, _chunk_idx: usize) {
411 let slab = unsafe { &*(slab_ptr as *const nexus_slab::byte::bounded::Slab<S>) };
412 unsafe { slab.free_raw(ptr) };
413}