1use crate::execution::create_kernel_thread;
11use crate::task::{
12 CurrentTask, DelayedReleaser, Task, TaskFlags, 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};
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(&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(&CurrentTask) -> T + Send + 'static>
93 where
94 T: Send + 'static,
95 F: FnOnce(&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(&CurrentTask) -> T + Send + 'static>
106 where
107 T: Send + 'static,
108 F: AsyncFnOnce(&CurrentTask) -> 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(&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 |current_task: &CurrentTask| {
134 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
135 let _ = closure(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 |current_task: &CurrentTask| {
159 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
160 let _ = sender.send(closure(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 = Box::new(move |current_task: &CurrentTask| {
181 fuchsia_trace::duration!(CATEGORY_STARNIX, debug_name);
182 let result = maybe_with_role(current_task);
183 let _ = sender_async.send(result);
184 });
185 let result_fut =
186 result_fut.map_err(|err| errno!(EINTR, format!("while receiving async: {err:?}")));
187 (result_fut, SpawnRequest { closure: repackaged, debug_name })
188 }
189}
190
191#[derive(Debug)]
194pub struct DynamicThreadSpawner {
195 state: Arc<LockDepMutex<DynamicThreadSpawnerState, DynamicThreadSpawnerLock>>,
196 system_task: Weak<Task>,
198 persistent_thread: RunningThread,
201}
202
203fn maybe_apply_role<R, F>(
205 role: Option<&'static str>,
206 f: F,
207) -> impl FnOnce(&CurrentTask) -> R + Send + 'static
208where
209 F: FnOnce(&CurrentTask) -> R + Send + 'static,
210{
211 move |current_task| {
212 if let Some(role) = role {
213 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(role) {
214 log_debug!(e:%; "failed to set kthread role");
215 }
216 let result = f(current_task);
217 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(DEFAULT_THREAD_ROLE) {
218 log_debug!(e:%; "failed to reset kthread role to default priority");
219 }
220 result
221 } else {
222 f(current_task)
223 }
224 }
225}
226
227fn async_to_sync<T, F>(f: F, name: &'static str) -> impl FnOnce(&CurrentTask) -> T + Send + 'static
229where
230 T: Send + 'static,
231 F: AsyncFnOnce(&CurrentTask) -> T + Send + 'static,
232{
233 move |current_task| {
234 let mut exec = fuchsia_async::LocalExecutor::default();
235
236 let wrapped_future = WrappedSpawnedFuture::new(
237 current_task,
238 ThreadLockupDetector::track_future(f(current_task)),
239 name,
240 );
241 let _waiting_guard = ThreadLockupDetector::pause_tracking();
242 exec.run_singlethreaded(wrapped_future)
243 }
244}
245
246pub trait ClosureKind {}
251
252pub struct ClosureNone {}
256impl ClosureKind for ClosureNone {}
257
258impl<T: Send + 'static, FN: FnOnce(&CurrentTask) -> T + Send + 'static> ClosureKind for FN {}
261
262#[derive(Debug)]
263struct DynamicThreadSpawnerState {
264 threads: Vec<RunningThread>,
265 idle_threads: u8,
266 max_idle_threads: u8,
267}
268
269impl DynamicThreadSpawner {
270 pub fn new(
271 max_idle_threads: u8,
272 system_task: Weak<Task>,
273 debug_name: impl Into<String>,
274 ) -> Self {
275 let persistent_thread =
276 RunningThread::new_persistent(system_task.clone(), debug_name.into());
277 Self {
278 state: Arc::new(
279 DynamicThreadSpawnerState { max_idle_threads, idle_threads: 0, threads: vec![] }
280 .into(),
281 ),
282 system_task,
283 persistent_thread,
284 }
285 }
286
287 pub fn spawn_from_request(&self, spawn_request: SpawnRequest) {
295 let mut function: BoxedClosure = spawn_request.closure;
297 let mut state = self.state.lock();
298 if state.idle_threads > 0 {
299 let mut i = 0;
300 while i < state.threads.len() {
301 let thread_index = i;
304 i += 1;
305 match state.threads[thread_index].try_dispatch(function) {
306 Ok(_) => {
307 state.idle_threads -= 1;
309 return;
310 }
311 Err(TrySendError::Full(f)) => {
312 function = f;
314 }
315 Err(TrySendError::Disconnected(f)) => {
316 state.idle_threads -= 1;
318 state.threads.remove(thread_index);
319 i -= 1;
320 function = f;
321 }
322 }
323 }
324 }
325
326 let (sender, receiver) = sync_channel::<RunningThread>(0);
328 let dispatch_function: BoxedClosure = Box::new({
329 let state = self.state.clone();
330 let system_task = self.system_task.clone();
331 move |_| {
332 sender
333 .send(RunningThread::new(
334 state,
335 system_task,
336 spawn_request.debug_name.to_string(),
337 function,
338 ))
339 .expect("receiver must not be dropped");
340 }
341 });
342 self.persistent_thread
343 .dispatch(dispatch_function)
344 .expect("persistent thread should not have ended.");
345 state.threads.push(receiver.recv().expect("persistent thread should not have ended."));
346 }
347}
348
349type WrappedSpawnedFuture<'a, F> = WrappedFuture<F, &'a CurrentTask>;
350
351impl<'a, F: 'a> WrappedSpawnedFuture<'a, F> {
352 fn new(current_task: &'a CurrentTask, fut: F, name: &'static str) -> Self {
353 Self::new_with_cleaner(current_task, trigger_delayed_releaser, fut, name)
354 }
355}
356
357fn trigger_delayed_releaser(current_task: &CurrentTask) {
358 current_task.trigger_delayed_releaser();
359}
360
361#[derive(Debug)]
362struct RunningThread {
363 thread: Option<JoinHandle<()>>,
364 sender: Option<SyncSender<BoxedClosure>>,
365}
366
367impl RunningThread {
368 fn new(
369 state: Arc<LockDepMutex<DynamicThreadSpawnerState, DynamicThreadSpawnerLock>>,
370 system_task: Weak<Task>,
371 debug_task_name: String,
372 f: BoxedClosure,
373 ) -> Self {
374 let (sender, receiver) = sync_channel::<BoxedClosure>(0);
375 let thread = Some(
376 std::thread::Builder::new()
377 .name("kthread-dynamic-worker".to_string())
378 .spawn(move || {
379 let _rcu_registration = fuchsia_rcu::register_thread();
380
381 let result =
382 with_new_current_task(&system_task, debug_task_name, |current_task| {
383 while let Ok(f) = receiver.recv() {
384 let _guard = ThreadLockupDetector::track();
385 debug_assert!(
386 !current_task.flags().contains(TaskFlags::WORK_CANCELLED)
387 );
388 f(¤t_task);
389 debug_assert!(
390 !current_task.flags().contains(TaskFlags::WORK_CANCELLED)
391 );
392 current_task.trigger_delayed_releaser();
394 let mut state = state.lock();
395 state.idle_threads += 1;
396 if state.idle_threads > state.max_idle_threads {
397 return;
402 }
403 }
404 });
405 if let Err(e) = result {
406 log_error!("Unable to create a kernel thread: {e:?}");
407 }
408 })
409 .expect("able to create threads"),
410 );
411 let result = Self { thread, sender: Some(sender) };
412 result
415 .sender
416 .as_ref()
417 .expect("sender should never be None")
418 .send(f)
419 .expect("Dispatch cannot fail");
420 result
421 }
422
423 fn new_persistent(system_task: Weak<Task>, task_name: String) -> Self {
424 let (sender, receiver) = sync_channel::<BoxedClosure>(20);
426 let thread = Some(
427 std::thread::Builder::new()
428 .name("kthread-persistent-worker".to_string())
429 .spawn(move || {
430 let _rcu_registration = fuchsia_rcu::register_thread();
431 let current_task = {
432 let Some(system_task) = system_task.upgrade() else {
433 return;
434 };
435 match create_kernel_thread(
436 &system_task,
437 TaskCommand::new(task_name.as_bytes()),
438 ) {
439 Ok(task) => task,
440 Err(e) => {
441 log_error!("Unable to create a kernel thread: {e:?}");
442 return;
443 }
444 }
445 };
446 release_after!(current_task, {
447 while let Ok(f) = receiver.recv() {
448 let _guard = ThreadLockupDetector::track();
449 f(¤t_task);
450
451 current_task.trigger_delayed_releaser();
453 }
454 });
455
456 DelayedReleaser::finalize();
458 })
459 .expect("able to create threads"),
460 );
461 Self { thread, sender: Some(sender) }
462 }
463
464 fn try_dispatch(&self, f: BoxedClosure) -> Result<(), TrySendError<BoxedClosure>> {
465 self.sender.as_ref().expect("sender should never be None").try_send(f)
466 }
467
468 fn dispatch(&self, f: BoxedClosure) -> Result<(), SendError<BoxedClosure>> {
469 self.sender.as_ref().expect("sender should never be None").send(f)
470 }
471}
472
473impl Drop for RunningThread {
474 fn drop(&mut self) {
475 self.sender = None;
476 match self.thread.take() {
477 Some(thread) => thread.join().expect("Thread should join."),
478 _ => panic!("Thread should never be None"),
479 };
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use crate::testing::spawn_kernel_and_run;
487
488 #[fuchsia::test]
489 async fn run_simple_task() {
490 spawn_kernel_and_run(async |current_task| {
491 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
492 let closure = move |_: &CurrentTask| {};
495 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
496 spawner.spawn_from_request(req);
497 })
498 .await;
499 }
500
501 #[fuchsia::test]
502 async fn run_10_tasks() {
503 spawn_kernel_and_run(async |current_task| {
504 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
505 for _ in 0..10 {
506 let closure = move |_: &CurrentTask| {};
507 let opts = SpawnRequestBuilder::new().with_sync_closure(closure).build();
508 spawner.spawn_from_request(opts);
509 }
510 })
511 .await;
512 }
513
514 #[fuchsia::test]
515 async fn blocking_task_do_not_prevent_further_processing() {
516 spawn_kernel_and_run(async |current_task| {
517 let spawner = DynamicThreadSpawner::new(1, current_task.weak_task(), "kthreadd");
518
519 let pair = Arc::new((fuchsia_sync::Mutex::new(false), fuchsia_sync::Condvar::new()));
520 for _ in 0..10 {
521 let pair2 = Arc::clone(&pair);
522 let closure = move |_: &CurrentTask| {
523 let (lock, cvar) = &*pair2;
524 let mut cont = lock.lock();
525 while !*cont {
526 cvar.wait(&mut cont);
527 }
528 };
529 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
530 spawner.spawn_from_request(req);
531 }
532
533 let closure = move |_: &CurrentTask| {
534 let (lock, cvar) = &*pair;
535 let mut cont = lock.lock();
536 *cont = true;
537 cvar.notify_all();
538 };
539
540 let (result, req) =
541 SpawnRequestBuilder::new().with_sync_closure(closure).build_with_sync_result();
542 spawner.spawn_from_request(req);
543
544 assert_eq!(result(), Ok(()));
545 })
546 .await;
547 }
548
549 #[fuchsia::test]
550 async fn run_spawn_and_get_result() {
551 spawn_kernel_and_run(async |current_task| {
552 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
553
554 let (result, req) =
555 SpawnRequestBuilder::new().with_sync_closure(|_| 3).build_with_sync_result();
556 spawner.spawn_from_request(req);
557 assert_eq!(result(), Ok(3));
558 })
559 .await;
560 }
561
562 #[fuchsia::test]
563 async fn test_spawn_async() {
564 spawn_kernel_and_run(async |current_task| {
565 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
566
567 let closure = move |current_task: &CurrentTask| {
571 let mut exec = fuchsia_async::LocalExecutor::default();
572 let fut = async {};
573 let wrapped_future = WrappedSpawnedFuture::new(current_task, fut, "test-async");
574 exec.run_singlethreaded(wrapped_future);
575 };
576 let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
577 spawner.spawn_from_request(req);
578 })
579 .await;
580 }
581
582 #[fuchsia::test]
583 async fn test_spawn_async_closure() {
584 spawn_kernel_and_run(async |current_task| {
585 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
586 let fut = async |_: &CurrentTask| 42;
587 let (result, req) =
588 SpawnRequestBuilder::new().with_async_closure(fut).build_with_sync_result();
589 spawner.spawn_from_request(req);
590 assert_eq!(result(), Ok(42));
591 })
592 .await;
593 }
594
595 #[fuchsia::test]
596 async fn test_spawn_sync_to_async_result() {
597 spawn_kernel_and_run(async |current_task| {
598 let spawner = DynamicThreadSpawner::new(2, current_task.weak_task(), "kthreadd");
599 let fut = async |_: &CurrentTask| 42;
600 let (result, req) =
601 SpawnRequestBuilder::new().with_async_closure(fut).build_with_sync_result();
602
603 let fut2 = async move |_: &CurrentTask| result().unwrap();
604 let (result2, req2) =
605 SpawnRequestBuilder::new().with_async_closure(fut2).build_with_sync_result();
606 spawner.spawn_from_request(req2);
607 spawner.spawn_from_request(req);
608 assert_eq!(result2(), Ok(42));
609 })
610 .await;
611 }
612
613 #[fuchsia::test]
614 async fn test_spawn_async_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 |_: &CurrentTask| 42;
618 let (result_fut, req) =
619 SpawnRequestBuilder::new().with_async_closure(fut).build_with_async_result();
620
621 let fut2 = async move |_: &CurrentTask| result_fut.await.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}