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}