branchless/core/task.rs
1//! Utilities for compute-heavy tasks which need to be run in parallel.
2
3use std::ops::Deref;
4use std::sync::Mutex;
5
6/// A factory which produces a resource for use with [`ResourcePool`].
7pub trait Resource {
8 /// The type of the resource to be produced.
9 type Output;
10
11 /// An error type.
12 type Error;
13
14 /// Constructor for the resource.
15 fn try_create(&self) -> Result<Self::Output, Self::Error>;
16}
17
18/// An unbounded pool of on-demand generated resources. This is useful when
19/// distributing work across threads which needs access to an expensive-to-build
20/// context.
21///
22/// A new resource is created when there isn't one available in the pool. When
23/// it's dropped, it's returned to the pool. Old resources remain in the pool
24/// until the pool itself is dropped.
25///
26/// ```
27/// # use std::cell::RefCell;
28/// # use branchless::core::task::{Resource, ResourceHandle, ResourcePool};
29/// struct MyResource {
30/// num_instantiations: RefCell<usize>,
31/// }
32///
33/// impl Resource for MyResource {
34/// type Output = String;
35/// type Error = std::convert::Infallible;
36/// fn try_create(&self) -> Result<Self::Output, Self::Error> {
37/// let mut r = self.num_instantiations.borrow_mut();
38/// *r += 1;
39/// Ok(format!("This is resource #{}", *r))
40/// }
41/// }
42///
43/// # fn main() {
44/// let resource = MyResource { num_instantiations: Default::default() };
45/// let pool = ResourcePool::new(resource);
46///
47/// // Any number of the resource can be created.
48/// let r1: ResourceHandle<'_, MyResource> = pool.try_create().unwrap();
49/// assert_eq!(&*r1, "This is resource #1");
50/// let r2 = pool.try_create().unwrap();
51/// assert_eq!(&*r2, "This is resource #2");
52/// drop(r2);
53/// drop(r1);
54///
55/// // After releasing a resource, an attempt to get a resource returns an
56/// // existing one from the pool.
57/// let r1_again = pool.try_create().unwrap();
58/// assert_eq!(&*r1_again, "This is resource #1");
59/// # }
60/// ```
61pub struct ResourcePool<R: Resource> {
62 factory: R,
63 resources: Mutex<Vec<R::Output>>,
64}
65
66impl<R: Resource> std::fmt::Debug for ResourcePool<R> {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.debug_struct("ResourcePool")
69 .field("factory", &"<not shown>")
70 .field(
71 "resources.len()",
72 &match self.resources.try_lock() {
73 Ok(resources) => resources.len().to_string(),
74 Err(_) => "<could not determine>".to_string(),
75 },
76 )
77 .finish()
78 }
79}
80
81/// A handle to an instance of a resource created by [`Resource::try_create`].
82/// When this value is dropped, the underlying resource returns to the owning
83/// [`ResourcePool`].
84pub struct ResourceHandle<'pool, R: Resource> {
85 parent: &'pool ResourcePool<R>,
86 inner: Option<R::Output>,
87}
88
89impl<'a, R: Resource> Drop for ResourceHandle<'a, R> {
90 fn drop(&mut self) {
91 let mut resources = self
92 .parent
93 .resources
94 .lock()
95 .expect("Poisoned mutex for ResourceHandle");
96 resources.push(self.inner.take().unwrap());
97 }
98}
99
100impl<'a, R: Resource> Deref for ResourceHandle<'a, R> {
101 type Target = R::Output;
102
103 fn deref(&self) -> &Self::Target {
104 self.inner.as_ref().unwrap()
105 }
106}
107
108impl<R: Resource> ResourcePool<R> {
109 /// Constructor.
110 pub fn new(factory: R) -> Self {
111 ResourcePool {
112 factory,
113 resources: Default::default(),
114 }
115 }
116
117 /// If there are any resources available in the pool, return an arbitrary
118 /// one. Otherwise, invoke the constructor function of the associated
119 /// [`Resource`] and return a [`ResourceHandle`] for it.
120 pub fn try_create(&self) -> Result<ResourceHandle<R>, R::Error> {
121 let resource = {
122 let mut resources = self
123 .resources
124 .lock()
125 .expect("Poisoned mutex for ResourcePool");
126 let resource = resources.pop();
127 match resource {
128 Some(resource) => resource,
129 None => self.factory.try_create()?,
130 }
131 };
132 Ok(ResourceHandle {
133 parent: self,
134 inner: Some(resource),
135 })
136 }
137}