object_controller_tracker/lib.rs
1#![deny(missing_docs)]
2
3//! A crate used to keep track of `Object-Controller` pairs, where a
4//! `Controller` is used to operate on the `Object` (e.g. to cancel it),
5//! usually from an other thread.
6//!
7//! A [Tracker](struct.Tracker.html) object is constructed and used to
8//! register `Object-Controller` pairs. When an `Object` is dropped, the
9//! `Controller` is automatically unregistered. The
10//! [Tracker](struct.Tracker.html) object can be used to operate on
11//! all registered objects through their associated controllers.
12//!
13//!
14//! # Example
15//!
16//! ```no_run
17//! # extern crate object_controller_tracker;
18//! # use std::sync::atomic::{AtomicBool, Ordering};
19//! # use std::sync::Arc;
20//! # use std::time::Duration;
21//! # use object_controller_tracker::*;
22//!
23//! // The object we want to track...
24//! struct Object {
25//! stop: Arc<AtomicBool>,
26//! //...
27//! }
28//!
29//! // ... and its associated controller.
30//! struct Controller(Arc<AtomicBool>);
31//!
32//! impl Object {
33//!
34//! // Create an Object-Controller pair.
35//! fn new2() -> (Object, Controller) {
36//! let stop = Arc::new(AtomicBool::new(false));
37//! let ctl_stop = Arc::clone(&stop);
38//! (Object { stop }, Controller(ctl_stop))
39//! }
40//!
41//! // Some method on the object that can be cancelled through the Controller.
42//! fn do_something(&self) {
43//! while !self.stop.load(Ordering::SeqCst) {
44//! println!("Do something.");
45//! std::thread::sleep(Duration::from_secs(1));
46//! }
47//! }
48//! }
49//!
50//! impl Controller {
51//! // Cancel an operation on the Object.
52//! fn cancel(&self) {
53//! self.0.store(true, Ordering::SeqCst);
54//! }
55//! }
56//!
57//! fn main() {
58//! // Create the tracker object.
59//! let mut tracker = Tracker::new();
60//!
61//! let tracker2 = tracker.clone();
62//! let thread = std::thread::spawn(move || {
63//! // Create an Object-Controller pair in some thread.
64//! let (object, controller) = Object::new2();
65//!
66//! // Register it with the tracker.
67//! let object = tracker2.track(object, controller);
68//!
69//! // Do some work with the Object.
70//! object.do_something();
71//! });
72//!
73//! std::thread::sleep(Duration::from_secs(5));
74//!
75//! // Cancel all registered Object operations.
76//! tracker.for_each(|r| r.cancel());
77//!
78//! thread.join().unwrap();
79//! }
80//! ```
81
82use std::collections::HashMap;
83use std::ops::{Deref, DerefMut};
84use std::sync::{Arc, Mutex, MutexGuard};
85
86type Id = u32;
87
88struct InnerTracker<C> {
89 controllers: HashMap<Id, C>,
90 next_id: Id,
91}
92
93/// An object used to keep track of controller parts of object-controller pairs.
94pub struct Tracker<C>(Arc<Mutex<InnerTracker<C>>>);
95
96/// Wrapper for the object part of tracked object-controller pair.
97///
98/// When this object is dropped, the associated controller is unregistered.
99pub struct Tracked<T, C> {
100 tracker: Tracker<C>,
101 id: Id,
102 object: T,
103}
104
105/// An RAII implementation of a Tracker lock. When this structure goes out
106/// of scope, the lock is released.
107///
108/// This structure is created by the [lock](struct.Tracker.html#method.lock) method
109/// on [Tracker](struct.Tracker.html).
110pub struct TrackerGuard<'a, C>(MutexGuard<'a, InnerTracker<C>>);
111
112/// An iterator over the tracked controllers. Controllers are visited in an
113/// unspecified order.
114pub struct Iter<'a, C>(std::collections::hash_map::Values<'a, Id, C>);
115
116impl<C> InnerTracker<C> {
117 fn register(&mut self, controller: C) -> Id {
118 let id = self.next_id;
119 self.next_id += 1;
120 self.controllers.insert(id, controller);
121 id
122 }
123
124 fn unregister(&mut self, id: Id) {
125 self.controllers.remove(&id);
126 }
127}
128
129impl<C> Tracker<C> {
130 /// Create a new tracker.
131 pub fn new() -> Tracker<C> {
132 Tracker(Arc::new(Mutex::new(InnerTracker {
133 controllers: HashMap::new(),
134 next_id: 0,
135 })))
136 }
137
138 /// Register an object-controller pair.
139 ///
140 /// The controller part is kept in the [Tracker](struct.Tracker.html),
141 /// and the object part is wrapped in a [Tracked](struct.Tracked.html).
142 /// When the [Tracked](struct.Tracked.html) object is dropped, the
143 /// controller is dropped too.
144 pub fn track<T>(&self, object: T, controller: C) -> Tracked<T, C> {
145 let mut tracker = self.0.lock().unwrap();
146 let id = tracker.register(controller);
147 Tracked {
148 tracker: Tracker(self.0.clone()),
149 id,
150 object,
151 }
152 }
153
154 /// Register an object-contoller pair.
155 ///
156 /// Same as [track](struct.Tracker.html#method.track)(pair.0, pair.1).
157 pub fn track_pair<T>(&self, pair: (T, C)) -> Tracked<T, C> {
158 self.track(pair.0, pair.1)
159 }
160
161 /// Lock the tracker so that one can iterate over its controllers.
162 pub fn lock(&mut self) -> TrackerGuard<C> {
163 TrackerGuard(self.0.lock().unwrap())
164 }
165
166 /// Call a closure on each tracked controller. The closure must not
167 /// register a new controller in this tracker, or drop a tracked
168 /// object. The controllers are visited in an unspecified order.
169 pub fn for_each<F: FnMut(&C)>(&mut self, f: F) {
170 self.lock().iter().for_each(f);
171 }
172}
173
174impl<C> Clone for Tracker<C> {
175 fn clone(&self) -> Tracker<C> {
176 Tracker(Arc::clone(&self.0))
177 }
178}
179
180impl<C> Default for Tracker<C> {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186impl<T, C> Drop for Tracked<T, C> {
187 fn drop(&mut self) {
188 let mut tracker = self.tracker.0.lock().unwrap();
189 tracker.unregister(self.id);
190 }
191}
192
193impl<T, C> Deref for Tracked<T, C> {
194 type Target = T;
195
196 fn deref(&self) -> &T {
197 &self.object
198 }
199}
200
201impl<T, C> DerefMut for Tracked<T, C> {
202 fn deref_mut(&mut self) -> &mut T {
203 &mut self.object
204 }
205}
206
207impl<'a, C> TrackerGuard<'a, C> {
208 /// Create an iterator over tracked controllers.
209 pub fn iter(&'a self) -> Iter<'a, C> {
210 Iter(self.0.controllers.values())
211 }
212}
213
214impl<'a, C> Iterator for Iter<'a, C> {
215 type Item = &'a C;
216
217 fn next(&mut self) -> Option<Self::Item> {
218 self.0.next()
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn test1() {
228 let mut tracker = Tracker::new();
229 let obj = tracker.track((), 5);
230 tracker.lock().iter().for_each(|c| {
231 assert_eq!(c, &5);
232 });
233 assert_eq!(tracker.lock().iter().count(), 1);
234 drop(obj);
235 assert_eq!(tracker.lock().iter().count(), 0);
236 }
237
238 #[test]
239 fn test2() {
240 let mut tracker = Tracker::new();
241 let obj1 = tracker.track((), 1);
242 let obj2 = tracker.track((), 2);
243 let obj3 = tracker.track((), 3);
244 assert_eq!(tracker.lock().iter().count(), 3);
245 let mut sum = 0;
246 tracker.for_each(|x| {
247 sum += x;
248 });
249 assert_eq!(sum, 6);
250 drop(obj2);
251 sum = 0;
252 tracker.for_each(|x| {
253 sum += x;
254 });
255 assert_eq!(sum, 4);
256 assert_eq!(tracker.lock().iter().count(), 2);
257 drop(obj3);
258 drop(obj1);
259 sum = 0;
260 tracker.for_each(|x| {
261 sum += x;
262 });
263 assert_eq!(sum, 0);
264 }
265}