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}