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