Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(recycling): add customizable recycling policies (#33)
## Motivation Currently, all queues and channels in `thingbuf` require that items in the queue/channel implement `Default`, because `Default` is used to fill slots when they are initially allocated. Furthermore, when slots are checked out for writing to, they are not cleared prior to being written to --- the user code is responsible for clearing them if needed. The `StringBuf` type currently implements special behavior _specifically_ for `String`s, where the `String` is cleared in place prior to writing to, but this only works for `String`s. `StringBuf` also provides an API for limiting the maximum capacity of "empty" strings, so that they can be shrunk down to that capacity when returning them to the pool. This allows introducing an upper bound on the capacity allocated by unused strings. However, again, this only works with `String`s and is only provided by the `StringBuf` type. This isn't ideal --- users shouldn't _have_ to be responsible for clearing non-`String` types when reusing allocations. ## Solution This branch introduces a new `Recycle<T>` trait that defines a policy for how `T`-typed pooled objects should be reused. `Recycle<T>` defines two methods: * `fn new_element(&self) -> T` creates a new element * `fn recycle(&self, element: &mut T)` clears a pooled element for reuse This allows a `Recycle` implementation to define the lifecycle of a pooled item. In addition, we define a couple of pre-made `Recycle` implementations: * `DefaultRecycle`, which implements `Recycle` for all types `T` where `T: Default + Clone`. This is used by all `thingbuf` types by default. It creates new elements using `Default::default`, and recycles them using `element.clone_from(T::default())`. `Clone::clone_from` is not _guaranteed_ to re-use existing capacity, but it's overridden by most array-based collections (such as the ones in the standard library) to do so --- it should be equivalent to `.clear()` when cloning from an empty collection. However, this policy will still *work* with types that don't have a clear-in-place function. * `WithCapacity` implements `Recycle` only for types that define `with_capacity`, `shrink_to`, and `clear` methods, like all array-based collections in the Rust standard library. Unlike `DefaultRecycle`, it is _guaranteed_ to clear elements in place and retain any previously allocated capacity. It can also be configured to add both upper and lower bounds on capacity. When there is a lower bound, new elements are allocated with that value as their initial capacity, rather than being allocated with 0 capacity. When an upper bound is set, it will call `shrink_to` prior to clearing elements, to limit the total allocated capacity retained by the pool. `WithCapacity` currently implements `Recycle` for all `alloc` and `std` types that define the requisite methods: `Vec`, `String`, `VecDeque`, and `BinaryHeap` when the `alloc` feature is enabled, and `HashMap` and `HashSet` as well, when the `std` feature is enabled. Finally, I've modified the existing queue and channel types to allow configuring them to use a `Recycle` implementation. The `StringBuf` type is removed, as it's now obviated by the new APIs. ## Future Work We may wish to factor out the `recycling` module into its own crate, so that it can be used in other libraries. Closes #30 Signed-off-by: Eliza Weisman <[email protected]>
- Loading branch information