Skip to main content

tcontract_exec/
pool.rs

1use crate::ArenaProvider;
2use std::sync::Arc;
3
4/// The CPU resources one owner lends to every operation: a
5/// [`threadteam::Pool`] (where tasks run) and an [`ArenaProvider`] (where
6/// scratch lives).
7///
8/// The wrapper never starts threads itself; [`threadteam::PoolBuilder`] is
9/// the only place a pool is made. Retain the wrapper across calls so its arena
10/// is reused; [`Exec::new`](crate::Exec::new) mints the per-call resource
11/// bundle from it, and that constructor is the only place a thread budget is
12/// clamped to the pool width.
13///
14/// # Examples
15///
16/// ```
17/// let sp = threadteam::Pool::builder().threads(3).build().unwrap();
18/// let pool = tcontract_exec::Pool::borrow(&sp);
19/// assert_eq!(pool.size(), sp.width().get());
20/// ```
21pub struct Pool<'p> {
22    pool: PoolRef<'p>,
23    /// Retained storage for operations through this wrapper. Reusing the wrapper
24    /// reuses its worker buffers, B panel and barriered team sets.
25    workspace: ArenaProvider,
26}
27
28enum PoolRef<'p> {
29    Borrowed(&'p threadteam::Pool),
30    Shared(Arc<threadteam::Pool>),
31}
32
33impl<'p> Pool<'p> {
34    /// Borrow a pool the host owns (an engine that keeps its own
35    /// [`threadteam::Pool`]).
36    pub fn borrow(pool: &'p threadteam::Pool) -> Self {
37        Self::with(PoolRef::Borrowed(pool))
38    }
39
40    fn with(pool: PoolRef<'p>) -> Self {
41        Self {
42            pool,
43            workspace: ArenaProvider::new(),
44        }
45    }
46
47    /// The storage this pool lends to every operation that runs on it.
48    ///
49    /// Borrowed, not copied: one pool is one owner, and a driver that is given
50    /// this provider returns its team sets to it on drop.
51    pub fn workspace(&self) -> &ArenaProvider {
52        &self.workspace
53    }
54
55    /// Release the pool's idle workspace storage. Live leases and borrowed
56    /// worker buffers survive; a worker parked on this pool keeps its slot
57    /// until it is idle again.
58    pub fn trim_workspace(&self) {
59        use crate::WorkspaceProvider;
60        self.workspace.trim();
61    }
62
63    /// Number of workers, the largest budget [`Exec::new`](crate::Exec::new)
64    /// grants.
65    pub fn size(&self) -> usize {
66        self.inner().width().get()
67    }
68
69    pub(crate) fn inner(&self) -> &threadteam::Pool {
70        match &self.pool {
71            PoolRef::Borrowed(p) => p,
72            PoolRef::Shared(p) => p,
73        }
74    }
75}
76
77impl Pool<'static> {
78    /// Take ownership of a pool, for hosts without one of their own (the C
79    /// ABI's executors). Dropping the wrapper shuts the workers down.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// let sp = threadteam::Pool::builder().threads(2).build().unwrap();
85    /// let pool = tcontract_exec::Pool::owned(sp);
86    /// assert!(pool.size() >= 1);
87    /// ```
88    pub fn owned(pool: threadteam::Pool) -> Self {
89        Self::shared(Arc::new(pool))
90    }
91
92    /// Share one [`threadteam::Pool`] between several wrappers, each with its
93    /// own workspace (the C ABI lends one pool handle to several executors). The
94    /// workers stop when the last wrapper is dropped.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use std::sync::Arc;
100    /// let sp = Arc::new(threadteam::Pool::builder().threads(2).build().unwrap());
101    /// let a = tcontract_exec::Pool::shared(Arc::clone(&sp));
102    /// let b = tcontract_exec::Pool::shared(sp);
103    /// assert_eq!(a.size(), b.size());
104    /// ```
105    pub fn shared(pool: Arc<threadteam::Pool>) -> Self {
106        Self::with(PoolRef::Shared(pool))
107    }
108}
109
110impl std::fmt::Debug for Pool<'_> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.debug_struct("Pool").field("size", &self.size()).finish()
113    }
114}