Skip to content

Commit ba3c421

Browse files
committed
irc: Add test and doc
1 parent 75562e9 commit ba3c421

2 files changed

Lines changed: 282 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ fastrand = "2.3"
3737
criterion2 = { version="3.0.2"}
3838
captains-log = {version="0", features = ["ringfile"] }
3939
log = { version="0"}
40+
crossfire = "3.1"
4041

4142
[[bench]]
4243
name = "avl"

src/irc.rs

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
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+
//! # Example
7+
//!
8+
//! ```rust
9+
//! use embed_collections::irc::{Irc, IrcItem};
10+
//! use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
11+
//! use crossfire::oneshot;
12+
//! use std::thread;
13+
//! use std::time::Duration;
14+
//!
15+
//! struct MyItem {
16+
//! is_done: AtomicBool,
17+
//! counter: AtomicUsize,
18+
//! done_tx: Option<oneshot::TxOneshot<Box<MyItem>>>,
19+
//! }
20+
//!
21+
//! unsafe impl IrcItem<()> for MyItem {
22+
//! type Counter = AtomicUsize;
23+
//! fn counter(&self) -> &Self::Counter {
24+
//! &self.counter
25+
//! }
26+
//!
27+
//! // overwrite default behavior to send the item through channel
28+
//! fn on_drop(mut this: Box<Self>) {
29+
//! let done_tx = this.done_tx.take().unwrap();
30+
//! done_tx.send(this);
31+
//! }
32+
//! }
33+
//!
34+
//! let (done_tx, done_rx) = oneshot::oneshot();
35+
//! let boxed_item = Box::new(MyItem {
36+
//! is_done: AtomicBool::new(false),
37+
//! counter: AtomicUsize::new(0),
38+
//! done_tx: Some(done_tx),
39+
//! });
40+
//!
41+
//! // Convert from Box to Irc, which does not have additional allocation.
42+
//! let item = Irc::<_, ()>::from(boxed_item);
43+
//! thread::spawn(move || {
44+
//! thread::sleep(Duration::from_secs(1));
45+
//! item.is_done.store(true, Ordering::SeqCst);
46+
//! drop(item);
47+
//! });
48+
//! let item: Box<MyItem> = done_rx.recv().unwrap();
49+
//! assert!(item.is_done.load(Ordering::SeqCst));
50+
//! ```
51+
152
use crate::{Pointer, SmartPointer};
253
use atomic_traits::{
354
Atomic, NumOps,
@@ -78,6 +129,37 @@ impl<T: IrcItem<Tag>, Tag> Irc<T, Tag> {
78129
///
79130
/// it's possible to return false when counter drop to 1,
80131
/// Because of using Acquire load and Release on drop.
132+
///
133+
/// # Example
134+
///
135+
///
136+
/// ```rust
137+
/// use embed_collections::irc::{Irc, IrcItem};
138+
/// use core::sync::atomic::AtomicUsize;
139+
///
140+
/// struct Tag;
141+
///
142+
/// struct MyItem {
143+
/// value: i32,
144+
/// counter: AtomicUsize,
145+
/// }
146+
///
147+
/// unsafe impl IrcItem<Tag> for MyItem {
148+
/// type Counter = AtomicUsize;
149+
/// fn counter(&self) -> &Self::Counter {
150+
/// &self.counter
151+
/// }
152+
/// }
153+
///
154+
/// // Create a new Irc
155+
/// let irc1 = Irc::<_, Tag>::new(MyItem { value: 10, counter: AtomicUsize::new(0) });
156+
/// assert_eq!(irc1.value, 10);
157+
/// assert!(irc1.is_unique());
158+
///
159+
/// // Clone the Irc
160+
/// let irc2 = irc1.clone();
161+
/// assert_eq!(irc1.strong_count(), 2);
162+
/// assert!(!irc1.is_unique());
81163
#[inline]
82164
pub fn is_unique(&self) -> bool {
83165
// Safety:
@@ -99,6 +181,42 @@ impl<T: IrcItem<Tag>, Tag> Irc<T, Tag> {
99181

100182
impl<T: IrcItem<Tag> + Clone, Tag> Irc<T, Tag> {
101183
/// The Cow function, the same as `Arc::make_mut()`
184+
///
185+
/// # Example
186+
///
187+
/// ```rust
188+
/// use embed_collections::irc::{Irc, IrcItem};
189+
/// use core::sync::atomic::AtomicUsize;
190+
///
191+
/// struct Tag;
192+
/// struct MyItem {
193+
/// value: i32,
194+
/// counter: AtomicUsize,
195+
/// }
196+
///
197+
/// impl Clone for MyItem {
198+
/// fn clone(&self) -> Self {
199+
/// Self { value: self.value, counter: AtomicUsize::new(0) }
200+
/// }
201+
/// }
202+
///
203+
/// unsafe impl IrcItem<Tag> for MyItem {
204+
/// type Counter = AtomicUsize;
205+
/// fn counter(&self) -> &Self::Counter {
206+
/// &self.counter
207+
/// }
208+
/// }
209+
///
210+
/// let mut irc1 = Irc::<_, Tag>::new(MyItem { value: 10, counter: AtomicUsize::new(0) });
211+
/// let irc2 = irc1.clone();
212+
///
213+
/// // This will clone the inner item because it's shared
214+
/// let m = Irc::make_mut(&mut irc1);
215+
/// m.value = 20;
216+
///
217+
/// assert_eq!(irc1.value, 20);
218+
/// assert_eq!(irc2.value, 10);
219+
/// ```
102220
#[inline]
103221
pub fn make_mut(this: &mut Self) -> &mut T {
104222
if !this.is_unique() {
@@ -211,3 +329,166 @@ impl<T: IrcItem<Tag>, Tag> SmartPointer for Irc<T, Tag> {
211329
Irc::new(inner)
212330
}
213331
}
332+
333+
#[cfg(test)]
334+
mod tests {
335+
use super::*;
336+
use crate::test::{CounterI32, alive_count, reset_alive_count};
337+
use core::sync::atomic::AtomicUsize;
338+
use std::thread;
339+
340+
struct Tag;
341+
342+
struct TestItem {
343+
value: CounterI32,
344+
counter: AtomicUsize,
345+
}
346+
347+
impl TestItem {
348+
fn new(val: i32) -> Self {
349+
Self { value: CounterI32::new(val), counter: AtomicUsize::new(0) }
350+
}
351+
}
352+
353+
impl Clone for TestItem {
354+
fn clone(&self) -> Self {
355+
Self { value: self.value.clone(), counter: AtomicUsize::new(0) }
356+
}
357+
}
358+
359+
unsafe impl IrcItem<Tag> for TestItem {
360+
type Counter = AtomicUsize;
361+
fn counter(&self) -> &Self::Counter {
362+
&self.counter
363+
}
364+
}
365+
366+
#[test]
367+
fn test_basic() {
368+
reset_alive_count();
369+
{
370+
let item = TestItem::new(10);
371+
let irc1 = Irc::<_, Tag>::new(item);
372+
assert_eq!(irc1.value.value, 10);
373+
assert_eq!(irc1.strong_count(), 1);
374+
assert!(irc1.is_unique());
375+
assert_eq!(alive_count(), 1);
376+
377+
let irc2 = irc1.clone();
378+
assert_eq!(irc1.strong_count(), 2);
379+
assert_eq!(irc2.strong_count(), 2);
380+
assert!(!irc1.is_unique());
381+
assert_eq!(alive_count(), 1);
382+
383+
drop(irc1);
384+
assert_eq!(irc2.strong_count(), 1);
385+
assert!(irc2.is_unique());
386+
assert_eq!(alive_count(), 1);
387+
}
388+
assert_eq!(alive_count(), 0);
389+
}
390+
391+
#[test]
392+
fn test_get_mut() {
393+
reset_alive_count();
394+
let mut irc = Irc::<_, Tag>::new(TestItem::new(10));
395+
assert!(Irc::get_mut(&mut irc).is_some());
396+
397+
let _irc2 = irc.clone();
398+
assert!(Irc::get_mut(&mut irc).is_none());
399+
}
400+
401+
#[test]
402+
fn test_make_mut() {
403+
reset_alive_count();
404+
let mut irc = Irc::<_, Tag>::new(TestItem::new(10));
405+
406+
// Unique, no clone
407+
{
408+
let m = Irc::make_mut(&mut irc);
409+
m.value.value = 20;
410+
}
411+
assert_eq!(irc.value.value, 20);
412+
assert_eq!(alive_count(), 1);
413+
414+
// Not unique, should clone
415+
let irc2 = irc.clone();
416+
assert_eq!(alive_count(), 1);
417+
{
418+
let m = Irc::make_mut(&mut irc);
419+
m.value.value = 30;
420+
}
421+
assert_eq!(irc.value.value, 30);
422+
assert_eq!(irc2.value.value, 20);
423+
assert_eq!(alive_count(), 2);
424+
425+
assert!(irc.is_unique());
426+
assert!(irc2.is_unique());
427+
}
428+
429+
#[test]
430+
fn test_multithread_count() {
431+
reset_alive_count();
432+
{
433+
let irc = Irc::<_, Tag>::new(TestItem::new(0));
434+
let mut handles = vec![];
435+
436+
for _ in 0..10 {
437+
let irc_clone = irc.clone();
438+
handles.push(thread::spawn(move || {
439+
for _ in 0..1000 {
440+
let temp = irc_clone.clone();
441+
assert_eq!(temp.value.value, 0);
442+
}
443+
}));
444+
}
445+
446+
for handle in handles {
447+
handle.join().unwrap();
448+
}
449+
450+
assert_eq!(irc.strong_count(), 1);
451+
assert!(irc.is_unique());
452+
assert_eq!(alive_count(), 1);
453+
}
454+
assert_eq!(alive_count(), 0);
455+
}
456+
457+
#[test]
458+
fn test_multithread_drop() {
459+
reset_alive_count();
460+
{
461+
let irc = Irc::<_, Tag>::new(TestItem::new(0));
462+
let mut handles = vec![];
463+
for _ in 0..10 {
464+
let irc_clone = irc.clone();
465+
handles.push(thread::spawn(move || {
466+
for _ in 0..1000 {
467+
let temp = irc_clone.clone();
468+
assert_eq!(temp.value.value, 0);
469+
}
470+
}));
471+
}
472+
drop(irc);
473+
for handle in handles {
474+
handle.join().unwrap();
475+
}
476+
}
477+
assert_eq!(alive_count(), 0);
478+
}
479+
480+
#[test]
481+
fn test_drop_all() {
482+
reset_alive_count();
483+
let irc = Irc::<_, Tag>::new(TestItem::new(0));
484+
let mut clones = vec![];
485+
for _ in 0..100 {
486+
clones.push(irc.clone());
487+
}
488+
assert_eq!(alive_count(), 1);
489+
drop(clones);
490+
assert_eq!(alive_count(), 1);
491+
drop(irc);
492+
assert_eq!(alive_count(), 0);
493+
}
494+
}

0 commit comments

Comments
 (0)