1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Dropout allow to send an object in a background thread to be dropped there.
//!
//! Dropout is inspired by [defer-drop](https://docs.rs/defer-drop) and (as defer-drop itself) by [https://abramov.io/rust-dropping-things-in-another-thread](https://abramov.io/rust-dropping-things-in-another-thread)
//!
//! See [`Dropper`] for details.
use Arc;
/// Dropper can send object to a background thread to be dropped there.
/// Useful when the object takes a long time to drop and you don't want your (main) thread
/// to be blocked while you drop it.
///
/// # Notes:
///
/// There is one dropper thread per `Dropper`. Dropped values are enqueued in an
/// unbounded channel to be consumed by this thread; if you send more
/// value than the thread can handle, this will cause unbounded memory
/// consumption. There is currently no way for the thread to signal or block
/// if it is overwhelmed.
///
/// The objects are guaranteed to be destructed in the order received through a
/// channel, which means that objects sent from a single thread will be
/// destructed in order. However, there is no guarantee about the ordering of
/// interleaved values from different threads.
/// Value send to be dropped are guaranted to be dropped at a moment as `Dropper` itself
/// wait for all values to be dropped when it is been dropped.
///
/// # Example
///
/// ```
/// # use std::time::{Instant, Duration};
/// # use dropout::Dropper;
/// # use std::collections::HashMap;
///
///
/// # type HeavyObject = HashMap<usize, Vec<usize>>;
///
/// # const NUM_ELEMENTS: usize = 1000000;
/// # fn make_heavy_object() -> HeavyObject {
/// # (1..=NUM_ELEMENTS).map(|v| (v, vec![v])).collect()
/// # }
///
/// let first_heavy_object = make_heavy_object();
/// let second_heavy_object = first_heavy_object.clone();
///
/// fn timer(f: impl FnOnce()) -> Duration {
/// let start = Instant::now();
/// f();
/// Instant::now() - start
/// }
///
/// let dropper = Dropper::new();
///
/// // This is a special case for this small test.
/// // The closure will explictly drop `first_heavy_object` but also implicitly drop the
/// // `dropper` and it will wait for background thread to finish.
/// // We don't want to mesure the background drop as it is what we want to avoid.
/// // By cloning the dropper, background thread is waited at end of main, not at end of closure.
/// let dropper_clone = dropper.clone();
/// let dropout_time = timer(move || dropper_clone.dropout(first_heavy_object));
/// let std_time = timer(move || drop(second_heavy_object));
///
/// assert!(dropout_time < std_time);
/// ```
;