job_pool/scope.rs
1use core::marker::PhantomData;
2use core::mem;
3
4use crate::worker::Job;
5use crate::{Counter, ThreadPool};
6
7/// A scope to spawn jobs inside a [ThreadPool]
8///
9/// This struct is created by the [ThreadPool::scope] function
10pub struct Scope<'scope, 'pool: 'scope> {
11 scope_counter: Counter,
12 pool: &'pool ThreadPool,
13
14 /// Invariance over 'scope, to make sure 'scope cannot shrink,
15 /// which is necessary for soundness.
16 ///
17 /// Without invariance, this would compile fine but be unsound:
18 ///
19 /// ```compile_fail,E0373
20 /// use job_pool::ThreadPool;
21 ///
22 /// let pool = ThreadPool::default();
23 /// pool.scope(|s| {
24 /// s.spawn(|| {
25 /// let a = String::from("abcd");
26 /// s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
27 /// });
28 /// });
29 /// ```
30 _marker_scope: PhantomData<&'scope mut &'scope ()>,
31}
32
33impl<'scope, 'pool> Scope<'scope, 'pool> {
34 pub(super) fn new(pool: &'pool ThreadPool) -> Self {
35 Self {
36 scope_counter: Counter::new(),
37 pool,
38 _marker_scope: PhantomData,
39 }
40 }
41
42 /// Executes a job inside this [Scope].
43 pub fn execute(&self, job: impl Job<'scope>) {
44 let job: Box<dyn Job<'scope>> = Box::new(job);
45 /* SAFETY: Scope makes sure that all jobs sent through it are
46 * finished before droping it. So the jobs won't outlive the
47 * 'scope lifetime. */
48 let job: Box<dyn Job<'static>> = unsafe { mem::transmute(job) };
49 self.pool.execute_inside_scope(job, self.scope_counter.clone());
50 }
51
52 /// Creates a new scope inside `self`.
53 ///
54 /// # Example
55 /// ```
56 /// use job_pool::ThreadPool;
57 ///
58 /// let pool = ThreadPool::default();
59 ///
60 /// let msg = String::from("Helloo :)");
61 /// pool.scope(|scope| {
62 /// let helloworld = format!("{msg} world!");
63 /// scope.subscope(|subscope1| {
64 /// subscope1.execute(|| println!("1) {helloworld}"));
65 /// subscope1.execute(|| println!("2) {helloworld}"));
66 /// });
67 /// });
68 /// ```
69 pub fn subscope<'new, F, R>(&self, f: F) -> R
70 where
71 F: FnOnce(&Scope<'new, 'pool>) -> R,
72 'scope: 'new
73 {
74 let scope = Scope {
75 scope_counter: Counter::new(),
76 pool: self.pool,
77 _marker_scope: PhantomData,
78 };
79 f(&scope)
80 }
81}
82
83impl Drop for Scope<'_, '_> {
84 fn drop(&mut self) {
85 self.scope_counter.join();
86 }
87}