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