1+ //! Intrusive Reference Counter (Irc)
2+ //!
3+ //! `Irc` is an intrusive reference counting smart pointer, similar to `Arc` but without weak reference support.
4+ //! It requires the inner type to implement [IrcItem] trait to provide a counter field.
5+ //!
6+ //! The underlayer of `Irc` is Box, unlike `Arc` which wrap a hidden ArcInner on your inner types,
7+ //! Irc use the same memory location of your inner types.
8+ //!
9+ //! [IrcItem::on_drop] in the trait allow you to have the ownship of underlying inner memory after the reference count of Irc is dropped.
10+ //!
11+ //! # Example
12+ //!
13+ //! ```rust
14+ //! use embed_collections::irc::{Irc, IrcItem};
15+ //! use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
16+ //! use crossfire::oneshot;
17+ //! use std::thread;
18+ //! use std::time::Duration;
19+ //!
20+ //! // Usually we use Irc for some large structure, but we show a simple demo here.
21+ //! struct MyItem {
22+ //! is_done: AtomicBool,
23+ //! counter: AtomicUsize,
24+ //! done_tx: Option<oneshot::TxOneshot<Box<MyItem>>>,
25+ //! }
26+ //!
27+ //! unsafe impl IrcItem<()> for MyItem {
28+ //! type Counter = AtomicUsize;
29+ //! fn counter(&self) -> &Self::Counter {
30+ //! &self.counter
31+ //! }
32+ //!
33+ //! // overwrite default behavior to send the item through channel
34+ //! fn on_drop(mut this: Box<Self>) {
35+ //! let done_tx = this.done_tx.take().unwrap();
36+ //! done_tx.send(this);
37+ //! }
38+ //! }
39+ //!
40+ //! let (done_tx, done_rx) = oneshot::oneshot();
41+ //! let boxed_item = Box::new(MyItem {
42+ //! is_done: AtomicBool::new(false),
43+ //! counter: AtomicUsize::new(0),
44+ //! done_tx: Some(done_tx),
45+ //! });
46+ //!
47+ //! // Convert from Box to Irc, which does not have additional allocation.
48+ //! let item = Irc::<_, ()>::from(boxed_item);
49+ //! thread::spawn(move || {
50+ //! thread::sleep(Duration::from_secs(1));
51+ //! item.is_done.store(true, Ordering::SeqCst);
52+ //! drop(item);
53+ //! });
54+ //! let item: Box<MyItem> = done_rx.recv().unwrap();
55+ //! assert!(item.is_done.load(Ordering::SeqCst));
56+ //! ```
57+
158use crate :: { Pointer , SmartPointer } ;
259use alloc:: boxed:: Box ;
360use atomic_traits:: {
3289
3390 /// The default behavior for Irc is dropping the boxed inner.
3491 ///
35- /// You can overwrite this if you want to send the inner somewhere
92+ /// You can overwrite this if you want to send the inner somewhere.
93+ /// We pass box here to reduce moving cost.
94+ #[ allow( clippy:: boxed_local) ]
3695 #[ inline( always) ]
3796 fn on_drop ( _this : Box < Self > ) { }
3897
44103
45104/// Intrusive reference counter, which support conversion bwteween `Box<T>`.
46105///
47- /// It does not support weak reference
106+ /// It does not support weak reference.
48107pub struct Irc < T : IrcItem < Tag > , Tag > {
49108 inner : NonNull < T > ,
50109 _phan : PhantomData < fn ( & Tag ) > ,
@@ -79,6 +138,37 @@ impl<T: IrcItem<Tag>, Tag> Irc<T, Tag> {
79138 ///
80139 /// it's possible to return false when counter drop to 1,
81140 /// Because of using Acquire load and Release on drop.
141+ ///
142+ /// # Example
143+ ///
144+ ///
145+ /// ```rust
146+ /// use embed_collections::irc::{Irc, IrcItem};
147+ /// use core::sync::atomic::AtomicUsize;
148+ ///
149+ /// struct Tag;
150+ ///
151+ /// struct MyItem {
152+ /// value: i32,
153+ /// counter: AtomicUsize,
154+ /// }
155+ ///
156+ /// unsafe impl IrcItem<Tag> for MyItem {
157+ /// type Counter = AtomicUsize;
158+ /// fn counter(&self) -> &Self::Counter {
159+ /// &self.counter
160+ /// }
161+ /// }
162+ ///
163+ /// // Create a new Irc
164+ /// let irc1 = Irc::<_, Tag>::new(MyItem { value: 10, counter: AtomicUsize::new(0) });
165+ /// assert_eq!(irc1.value, 10);
166+ /// assert!(irc1.is_unique());
167+ ///
168+ /// // Clone the Irc
169+ /// let irc2 = irc1.clone();
170+ /// assert_eq!(irc1.strong_count(), 2);
171+ /// assert!(!irc1.is_unique());
82172 #[ inline]
83173 pub fn is_unique ( & self ) -> bool {
84174 // Safety:
@@ -100,6 +190,42 @@ impl<T: IrcItem<Tag>, Tag> Irc<T, Tag> {
100190
101191impl < T : IrcItem < Tag > + Clone , Tag > Irc < T , Tag > {
102192 /// The Cow function, the same as `Arc::make_mut()`
193+ ///
194+ /// # Example
195+ ///
196+ /// ```rust
197+ /// use embed_collections::irc::{Irc, IrcItem};
198+ /// use core::sync::atomic::AtomicUsize;
199+ ///
200+ /// struct Tag;
201+ /// struct MyItem {
202+ /// value: i32,
203+ /// counter: AtomicUsize,
204+ /// }
205+ ///
206+ /// impl Clone for MyItem {
207+ /// fn clone(&self) -> Self {
208+ /// Self { value: self.value, counter: AtomicUsize::new(0) }
209+ /// }
210+ /// }
211+ ///
212+ /// unsafe impl IrcItem<Tag> for MyItem {
213+ /// type Counter = AtomicUsize;
214+ /// fn counter(&self) -> &Self::Counter {
215+ /// &self.counter
216+ /// }
217+ /// }
218+ ///
219+ /// let mut irc1 = Irc::<_, Tag>::new(MyItem { value: 10, counter: AtomicUsize::new(0) });
220+ /// let irc2 = irc1.clone();
221+ ///
222+ /// // This will clone the inner item because it's shared
223+ /// let m = Irc::make_mut(&mut irc1);
224+ /// m.value = 20;
225+ ///
226+ /// assert_eq!(irc1.value, 20);
227+ /// assert_eq!(irc2.value, 10);
228+ /// ```
103229 #[ inline]
104230 pub fn make_mut ( this : & mut Self ) -> & mut T {
105231 if !this. is_unique ( ) {
@@ -212,3 +338,166 @@ impl<T: IrcItem<Tag>, Tag> SmartPointer for Irc<T, Tag> {
212338 Irc :: new ( inner)
213339 }
214340}
341+
342+ #[ cfg( test) ]
343+ mod tests {
344+ use super :: * ;
345+ use crate :: test:: { CounterI32 , alive_count, reset_alive_count} ;
346+ use core:: sync:: atomic:: AtomicUsize ;
347+ use std:: thread;
348+
349+ struct Tag ;
350+
351+ struct TestItem {
352+ value : CounterI32 ,
353+ counter : AtomicUsize ,
354+ }
355+
356+ impl TestItem {
357+ fn new ( val : i32 ) -> Self {
358+ Self { value : CounterI32 :: new ( val) , counter : AtomicUsize :: new ( 0 ) }
359+ }
360+ }
361+
362+ impl Clone for TestItem {
363+ fn clone ( & self ) -> Self {
364+ Self { value : self . value . clone ( ) , counter : AtomicUsize :: new ( 0 ) }
365+ }
366+ }
367+
368+ unsafe impl IrcItem < Tag > for TestItem {
369+ type Counter = AtomicUsize ;
370+ fn counter ( & self ) -> & Self :: Counter {
371+ & self . counter
372+ }
373+ }
374+
375+ #[ test]
376+ fn test_basic ( ) {
377+ reset_alive_count ( ) ;
378+ {
379+ let item = TestItem :: new ( 10 ) ;
380+ let irc1 = Irc :: < _ , Tag > :: new ( item) ;
381+ assert_eq ! ( irc1. value. value, 10 ) ;
382+ assert_eq ! ( irc1. strong_count( ) , 1 ) ;
383+ assert ! ( irc1. is_unique( ) ) ;
384+ assert_eq ! ( alive_count( ) , 1 ) ;
385+
386+ let irc2 = irc1. clone ( ) ;
387+ assert_eq ! ( irc1. strong_count( ) , 2 ) ;
388+ assert_eq ! ( irc2. strong_count( ) , 2 ) ;
389+ assert ! ( !irc1. is_unique( ) ) ;
390+ assert_eq ! ( alive_count( ) , 1 ) ;
391+
392+ drop ( irc1) ;
393+ assert_eq ! ( irc2. strong_count( ) , 1 ) ;
394+ assert ! ( irc2. is_unique( ) ) ;
395+ assert_eq ! ( alive_count( ) , 1 ) ;
396+ }
397+ assert_eq ! ( alive_count( ) , 0 ) ;
398+ }
399+
400+ #[ test]
401+ fn test_get_mut ( ) {
402+ reset_alive_count ( ) ;
403+ let mut irc = Irc :: < _ , Tag > :: new ( TestItem :: new ( 10 ) ) ;
404+ assert ! ( Irc :: get_mut( & mut irc) . is_some( ) ) ;
405+
406+ let _irc2 = irc. clone ( ) ;
407+ assert ! ( Irc :: get_mut( & mut irc) . is_none( ) ) ;
408+ }
409+
410+ #[ test]
411+ fn test_make_mut ( ) {
412+ reset_alive_count ( ) ;
413+ let mut irc = Irc :: < _ , Tag > :: new ( TestItem :: new ( 10 ) ) ;
414+
415+ // Unique, no clone
416+ {
417+ let m = Irc :: make_mut ( & mut irc) ;
418+ m. value . value = 20 ;
419+ }
420+ assert_eq ! ( irc. value. value, 20 ) ;
421+ assert_eq ! ( alive_count( ) , 1 ) ;
422+
423+ // Not unique, should clone
424+ let irc2 = irc. clone ( ) ;
425+ assert_eq ! ( alive_count( ) , 1 ) ;
426+ {
427+ let m = Irc :: make_mut ( & mut irc) ;
428+ m. value . value = 30 ;
429+ }
430+ assert_eq ! ( irc. value. value, 30 ) ;
431+ assert_eq ! ( irc2. value. value, 20 ) ;
432+ assert_eq ! ( alive_count( ) , 2 ) ;
433+
434+ assert ! ( irc. is_unique( ) ) ;
435+ assert ! ( irc2. is_unique( ) ) ;
436+ }
437+
438+ #[ test]
439+ fn test_multithread_count ( ) {
440+ reset_alive_count ( ) ;
441+ {
442+ let irc = Irc :: < _ , Tag > :: new ( TestItem :: new ( 0 ) ) ;
443+ let mut handles = vec ! [ ] ;
444+
445+ for _ in 0 ..10 {
446+ let irc_clone = irc. clone ( ) ;
447+ handles. push ( thread:: spawn ( move || {
448+ for _ in 0 ..1000 {
449+ let temp = irc_clone. clone ( ) ;
450+ assert_eq ! ( temp. value. value, 0 ) ;
451+ }
452+ } ) ) ;
453+ }
454+
455+ for handle in handles {
456+ handle. join ( ) . unwrap ( ) ;
457+ }
458+
459+ assert_eq ! ( irc. strong_count( ) , 1 ) ;
460+ assert ! ( irc. is_unique( ) ) ;
461+ assert_eq ! ( alive_count( ) , 1 ) ;
462+ }
463+ assert_eq ! ( alive_count( ) , 0 ) ;
464+ }
465+
466+ #[ test]
467+ fn test_multithread_drop ( ) {
468+ reset_alive_count ( ) ;
469+ {
470+ let irc = Irc :: < _ , Tag > :: new ( TestItem :: new ( 0 ) ) ;
471+ let mut handles = vec ! [ ] ;
472+ for _ in 0 ..10 {
473+ let irc_clone = irc. clone ( ) ;
474+ handles. push ( thread:: spawn ( move || {
475+ for _ in 0 ..1000 {
476+ let temp = irc_clone. clone ( ) ;
477+ assert_eq ! ( temp. value. value, 0 ) ;
478+ }
479+ } ) ) ;
480+ }
481+ drop ( irc) ;
482+ for handle in handles {
483+ handle. join ( ) . unwrap ( ) ;
484+ }
485+ }
486+ assert_eq ! ( alive_count( ) , 0 ) ;
487+ }
488+
489+ #[ test]
490+ fn test_drop_all ( ) {
491+ reset_alive_count ( ) ;
492+ let irc = Irc :: < _ , Tag > :: new ( TestItem :: new ( 0 ) ) ;
493+ let mut clones = vec ! [ ] ;
494+ for _ in 0 ..100 {
495+ clones. push ( irc. clone ( ) ) ;
496+ }
497+ assert_eq ! ( alive_count( ) , 1 ) ;
498+ drop ( clones) ;
499+ assert_eq ! ( alive_count( ) , 1 ) ;
500+ drop ( irc) ;
501+ assert_eq ! ( alive_count( ) , 0 ) ;
502+ }
503+ }
0 commit comments