Next Generation WASM Microkernel Operating System

fix(kasync): remove `*.try_spawn_in` methods. We cannot actually allocate tasks with an allocator other than the default global one, since we currently do not have the ability to free using a custom allocator. (#523)

+44 -102
+2 -2
kernel/src/main.rs
··· 171 171 // (e.g. setting the trap vector and enabling interrupts) 172 172 let cpu = arch::device::cpu::Cpu::new(&device_tree, cpuid)?; 173 173 174 - let executor = Executor::with_capacity(boot_info.cpu_mask.count_ones() as usize); 174 + let executor = Executor::with_capacity(boot_info.cpu_mask.count_ones() as usize).unwrap(); 175 175 let timer = Timer::new(Duration::from_millis(1), cpu.clock); 176 176 177 177 Ok(Global { ··· 200 200 Instant::from_ticks(&global.timer, Ticks(boot_ticks)).elapsed(&global.timer) 201 201 ); 202 202 203 - let mut worker2 = Worker::new(&global.executor, FastRand::from_seed(rng.next_u64())); 203 + let mut worker2 = Worker::new(&global.executor, FastRand::from_seed(rng.next_u64())).unwrap(); 204 204 205 205 cfg_if! { 206 206 if #[cfg(test)] {
+32 -51
libs/kasync/src/executor.rs
··· 8 8 mod steal; 9 9 10 10 use alloc::boxed::Box; 11 - use core::alloc::Allocator; 11 + use core::alloc::AllocError; 12 12 use core::num::NonZeroUsize; 13 13 use core::ptr; 14 14 use core::ptr::NonNull; ··· 84 84 85 85 // === impl Executor === 86 86 87 - impl Default for Executor { 88 - fn default() -> Self { 89 - Self::new() 90 - } 91 - } 92 - 93 87 impl Executor { 94 - pub fn new() -> Self { 95 - Self { 88 + /// # Errors 89 + /// 90 + /// Returns `AllocError` when allocating the underlying resources fails. 91 + pub fn new() -> Result<Self, AllocError> { 92 + Ok(Self { 96 93 schedulers: CpuLocal::new(), 97 - injector: Injector::new(), 94 + injector: Injector::new()?, 98 95 sleepers: WaitQueue::new(), 99 - } 96 + }) 100 97 } 101 98 102 - pub fn with_capacity(capacity: usize) -> Self { 103 - Self { 99 + /// # Errors 100 + /// 101 + /// Returns `AllocError` when allocating the underlying resources fails. 102 + pub fn with_capacity(capacity: usize) -> Result<Self, AllocError> { 103 + Ok(Self { 104 104 schedulers: CpuLocal::with_capacity(capacity), 105 - injector: Injector::new(), 105 + injector: Injector::new()?, 106 106 sleepers: WaitQueue::new(), 107 - } 107 + }) 108 108 } 109 109 110 110 /// Closes the executor. ··· 170 170 { 171 171 self.build_task().try_spawn(future) 172 172 } 173 - 174 - /// Attempt spawn this [`Future`] onto this executor using a custom [`Allocator`]. 175 - /// 176 - /// This method returns a [`TaskRef`] which can be used to spawn it onto an [`crate::executor::Executor`] 177 - /// and a [`JoinHandle`] which can be used to await the futures output as well as control some aspects 178 - /// of its runtime behaviour (such as cancelling it). 179 - /// 180 - /// # Errors 181 - /// 182 - /// Returns [`AllocError`] when allocation of the task fails. 183 - pub fn try_spawn_in<F, A>( 184 - &'static self, 185 - future: F, 186 - alloc: A, 187 - ) -> Result<JoinHandle<F::Output>, SpawnError> 188 - where 189 - F: Future + Send + 'static, 190 - F::Output: Send + 'static, 191 - A: Allocator, 192 - { 193 - self.build_task().try_spawn_in(future, alloc) 194 - } 195 173 } 196 174 197 175 // === impl Worker === 198 176 199 177 impl Worker { 200 - pub fn new(executor: &'static Executor, rng: FastRand) -> Self { 178 + /// # Errors 179 + /// 180 + /// Returns `AllocError` when allocating the underlying resources fails. 181 + pub fn new(executor: &'static Executor, rng: FastRand) -> Result<Self, AllocError> { 201 182 let id = executor.schedulers.len(); 202 - let core = executor.schedulers.get_or(Scheduler::new); 183 + let core = executor.schedulers.get_or_try(Scheduler::new)?; 203 184 204 - Self { 185 + Ok(Self { 205 186 id, 206 187 executor, 207 188 scheduler: core, 208 189 rng, 209 - } 190 + }) 210 191 } 211 192 212 193 /// Returns a reference to the task that's current being polled or `None`. ··· 350 331 // === impl Scheduler === 351 332 352 333 impl Scheduler { 353 - fn new() -> Self { 354 - let stub_task = Box::new(Task::new_stub()); 334 + fn new() -> Result<Self, AllocError> { 335 + let stub_task = Box::try_new(Task::new_stub())?; 355 336 let (stub_task, _) = TaskRef::new_allocated(stub_task); 356 337 357 - Self { 338 + Ok(Self { 358 339 run_queue: MpscQueue::new_with_stub(stub_task), 359 340 queued: AtomicUsize::new(0), 360 341 current_task: AtomicPtr::new(ptr::null_mut()), ··· 362 343 spawned: AtomicUsize::new(0), 363 344 #[cfg(feature = "counters")] 364 345 woken: AtomicUsize::new(0), 365 - } 346 + }) 366 347 } 367 348 368 349 /// Returns a reference to the task that's current being polled or `None`. ··· 515 496 516 497 loom::model(|| { 517 498 loom::lazy_static! { 518 - static ref EXEC: Executor = Executor::new(); 499 + static ref EXEC: Executor = Executor::new().unwrap(); 519 500 static ref CALLED: AtomicBool = AtomicBool::new(false); 520 501 } 521 502 ··· 526 507 }) 527 508 .unwrap(); 528 509 529 - let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)); 510 + let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)).unwrap(); 530 511 test_util::block_on(worker.run(crate::future::pending::<()>())).expect_err( 531 512 "stopping the executor should always result in a Closed(()) error here", 532 513 ); ··· 544 525 const NUM_THREADS: usize = 3; 545 526 546 527 loom::lazy_static! { 547 - static ref EXEC: Executor = Executor::new(); 528 + static ref EXEC: Executor = Executor::new().unwrap(); 548 529 static ref CALLED: AtomicBool = AtomicBool::new(false); 549 530 } 550 531 ··· 558 539 let joins: Vec<_> = (0..NUM_THREADS) 559 540 .map(|_| { 560 541 loom::thread::spawn(move || { 561 - let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)); 542 + let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)).unwrap(); 562 543 563 544 test_util::block_on(worker.run(crate::future::pending::<()>())).expect_err( 564 545 "stopping the executor should always result in a Closed(()) error here", ··· 582 563 583 564 loom::model(|| { 584 565 loom::lazy_static! { 585 - static ref EXEC: Executor = Executor::new(); 566 + static ref EXEC: Executor = Executor::new().unwrap(); 586 567 } 587 568 588 569 let (tx, rx) = loom::sync::mpsc::channel::<JoinHandle<u32>>(); 589 570 590 571 let h0 = loom::thread::spawn(move || { 591 572 let tid = loom::thread::current().id(); 592 - let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)); 573 + let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)).unwrap(); 593 574 594 575 let h = EXEC 595 576 .try_spawn(async move {
+5 -10
libs/kasync/src/executor/steal.rs
··· 6 6 // copied, modified, or distributed except according to those terms. 7 7 8 8 use alloc::boxed::Box; 9 + use core::alloc::AllocError; 9 10 use core::fmt::Debug; 10 11 use core::num::NonZeroUsize; 11 12 ··· 31 32 queued: AtomicUsize, 32 33 } 33 34 34 - impl Default for Injector { 35 - fn default() -> Self { 36 - Self::new() 37 - } 38 - } 39 - 40 35 impl Injector { 41 - pub fn new() -> Self { 42 - let stub_task = Box::new(Task::new_stub()); 36 + pub fn new() -> Result<Self, AllocError> { 37 + let stub_task = Box::try_new(Task::new_stub())?; 43 38 let (stub_task, _) = TaskRef::new_allocated(stub_task); 44 39 45 - Self { 40 + Ok(Self { 46 41 run_queue: MpscQueue::new_with_stub(stub_task), 47 42 queued: AtomicUsize::new(0), 48 - } 43 + }) 49 44 } 50 45 51 46 /// Attempt to steal from this `Injector`, the returned [`Stealer`] will grant exclusive access to
+1 -2
libs/kasync/src/lib.rs
··· 9 9 //! 10 10 //! This crate was heavily inspired by tokio and the (much better) maitake crates, to a small extend smol also influenced the design. 11 11 12 - #![feature(allocator_api)] 13 12 #![cfg_attr(not(any(test, feature = "__bench")), no_std)] 14 13 #![feature(debug_closure_helpers)] 15 14 #![feature(never_type)] 16 - 15 + #![feature(allocator_api)] 17 16 extern crate alloc; 18 17 19 18 mod error;
+2 -4
libs/kasync/src/task.rs
··· 12 12 mod yield_now; 13 13 14 14 use alloc::boxed::Box; 15 - use core::alloc::Allocator; 16 15 use core::any::type_name; 17 16 use core::mem::offset_of; 18 17 use core::panic::AssertUnwindSafe; ··· 214 213 215 214 impl TaskRef { 216 215 #[track_caller] 217 - pub(crate) fn new_allocated<F, A>(task: Box<Task<F>, A>) -> (Self, JoinHandle<F::Output>) 216 + pub(crate) fn new_allocated<F>(task: Box<Task<F>>) -> (Self, JoinHandle<F::Output>) 218 217 where 219 218 F: Future, 220 - A: Allocator, 221 219 { 222 220 assert_eq!(task.state().refcount(), 1); 223 - let (ptr, _) = Box::into_raw_with_allocator(task); 221 + let ptr = Box::into_raw(task); 224 222 225 223 // Safety: we just allocated the ptr so it is never null 226 224 let task = Self(unsafe { NonNull::new_unchecked(ptr).cast() });
-31
libs/kasync/src/task/builder.rs
··· 6 6 // copied, modified, or distributed except according to those terms. 7 7 8 8 use alloc::boxed::Box; 9 - use core::alloc::Allocator; 10 9 use core::any::type_name; 11 10 use core::panic::Location; 12 11 ··· 102 101 { 103 102 let task = self.build(future); 104 103 let task = Box::try_new(task)?; 105 - let (task, join) = TaskRef::new_allocated(task); 106 - 107 - (self.schedule)(task)?; 108 - 109 - Ok(join) 110 - } 111 - 112 - /// Attempt spawn this [`Future`] onto the executor using a custom [`Allocator`]. 113 - /// 114 - /// This method returns a [`TaskRef`] which can be used to spawn it onto an [`crate::executor::Executor`] 115 - /// and a [`JoinHandle`] which can be used to await the futures output as well as control some aspects 116 - /// of its runtime behaviour (such as cancelling it). 117 - /// 118 - /// # Errors 119 - /// 120 - /// Returns [`AllocError`] when allocation of the task fails. 121 - #[inline] 122 - #[track_caller] 123 - pub fn try_spawn_in<F, A>( 124 - &self, 125 - future: F, 126 - alloc: A, 127 - ) -> Result<JoinHandle<F::Output>, SpawnError> 128 - where 129 - F: Future + Send, 130 - F::Output: Send, 131 - A: Allocator, 132 - { 133 - let task = self.build(future); 134 - let task = Box::try_new_in(task, alloc)?; 135 104 let (task, join) = TaskRef::new_allocated(task); 136 105 137 106 (self.schedule)(task)?;
+2 -2
libs/kasync/src/time/sleep.rs
··· 163 163 164 164 loom::model(move || { 165 165 loom::lazy_static! { 166 - static ref EXEC: Executor = Executor::new(); 166 + static ref EXEC: Executor = Executor::new().unwrap(); 167 167 static ref TIMER: Timer = Timer::new(Duration::from_millis(1), MockClock::new_1us()); 168 168 static ref CALLED: AtomicBool = AtomicBool::new(false); 169 169 } 170 170 171 - let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)); 171 + let mut worker = Worker::new(&EXEC, FastRand::from_seed(0)).unwrap(); 172 172 173 173 let th = EXEC 174 174 .try_spawn(async move {