loopcell/
lib.rs

1//! [qcell]: https://docs.rs/qcell/latest/qcell/
2//! [ghost_cell]: https://docs.rs/ghost-cell/latest/ghost_cell/
3//!
4//! 
5//! [LoopCell]: `LoopCell`
6//! [LoopSyncCell]: `LoopSyncCell`
7#![doc = include_str!("../README.md")]
8#![cfg_attr(not(test), no_std)]
9
10use core::{
11    cell::Cell,
12    fmt::Debug,
13    mem,
14    ops::{Deref, DerefMut},
15};
16
17use option_lock::OptionLock;
18
19/// Specialized cell for building iterators that handle a value (such as a shared buffer), make it
20/// available for processing, modification, etc., and then it needs to be available for the next
21/// iteration. Access is performed by [`LoopCell::access`].
22///
23/// Because [`Cell`] is not [`Sync`], the [`LoopCellAccess`] for [`LoopCell`]s are not [`Send`].
24/// This is particularly important when writing [`core::future::Future`]s and asynchronous code,
25/// which is often a place where you'd want this (e.g. handling streams of events). In that case,
26/// you want [`LoopSyncCell`].
27///
28/// Unlike the internal cell data (which uses an [`Option`]), this cell type only implements [`Default`]
29/// if the type parameter implements [`Default`], and contains the relevant default value.
30///
31/// If you access the [`LoopCell`] while something else is holding on to the [`LoopCell`] value,
32/// then you will not be able to obtain a value. You can also
33/// [create an empty `LoopCell` that cannot provide any access][LoopCell::new_empty]
34#[repr(transparent)]
35pub struct LoopCell<T>(Cell<Option<T>>);
36
37impl<T> LoopCell<T> {
38    /// Create a new [`LoopCell`] with the given value
39    #[inline]
40    pub const fn new(initial_value: T) -> Self {
41        Self(Cell::new(Some(initial_value)))
42    }
43
44    /// Create a new [`LoopCell`] using the default value of the type it holds
45    #[inline]
46    pub fn new_default() -> Self
47    where
48        T: Default,
49    {
50        Self::default()
51    }
52
53    /// Create a new, empty [`LoopCell`] that can never provide a value.
54    #[inline]
55    pub const fn new_empty() -> Self {
56        Self(Cell::new(None))
57    }
58
59    /// Attempt to access the loop cell, producing an accessor that will write back any changes to
60    /// the cell once it is dropped (unless it's manually disarmed).
61    ///
62    /// Only produces [`Some`] if no other accessors are using this [`LoopCell`]
63    #[inline]
64    pub fn access(&self) -> Option<LoopCellAccess<'_, T>> {
65        let maybe_available = self.0.replace(None);
66        maybe_available.map(|value| LoopCellAccess::new(self, value))
67    }
68}
69
70impl<T: Copy + Debug> Debug for LoopCell<T> {
71    #[inline]
72    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73        f.debug_tuple("LoopCell").field(&self.0).finish()
74    }
75}
76
77impl<T> From<T> for LoopCell<T> {
78    #[inline]
79    fn from(value: T) -> Self {
80        Self(Cell::new(Some(value)))
81    }
82}
83
84impl<T: Default> Default for LoopCell<T> {
85    #[inline]
86    fn default() -> Self {
87        Self::new(Default::default())
88    }
89}
90
91/// Accessor for a [`LoopCell`], that will automatically write back any modifications to `T`
92/// when dropped (or manually committed), unless you deliberately disarm it with
93/// [`LoopCellAccess::consume`] to make it so the [`LoopCell`] is "used up".
94///
95/// The [`Send`]-equivalent form is [`LoopSyncCellAccess`]
96#[clippy::has_significant_drop]
97pub struct LoopCellAccess<'cell, T> {
98    /// The actual loop cell. When creating this structure, this should now have [`None`] in it, as
99    /// the loop accessor (self) holds the `T`.
100    loop_cell: &'cell LoopCell<T>,
101    /// The value of the cell, directly.
102    ///
103    /// This is **always [`Some`] except during drop or discard/consume**.
104    value: Option<T>,
105}
106
107impl<'cell, T> LoopCellAccess<'cell, T> {
108    /// Build a new [`LoopCellAccess`] from the cell, and the value that was extracted from it.
109    ///
110    /// This is an internal function.
111    #[inline]
112    const fn new(cell: &'cell LoopCell<T>, value: T) -> Self {
113        Self {
114            loop_cell: cell,
115            value: Some(value),
116        }
117    }
118
119    /// Consume the accessor and commit the changes made to the [`LoopCell`]'s value, making the
120    /// value available once again to anyone trying to get it via [`LoopCell::access`]. Equivalent
121    /// to [`mem::drop`]
122    #[inline]
123    pub fn commit(self) {
124        mem::drop(self)
125    }
126
127    /// Consume the accessor, but do not write the value to the [`LoopCell`]. This will leave the
128    /// [`LoopCell`] in such a state that it can no longer ever provide values.
129    ///
130    /// This returns the value itself as-per it's modifications.
131    #[inline]
132    pub fn consume(mut self) -> T {
133        self.value
134            .take()
135            .expect("value inside loop cell accessor should always be Some")
136    }
137
138    /// Get a version of this capable of being mapped.
139    #[inline]
140    pub fn into_mapped_access(self) -> LoopCellMappedAccess<'cell, T, ()> {
141        self.into()
142    }
143
144    /// Directly map this bare [`LoopCellAccess`] into one that is "mapped" to another value.
145    #[inline]
146    pub fn map_loopcell_access<O>(
147        mut self,
148        f: impl FnOnce(&mut T) -> O,
149    ) -> LoopCellMappedAccess<'cell, T, O> {
150        let o = f(&mut self);
151        LoopCellMappedAccess {
152            bare_access: self,
153            value: o,
154        }
155    }
156
157    /// Get the value of the loopcell from the access
158    #[inline]
159    pub fn as_loopcell_value(&self) -> &T {
160        self
161    }
162
163    /// Get the value of the loopcell from the access, mutably.
164    #[inline]
165    pub fn as_loopcell_value_mut(&mut self) -> &mut T {
166        self
167    }
168}
169
170impl<'cell, T: Debug> Debug for LoopCellAccess<'cell, T> {
171    #[inline]
172    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        f.debug_struct("LoopCellAccess")
174            .field("loop_cell", &"<opaque>")
175            .field("value", &self.value)
176            .finish()
177    }
178}
179
180impl<'cell, T> Deref for LoopCellAccess<'cell, T> {
181    type Target = T;
182
183    #[inline]
184    fn deref(&self) -> &T {
185        self.value
186            .as_ref()
187            .expect("value should always be Some inside loopcell accessor")
188    }
189}
190
191impl<'cell, T> DerefMut for LoopCellAccess<'cell, T> {
192    #[inline]
193    fn deref_mut(&mut self) -> &mut T {
194        self.value
195            .as_mut()
196            .expect("value should always be Some inside loopcell accessor")
197    }
198}
199
200impl<'cell, T> AsRef<T> for LoopCellAccess<'cell, T> {
201    #[inline]
202    fn as_ref(&self) -> &T {
203        self
204    }
205}
206
207impl<'cell, T> AsMut<T> for LoopCellAccess<'cell, T> {
208    #[inline]
209    fn as_mut(&mut self) -> &mut T {
210        self
211    }
212}
213
214impl<'cell, T> Drop for LoopCellAccess<'cell, T> {
215    fn drop(&mut self) {
216        // If we've not been disarmed, then set the value
217        if let Some(v) = self.value.take() {
218            self.loop_cell.0.set(Some(v))
219        }
220    }
221}
222
223/// Accessor for a [`LoopCell`], that will automatically write back any modifications to the value
224/// on drop (unless disarmed with [`LoopCellMappedAccess::consume`]) so the [`LoopCell`] is "used
225/// up".
226///
227/// Unlike [`LoopCellAccess`], this contains an extra value, and lets you perform modifications to
228/// it with the "context" of the loop cell accessor (which is mutable).
229///
230/// This provides a clean way to bundle an accessor and also bundle a data value, which should be
231/// processed with the accessor.
232#[clippy::has_significant_drop]
233pub struct LoopCellMappedAccess<'cell, T, V = ()> {
234    /// Bare accessor. This handles all the relevant drop code.
235    bare_access: LoopCellAccess<'cell, T>,
236    /// Value "paired" with the loop cell access.
237    value: V,
238}
239
240impl<'cell, T, V> LoopCellMappedAccess<'cell, T, V> {
241    /// Apply a function to the mapped value, to produce a new mapped value - but it also makes the
242    /// inner loop cell value that we have access to available.
243    #[inline]
244    pub fn map_loopcell_access<O>(
245        self,
246        f: impl FnOnce(&mut T, V) -> O,
247    ) -> LoopCellMappedAccess<'cell, T, O> {
248        let Self {
249            mut bare_access,
250            value,
251        } = self;
252        let o = f(&mut bare_access, value);
253        LoopCellMappedAccess {
254            bare_access,
255            value: o,
256        }
257    }
258
259    /// Commit the changes made to the [`LoopCell`] through this accessor, making the [`LoopCell`]
260    /// available to everyone once again with the new cell data, while also emitting the bundled value.
261    #[inline]
262    pub fn commit(self) -> V {
263        self.bare_access.commit();
264        self.value
265    }
266
267    /// Consume the changes made to the [`LoopCell`] through this accessor, making the [`LoopCell`]
268    /// never have any more values. This returns the last value of the [`LoopCell`] as modified by
269    /// this accessor, as well as the bundled value.
270    #[inline]
271    pub fn consume(self) -> (T, V) {
272        let cell_v = self.bare_access.consume();
273        (cell_v, self.value)
274    }
275
276    /// Get the value of the loopcell from the access
277    #[inline]
278    pub fn get_loopcell_value(&self) -> &T {
279        &self.bare_access
280    }
281
282    /// Get the value of the loopcell from the access, mutably.
283    #[inline]
284    pub fn get_loopcell_value_mut(&mut self) -> &mut T {
285        &mut self.bare_access
286    }
287
288    /// Get the bundled value from the access
289    #[inline]
290    pub fn get_value(&self) -> &V {
291        &self.value
292    }
293
294    /// Get the bundled value from the access, mutably.
295    #[inline]
296    pub fn get_value_mut(&mut self) -> &mut V {
297        &mut self.value
298    }
299
300    #[inline]
301    /// Unpack the bundled value from the normal [`LoopCellAccess`]
302    pub fn unmap_access(self) -> (LoopCellAccess<'cell, T>, V) {
303        (self.bare_access, self.value)
304    }
305}
306
307impl<'cell, T, V: Default> From<LoopCellAccess<'cell, T>> for LoopCellMappedAccess<'cell, T, V> {
308    #[inline]
309    fn from(bare_access: LoopCellAccess<'cell, T>) -> Self {
310        Self {
311            bare_access,
312            value: Default::default(),
313        }
314    }
315}
316
317impl<'cell, T, V> Deref for LoopCellMappedAccess<'cell, T, V> {
318    type Target = V;
319
320    #[inline]
321    fn deref(&self) -> &Self::Target {
322        self.get_value()
323    }
324}
325
326impl<'cell, T, V> DerefMut for LoopCellMappedAccess<'cell, T, V> {
327    #[inline]
328    fn deref_mut(&mut self) -> &mut Self::Target {
329        self.get_value_mut()
330    }
331}
332
333impl<'cell, T, V> AsRef<V> for LoopCellMappedAccess<'cell, T, V> {
334    #[inline]
335    fn as_ref(&self) -> &V {
336        self
337    }
338}
339
340impl<'cell, T, V> AsMut<V> for LoopCellMappedAccess<'cell, T, V> {
341    #[inline]
342    fn as_mut(&mut self) -> &mut V {
343        self
344    }
345}
346
347/// Specialized cell for when you want to build an iterator that produces a value, to then be
348/// modified and used, but then finally made available for the next iteration. The value of this
349/// cell can be restored from an accessor on a different thread to the one where it was taken (i.e.
350/// it is [`Sync`]).
351///
352/// This form is particularly useful when writing asynchronous event processors with shared data,
353/// where you might need to hold an access across an await point. It is an alternative to
354/// [`LoopCell`], and is likely to be used often.
355///
356/// Unlike the underlying [`option_lock::OptionLock`], this only implements [`Default`] if the type
357/// parameter `T` implements [`Default`], and it is created with the default value of `T` in that
358/// case. Also note that [`option_lock::OptionLock`] is implemented entirely with atomics - it's
359/// very lightweight and doesn't carry the overhead of allocation or normal mutex. This is
360/// essentially close to the most minimal equivalent to how we use `Cell<Option<T>>` in [`LoopCell`].
361///
362/// If you access the [`LoopSyncCell`] while something else is holding on to an accessor, then you
363/// won't be able to get any access. If something else deliberately consumes it's accessor, then
364/// you will not be able to get any values out of this any more. You can also
365/// [create a `LoopSyncCell` which cannot have anything retrieved][LoopSyncCell::new_empty]
366// The internal lock here is only held for very short periods of time (for taking and then
367// restoring). This is why we tolerate spinlocking, as it's an extremely ephemeral lock. This type
368// only ever permits exactly one accessor to actually exist at a time with `Some` in it.
369#[repr(transparent)]
370pub struct LoopSyncCell<T>(OptionLock<T>);
371
372impl<T> LoopSyncCell<T> {
373    /// Create a new [`LoopSyncCell`] with the given value
374    #[inline]
375    pub const fn new(initial_value: T) -> Self {
376        Self(OptionLock::new(initial_value))
377    }
378
379    /// Create a new [`LoopSyncCell`] using the default value of the type it holds
380    #[inline]
381    pub fn new_default() -> Self
382    where
383        T: Default,
384    {
385        Self::default()
386    }
387
388    /// Create a new, empty [`LoopSyncCell`] that can never provide a value.
389    #[inline]
390    pub const fn new_empty() -> Self {
391        Self(OptionLock::empty())
392    }
393
394    /// Attempt to access the loop cell, producing an accessor that will write back any changes to
395    /// the cell once it is dropped (unless it's manually disarmed).
396    ///
397    /// Only produces [`Some`] if no other accessors are using this [`LoopSyncCell`]
398    #[inline]
399    pub fn access(&self) -> Option<LoopSyncCellAccess<'_, T>> {
400        // Spinlock is ok because the lock is only ever held very ephemerally.
401        let maybe_available = self.0.spin_lock().take();
402        maybe_available.map(|value| LoopSyncCellAccess::new(self, value))
403    }
404}
405
406impl<T: Copy + Debug> Debug for LoopSyncCell<T> {
407    #[inline]
408    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
409        f.debug_tuple("LoopSyncCell").field(&self.0).finish()
410    }
411}
412
413impl<T> From<T> for LoopSyncCell<T> {
414    #[inline]
415    fn from(value: T) -> Self {
416        Self::new(value)
417    }
418}
419
420impl<T: Default> Default for LoopSyncCell<T> {
421    #[inline]
422    fn default() -> Self {
423        Self::new(T::default())
424    }
425}
426
427/// Accessor for a [`LoopSyncCell`], that will automatically write back any modifications to `T`
428/// when dropped (or manually committed), unless you deliberately disarm it with
429/// [`LoopSyncCellAccess::consume`] to make it so the [`LoopSyncCell`] is "used up".
430#[clippy::has_significant_drop]
431pub struct LoopSyncCellAccess<'cell, T> {
432    /// The actual loop cell. When creating this structure, this should now have [`None`] in it, as
433    /// the loop accessor (self) holds the `T`.
434    loop_cell: &'cell LoopSyncCell<T>,
435    /// The value of the cell, directly.
436    ///
437    /// This is **always [`Some`] except during drop or discard/consume**.
438    value: Option<T>,
439}
440
441impl<'cell, T> LoopSyncCellAccess<'cell, T> {
442    /// Build a new [`LoopSyncCellAccess`] from the cell, and the value that was extracted from it.
443    ///
444    /// This is an internal function.
445    #[inline]
446    const fn new(cell: &'cell LoopSyncCell<T>, value: T) -> Self {
447        Self {
448            loop_cell: cell,
449            value: Some(value),
450        }
451    }
452
453    /// Consume the accessor and commit the changes made to the [`LoopSyncCell`]'s value, making the
454    /// value available once again to anyone trying to get it via [`LoopSyncCell::access`]. Equivalent
455    /// to [`mem::drop`]
456    #[inline]
457    pub fn commit(self) {
458        mem::drop(self)
459    }
460
461    /// Consume the accessor, but do not write the value to the [`LoopSyncCell`]. This will leave the
462    /// [`LoopSyncCell`] in such a state that it can no longer ever provide values.
463    ///
464    /// This returns the value itself as-per it's modifications.
465    #[inline]
466    pub fn consume(mut self) -> T {
467        self.value
468            .take()
469            .expect("value inside loop cell accessor should always be Some")
470    }
471
472    /// Get a version of this capable of being mapped.
473    #[inline]
474    pub fn into_mapped_access(self) -> LoopSyncCellMappedAccess<'cell, T, ()> {
475        self.into()
476    }
477
478    /// Directly map this bare [`LoopSyncCellAccess`] into one that is "mapped" to another value.
479    #[inline]
480    pub fn map_loopcell_access<O>(
481        mut self,
482        f: impl FnOnce(&mut T) -> O,
483    ) -> LoopSyncCellMappedAccess<'cell, T, O> {
484        let o = f(&mut self);
485        LoopSyncCellMappedAccess {
486            bare_access: self,
487            value: o,
488        }
489    }
490
491    /// Get the value of the loopcell from the access
492    #[inline]
493    pub fn as_loopcell_value(&self) -> &T {
494        self
495    }
496
497    /// Get the value of the loopcell from the access, mutably.
498    #[inline]
499    pub fn as_loopcell_value_mut(&mut self) -> &mut T {
500        self
501    }
502}
503
504impl<'cell, T: Debug> Debug for LoopSyncCellAccess<'cell, T> {
505    #[inline]
506    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
507        f.debug_struct("LoopSyncCellAccess")
508            .field("loop_cell", &"<opaque>")
509            .field("value", &self.value)
510            .finish()
511    }
512}
513
514impl<'cell, T> Deref for LoopSyncCellAccess<'cell, T> {
515    type Target = T;
516
517    #[inline]
518    fn deref(&self) -> &T {
519        self.value
520            .as_ref()
521            .expect("value should always be Some inside loopcell accessor")
522    }
523}
524
525impl<'cell, T> DerefMut for LoopSyncCellAccess<'cell, T> {
526    #[inline]
527    fn deref_mut(&mut self) -> &mut T {
528        self.value
529            .as_mut()
530            .expect("value should always be Some inside loopcell accessor")
531    }
532}
533
534impl<'cell, T> AsRef<T> for LoopSyncCellAccess<'cell, T> {
535    #[inline]
536    fn as_ref(&self) -> &T {
537        self
538    }
539}
540
541impl<'cell, T> AsMut<T> for LoopSyncCellAccess<'cell, T> {
542    #[inline]
543    fn as_mut(&mut self) -> &mut T {
544        self
545    }
546}
547
548impl<'cell, T> Drop for LoopSyncCellAccess<'cell, T> {
549    fn drop(&mut self) {
550        // While `self.value` is almost always `Some`, if we've been disarmed then it will be `None`. No point in spinlocking if there is nothing there.
551        if let Some(v) = self.value.take() {
552            // spinlock is ok because this is very ephemeral - note that the value returned "should" be `None` as only one thing can take out of the loop sync cell at once.
553            self.loop_cell.0.spin_lock().replace(v);
554        }
555    }
556}
557
558/// Accessor for a [`LoopSyncCell`], that will automatically write back any modifications to the value
559/// on drop (unless disarmed with [`LoopSyncCellMappedAccess::consume`]) so the [`LoopSyncCell`] is "used
560/// up".
561///
562/// Unlike [`LoopSyncCellAccess`], this contains an extra value, and lets you perform modifications to
563/// it with the "context" of the loop cell accessor (which is mutable).
564///
565/// This provides a clean way to bundle an accessor and also bundle a data value, which should be
566/// processed with the accessor.
567#[clippy::has_significant_drop]
568pub struct LoopSyncCellMappedAccess<'cell, T, V = ()> {
569    /// Bare accessor. This handles all the relevant drop code.
570    bare_access: LoopSyncCellAccess<'cell, T>,
571    /// Value "paired" with the loop cell access.
572    value: V,
573}
574
575impl<'cell, T, V> LoopSyncCellMappedAccess<'cell, T, V> {
576    /// Apply a function to the mapped value, to produce a new mapped value - but it also makes the
577    /// inner loop cell value that we have access to available.
578    #[inline]
579    pub fn map_loopcell_access<O>(
580        self,
581        f: impl FnOnce(&mut T, V) -> O,
582    ) -> LoopSyncCellMappedAccess<'cell, T, O> {
583        let Self {
584            mut bare_access,
585            value,
586        } = self;
587        let o = f(&mut bare_access, value);
588        LoopSyncCellMappedAccess {
589            bare_access,
590            value: o,
591        }
592    }
593
594    /// Commit the changes made to the [`LoopSyncCell`] through this accessor, making the [`LoopSyncCell`]
595    /// available to everyone once again with the new cell data, while also emitting the bundled value.
596    #[inline]
597    pub fn commit(self) -> V {
598        self.bare_access.commit();
599        self.value
600    }
601
602    /// Consume the changes made to the [`LoopSyncCell`] through this accessor, making the [`LoopSyncCell`]
603    /// never have any more values. This returns the last value of the [`LoopSyncCell`] as modified by
604    /// this accessor, as well as the bundled value.
605    #[inline]
606    pub fn consume(self) -> (T, V) {
607        let cell_v = self.bare_access.consume();
608        (cell_v, self.value)
609    }
610
611    /// Get the value of the loopcell from the access
612    #[inline]
613    pub fn get_loopcell_value(&self) -> &T {
614        &self.bare_access
615    }
616
617    /// Get the value of the loopcell from the access, mutably.
618    #[inline]
619    pub fn get_loopcell_value_mut(&mut self) -> &mut T {
620        &mut self.bare_access
621    }
622
623    /// Get the bundled value from the access
624    #[inline]
625    pub fn get_value(&self) -> &V {
626        &self.value
627    }
628
629    /// Get the bundled value from the access, mutably.
630    #[inline]
631    pub fn get_value_mut(&mut self) -> &mut V {
632        &mut self.value
633    }
634
635    #[inline]
636    /// Unpack the bundled value from the normal [`LoopSyncCellAccess`]
637    pub fn unmap_access(self) -> (LoopSyncCellAccess<'cell, T>, V) {
638        (self.bare_access, self.value)
639    }
640}
641
642impl<'cell, T, V: Default> From<LoopSyncCellAccess<'cell, T>>
643    for LoopSyncCellMappedAccess<'cell, T, V>
644{
645    #[inline]
646    fn from(bare_access: LoopSyncCellAccess<'cell, T>) -> Self {
647        Self {
648            bare_access,
649            value: Default::default(),
650        }
651    }
652}
653
654impl<'cell, T, V> Deref for LoopSyncCellMappedAccess<'cell, T, V> {
655    type Target = V;
656
657    #[inline]
658    fn deref(&self) -> &Self::Target {
659        self.get_value()
660    }
661}
662
663impl<'cell, T, V> DerefMut for LoopSyncCellMappedAccess<'cell, T, V> {
664    #[inline]
665    fn deref_mut(&mut self) -> &mut Self::Target {
666        self.get_value_mut()
667    }
668}
669
670impl<'cell, T, V> AsRef<V> for LoopSyncCellMappedAccess<'cell, T, V> {
671    #[inline]
672    fn as_ref(&self) -> &V {
673        self
674    }
675}
676
677impl<'cell, T, V> AsMut<V> for LoopSyncCellMappedAccess<'cell, T, V> {
678    #[inline]
679    fn as_mut(&mut self) -> &mut V {
680        self
681    }
682}
683
684// This Source Code Form is subject to the terms of the Mozilla Public
685// License, v. 2.0. If a copy of the MPL was not distributed with this
686// file, You can obtain one at http://mozilla.org/MPL/2.0/.