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/.