1use crate::execution::create_kernel_thread;
11use crate::task::{
12 CurrentTask, DelayedReleaser, LockedAndTask, Task, ThreadLockupDetector, WrappedFuture,
13 with_new_current_task,
14};
15use futures::TryFutureExt;
16use futures::channel::oneshot;
17use starnix_logging::{CATEGORY_STARNIX, log_debug, log_error};
18use starnix_sync::{DynamicThreadSpawnerLock, LockDepMutex, Locked, Unlocked};
19use starnix_task_command::TaskCommand;
20use starnix_types::ownership::release_after;
21use starnix_uapi::errno;
22use starnix_uapi::errors::Errno;
23use std::future::Future;
24use std::sync::mpsc::{SendError, SyncSender, TrySendError, sync_channel};
25use std::sync::{Arc, Weak};
26use std::thread::JoinHandle;
27
28type BoxedClosure = Box<dyn FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> () + Send + 'static>;
29
30const DEFAULT_THREAD_ROLE: &str = "fuchsia.starnix.fair.16";
31
32pub struct SpawnRequestBuilder<C: ClosureKind> {
60 debug_name: &'static str,
61 role: Option<&'static str>,
62 closure_kind: C,
63}
64
65impl SpawnRequestBuilder<ClosureNone> {
67 pub fn new() -> Self {
69 Self { role: None, closure_kind: ClosureNone {}, debug_name: "kthreadd" }
70 }
71}
72
73impl<C: ClosureKind> SpawnRequestBuilder<C> {
75 pub fn with_role(self, role: &'static str) -> Self {
77 Self { role: Some(role), ..self }
78 }
79
80 pub fn with_debug_name(self, debug_name: &'static str) -> Self {
82 Self { debug_name, ..self }
83 }
84}
85
86impl SpawnRequestBuilder<ClosureNone> {
88 pub fn with_sync_closure<F, T>(
90 self,
91 f: F,
92 ) -> SpawnRequestBuilder<impl FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static>
93 where
94 T: Send + 'static,
95 F: FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static,
96 {
97 let SpawnRequestBuilder { role, closure_kind: _, debug_name } = self;
98 SpawnRequestBuilder { role, closure_kind: f, debug_name }
99 }
100
101 pub fn with_async_closure<F, T>(
103 self,
104 f: F,
105 ) -> SpawnRequestBuilder<impl FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static>
106 where
107 T: Send + 'static,
108 F: AsyncFnOnce(LockedAndTask<'_>) -> T + Send + 'static,
109 {
110 let sync_fn = async_to_sync(f, self.debug_name);
111 self.with_sync_closure(sync_fn)
112 }
113}
114
115pub struct SpawnRequest {
117 closure: BoxedClosure,
119 debug_name: &'static str,
121}
122
123impl<T, F> SpawnRequestBuilder<F>
124where
125 T: Send + 'static,
126 F: FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static,
127{
128 pub fn build(self) -> SpawnRequest {
130 let Self { role, closure_kind, debug_name } = self;
131 let closure = closure_kind;
132 let closure = maybe_apply_role(role, closure);
133 let closure = Box::new(move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
134 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
135 let _ = closure(locked, current_task);
136 });
137 SpawnRequest { closure, debug_name }
138 }
139
140 pub fn build_with_sync_result(self) -> (impl FnOnce() -> Result<T, Errno>, SpawnRequest) {
151 let Self { role, closure_kind, debug_name } = self;
152 let closure = closure_kind;
153 let (sender, receiver) = sync_channel::<T>(0);
154 let result_fn = move || {
155 receiver.recv().map_err(|err| errno!(EINTR, format!("while receiving: {err:?}")))
156 };
157 let closure = maybe_apply_role(role, closure);
158 let closure = Box::new(move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
159 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
160 let _ = sender.send(closure(locked, current_task));
161 });
162 (result_fn, SpawnRequest { closure, debug_name })
163 }
164
165 pub fn build_with_async_result(self) -> (impl Future<Output = Result<T, Errno>>, SpawnRequest) {
176 let Self { role, closure_kind, debug_name } = self;
177 let closure = closure_kind;
178 let (sender_async, result_fut) = oneshot::channel::<T>();
179 let maybe_with_role = maybe_apply_role(role, closure);
180 let repackaged =
181 Box::new(move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
182 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
183 let result = maybe_with_role(locked, current_task);
184 let _ = sender_async.send(result);
185 });
186 let result_fut =
187 result_fut.map_err(|err| errno!(EINTR, format!("while receiving async: {err:?}")));
188 (result_fut, SpawnRequest { closure: repackaged, debug_name })
189 }
190}
191
192#[derive(Debug)]
195pub struct DynamicThreadSpawner {
196 state: Arc<LockDepMutex<DynamicThreadSpawnerState, DynamicThreadSpawnerLock>>,
197 system_task: Weak<Task>,
199 persistent_thread: RunningThread,
202}
203
204fn maybe_apply_role<R, F>(
206 role: Option<&'static str>,
207 f: F,
208) -> impl FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> R + Send + 'static
209where
210 F: FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> R + Send + 'static,
211{
212 move |locked, current_task| {
213 if let Some(role) = role {
214 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(role) {
215 log_debug!(e:%; "failed to set kthread role");
216 }
217 let result = f(locked, current_task);
218 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(DEFAULT_THREAD_ROLE) {
219 log_debug!(e:%; "failed to reset kthread role to default priority");
220 }
221 result
222 } else {
223 f(locked, current_task)
224 }
225 }
226}
227
228fn async_to_sync<T, F>(
230 f: F,
231 name: &'static str,
232) -> impl FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static
233where
234 T: Send + 'static,
235 F: AsyncFnOnce(LockedAndTask<'_>) -> T + Send + 'static,
236{
237 move |locked, current_task| {
238 let mut exec = fuchsia_async::LocalExecutor::default();
239 let locked_and_task = LockedAndTask::new(locked, current_task);
240
241 let locked_and_task_clone = locked_and_task.clone();
242 let wrapped_future = WrappedSpawnedFuture::new(
243 locked_and_task,
244 ThreadLockupDetector::track_future(f(locked_and_task_clone)),
245 name,
246 );
247 let _waiting_guard = ThreadLockupDetector::pause_tracking();
248 exec.run_singlethreaded(wrapped_future)
249 }
250}
251
252pub trait ClosureKind {}
257
258pub struct ClosureNone {}
262impl ClosureKind for ClosureNone {}
263
264impl<T: Send + 'static, FN: FnOnce(&mut Locked<Unlocked>, &CurrentTask) -> T + Send + 'static>
267 ClosureKind for FN
268{
269}
270
271#[derive(Debug)]
272struct DynamicThreadSpawnerState {
273 threads: Vec<RunningThread>,
274 idle_threads: u8,
275 max_idle_threads: u8,
276}
277
278impl DynamicThreadSpawner {
279 pub fn new(
280 max_idle_threads: u8,
281 system_task: Weak<Task>,
282 debug_name: impl Into<String>,
283 ) -> Self {
284 let persistent_thread =
285 RunningThread::new_persistent(system_task.clone(), debug_name.into());
286 Self {
287 state: Arc::new(
288 DynamicThreadSpawnerState { max_idle_threads, idle_threads: 0, threads: vec![] }
289 .into(),
290 ),
291 system_task,
292 persistent_thread,
293 }
294 }
295
296 pub fn spawn_from_request(&self, spawn_request: SpawnRequest) {
304 let mut function: BoxedClosure = spawn_request.closure;
306 let mut state = self.state.lock();
307 if state.idle_threads > 0 {
308 let mut i = 0;
309 while i < state.threads.len() {
310 let thread_index = i;
313 i += 1;
314 match state.threads[thread_index].try_dispatch(function) {
315 Ok(_) => {
316 state.idle_threads -= 1;
318 return;
319 }
320 Err(TrySendError::Full(f)) => {
321 function = f;
323 }
324 Err(TrySendError::Disconnected(f)) => {
325 state.idle_threads -= 1;
327 state.threads.remove(thread_index);
328 i -= 1;
329 function = f;
330 }
331 }
332 }
333 }
334
335 let (sender, receiver) = sync_channel::<RunningThread>(0);
337 let dispatch_function: BoxedClosure = Box::new({
338 let state = self.state.clone();
339 let system_task = self.system_task.clone();
340 move |_, _| {
341 sender
342 .send(RunningThread::new(
343 state,
344 system_task,
345 spawn_request.debug_name.to_string(),
346 function,
347 ))
348 .expect("receiver must not be dropped");
349 }
350 });
351 self.persistent_thread
352 .dispatch(dispatch_function)
353 .expect("persistent thread should not have ended.");
354 state.threads.push(receiver.recv().expect("persistent thread should not have ended."));
355 }
356}
357
358type WrappedSpawnedFuture<'a, F> = WrappedFuture<F, LockedAndTask<'a>>;
359
360impl<'a, F: 'a> WrappedSpawnedFuture<'a, F> {
361 fn new(locked_and_task: LockedAndTask<'a>, fut: F, name: &'static str) -> Self {
362 Self::new_with_cleaner(locked_and_task, trigger_delayed_releaser, fut, name)
363 }
364}
365
366fn trigger_delayed_releaser(locked_and_task: LockedAndTask<'_>) {
367 locked_and_task.current_task().trigger_delayed_releaser(&mut locked_and_task.unlocked());
368}
369
370#[derive(Debug)]
371struct RunningThread {
372 thread: Option<JoinHandle<()>>,
373 sender: Option<SyncSender<BoxedClosure>>,
374}
375
376impl RunningThread {
377 fn new(
378 state: Arc<LockDepMutex<DynamicThreadSpawnerState, DynamicThreadSpawnerLock>>,
379 system_task: Weak<Task>,
380 debug_task_name: String,
381 f: BoxedClosure,
382 ) -> Self {
383 let (sender, receiver) = sync_channel::<BoxedClosure>(0);
384 let thread = Some(
385 std::thread::Builder::new()
386 .name("kthread-dynamic-worker".to_string())
387 .spawn(move || {
388 #[allow(
390 clippy::undocumented_unsafe_blocks,
391 reason = "Force documented unsafe blocks in Starnix"
392 )]
393 let locked = unsafe { Unlocked::new() };
394 let result = with_new_current_task(
395 locked,
396 &system_task,
397 debug_task_name,
398 |locked, current_task| {
399 while let Ok(f) = receiver.recv() {
400 let _guard = ThreadLockupDetector::track();
401 f(locked, ¤t_task);
402 current_task.trigger_delayed_releaser(locked);
404 let mut state = state.lock();
405 state.idle_threads += 1;
406 if state.idle_threads > state.max_idle_threads {
407 return;
412 }
413 }
414 },
415 );
416 if let Err(e) = result {
417 log_error!("Unable to create a kernel thread: {e:?}");
418 }
419 })
420 .expect("able to create threads"),
421 );
422 let result = Self { thread, sender: Some(sender) };
423 result
426 .sender
427 .as_ref()
428 .expect("sender should never be None")
429 .send(f)
430 .expect("Dispatch cannot fail");
431 result
432 }
433
434 fn new_persistent(system_task: Weak<Task>, task_name: String) -> Self {
435 let (sender, receiver) = sync_channel::<BoxedClosure>(20);
437 let thread = Some(
438 std::thread::Builder::new()
439 .name("kthread-persistent-worker".to_string())
440 .spawn(move || {
441 #[allow(
443 clippy::undocumented_unsafe_blocks,
444 reason = "Force documented unsafe blocks in Starnix"
445 )]
446 let locked = unsafe { Unlocked::new() };
447 let current_task = {
448 let Some(system_task) = system_task.upgrade() else {
449 return;
450 };
451 match create_kernel_thread(
452 locked,
453 &system_task,
454 TaskCommand::new(task_name.as_bytes()),
455 ) {
456 Ok(task) => task,
457 Err(e) => {
458 log_error!("Unable to create a kernel thread: {e:?}");
459 return;
460 }
461 }
462 };
463 release_after!(current_task, locked, {
464 while let Ok(f) = receiver.recv() {
465 let _guard = ThreadLockupDetector::track();
466 f(locked, ¤t_task);
467
468 current_task.trigger_delayed_releaser(locked);
470 }
471 });
472
473 DelayedReleaser::finalize();
475 })
476 .expect("able to create threads"),
477 );
478 Self { thread, sender: Some(sender) }
479 }
480
481 fn try_dispatch(&self, f: BoxedClosure) -> Result<(), TrySendError<BoxedClosure>> {
482 self.sender.as_ref().expect("sender should never be None").try_send(f)
483 }
484
485 fn dispatch(&self, f: BoxedClosure) -> Result<(), SendError<BoxedClosure>> {
486 self.sender.as_ref().expect("sender should never be None").send(f)
487 }
488}
489
490impl Drop for RunningThread {
491 fn drop(&mut self) {
492 self.sender = None;
493 match self.thread.take() {
494 Some(thread) => thread.join().expect("Thread should join."),
495 _ => panic!("Thread should never be None"),
496 };
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503 use crate::testing::spawn_kernel_and_run;
504
505 #[fuchsia::test]
506 async fn run_simple_task() {
507 spawn_kernel_and_run(async |_, current_task| {
508 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
509 let closure = move |_: &mut Locked<Unlocked>, _: &CurrentTask| {};
512 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
513 spawner.spawn_from_request(req);
514 })
515 .await;
516 }
517
518 #[fuchsia::test]
519 async fn run_10_tasks() {
520 spawn_kernel_and_run(async |_, current_task| {
521 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
522 for _ in 0..10 {
523 let closure = move |_: &mut Locked<Unlocked>, _: &CurrentTask| {};
524 let opts = SpawnRequestBuilder::new().with_sync_closure(closure).build();
525 spawner.spawn_from_request(opts);
526 }
527 })
528 .await;
529 }
530
531 #[fuchsia::test]
532 async fn blocking_task_do_not_prevent_further_processing() {
533 spawn_kernel_and_run(async |_, current_task| {
534 let spawner = DynamicThreadSpawner::new(1, current_task.weak_task(), "kthreadd");
535
536 let pair = Arc::new((fuchsia_sync::Mutex::new(false), fuchsia_sync::Condvar::new()));
537 for _ in 0..10 {
538 let pair2 = Arc::clone(&pair);
539 let closure = move |_: &mut Locked<Unlocked>, _: &CurrentTask| {
540 let (lock, cvar) = &*pair2;
541 let mut cont = lock.lock();
542 while !*cont {
543 cvar.wait(&mut cont);
544 }
545 };
546 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
547 spawner.spawn_from_request(req);
548 }
549
550 let closure = move |_: &mut Locked<Unlocked>, _: &CurrentTask| {
551 let (lock, cvar) = &*pair;
552 let mut cont = lock.lock();
553 *cont = true;
554 cvar.notify_all();
555 };
556
557 let (result, req) =
558 SpawnRequestBuilder::new().with_sync_closure(closure).build_with_sync_result();
559 spawner.spawn_from_request(req);
560
561 assert_eq!(result(), Ok(()));
562 })
563 .await;
564 }
565
566 #[fuchsia::test]
567 async fn run_spawn_and_get_result() {
568 spawn_kernel_and_run(async |_, current_task| {
569 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
570
571 let (result, req) =
572 SpawnRequestBuilder::new().with_sync_closure(|_, _| 3).build_with_sync_result();
573 spawner.spawn_from_request(req);
574 assert_eq!(result(), Ok(3));
575 })
576 .await;
577 }
578
579 #[fuchsia::test]
580 async fn test_spawn_async() {
581 spawn_kernel_and_run(async |_, current_task| {
582 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
583
584 let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
588 let mut exec = fuchsia_async::LocalExecutor::default();
589 let locked_and_task = LockedAndTask::new(locked, current_task);
590 let fut = async {};
591 let wrapped_future = WrappedSpawnedFuture::new(locked_and_task, fut, "test-async");
592 exec.run_singlethreaded(wrapped_future);
593 };
594 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
595 spawner.spawn_from_request(req);
596 })
597 .await;
598 }
599
600 #[fuchsia::test]
601 async fn test_spawn_async_closure() {
602 spawn_kernel_and_run(async |_, current_task| {
603 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
604 let fut = async |_: LockedAndTask<'_>| 42;
605 let (result, req) =
606 SpawnRequestBuilder::new().with_async_closure(fut).build_with_sync_result();
607 spawner.spawn_from_request(req);
608 assert_eq!(result(), Ok(42));
609 })
610 .await;
611 }
612
613 #[fuchsia::test]
614 async fn test_spawn_sync_to_async_result() {
615 spawn_kernel_and_run(async |_, current_task| {
616 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
617 let fut = async |_: LockedAndTask<'_>| 42;
618 let (result, req) =
619 SpawnRequestBuilder::new().with_async_closure(fut).build_with_sync_result();
620
621 let fut2 = async move |_: LockedAndTask<'_>| result().unwrap();
622 let (result2, req2) =
623 SpawnRequestBuilder::new().with_async_closure(fut2).build_with_sync_result();
624 spawner.spawn_from_request(req2);
625 spawner.spawn_from_request(req);
626 assert_eq!(result2(), Ok(42));
627 })
628 .await;
629 }
630
631 #[fuchsia::test]
632 async fn test_spawn_async_to_async_result() {
633 spawn_kernel_and_run(async |_, current_task| {
634 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
635 let fut = async |_: LockedAndTask<'_>| 42;
636 let (result_fut, req) =
637 SpawnRequestBuilder::new().with_async_closure(fut).build_with_async_result();
638
639 let fut2 = async move |_: LockedAndTask<'_>| result_fut.await.unwrap();
640 let (result2, req2) =
641 SpawnRequestBuilder::new().with_async_closure(fut2).build_with_sync_result();
642 spawner.spawn_from_request(req2);
643 spawner.spawn_from_request(req);
644 assert_eq!(result2(), Ok(42));
645 })
646 .await;
647 }
648}