]> git.lizzy.rs Git - rust.git/blob - src/libstd/sync/once.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / libstd / sync / once.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! A "once initialization" primitive
12 //!
13 //! This primitive is meant to be used to run one-time initialization. An
14 //! example use case would be for initializing an FFI library.
15
16 use int;
17 use kinds::Sync;
18 use mem::drop;
19 use ops::FnOnce;
20 use sync::atomic::{AtomicInt, Ordering, ATOMIC_INT_INIT};
21 use sync::{StaticMutex, MUTEX_INIT};
22
23 /// A synchronization primitive which can be used to run a one-time global
24 /// initialization. Useful for one-time initialization for FFI or related
25 /// functionality. This type can only be constructed with the `ONCE_INIT`
26 /// value.
27 ///
28 /// # Example
29 ///
30 /// ```rust
31 /// use std::sync::{Once, ONCE_INIT};
32 ///
33 /// static START: Once = ONCE_INIT;
34 ///
35 /// START.call_once(|| {
36 ///     // run initialization here
37 /// });
38 /// ```
39 #[stable]
40 pub struct Once {
41     mutex: StaticMutex,
42     cnt: AtomicInt,
43     lock_cnt: AtomicInt,
44 }
45
46 unsafe impl Sync for Once {}
47
48 /// Initialization value for static `Once` values.
49 #[stable]
50 pub const ONCE_INIT: Once = Once {
51     mutex: MUTEX_INIT,
52     cnt: ATOMIC_INT_INIT,
53     lock_cnt: ATOMIC_INT_INIT,
54 };
55
56 impl Once {
57     /// Perform an initialization routine once and only once. The given closure
58     /// will be executed if this is the first time `call_once` has been called,
59     /// and otherwise the routine will *not* be invoked.
60     ///
61     /// This method will block the calling task if another initialization
62     /// routine is currently running.
63     ///
64     /// When this function returns, it is guaranteed that some initialization
65     /// has run and completed (it may not be the closure specified).
66     #[stable]
67     pub fn call_once<F>(&'static self, f: F) where F: FnOnce() {
68         // Optimize common path: load is much cheaper than fetch_add.
69         if self.cnt.load(Ordering::SeqCst) < 0 {
70             return
71         }
72
73         // Implementation-wise, this would seem like a fairly trivial primitive.
74         // The stickler part is where our mutexes currently require an
75         // allocation, and usage of a `Once` shouldn't leak this allocation.
76         //
77         // This means that there must be a deterministic destroyer of the mutex
78         // contained within (because it's not needed after the initialization
79         // has run).
80         //
81         // The general scheme here is to gate all future threads once
82         // initialization has completed with a "very negative" count, and to
83         // allow through threads to lock the mutex if they see a non negative
84         // count. For all threads grabbing the mutex, exactly one of them should
85         // be responsible for unlocking the mutex, and this should only be done
86         // once everyone else is done with the mutex.
87         //
88         // This atomicity is achieved by swapping a very negative value into the
89         // shared count when the initialization routine has completed. This will
90         // read the number of threads which will at some point attempt to
91         // acquire the mutex. This count is then squirreled away in a separate
92         // variable, and the last person on the way out of the mutex is then
93         // responsible for destroying the mutex.
94         //
95         // It is crucial that the negative value is swapped in *after* the
96         // initialization routine has completed because otherwise new threads
97         // calling `call_once` will return immediately before the initialization
98         // has completed.
99
100         let prev = self.cnt.fetch_add(1, Ordering::SeqCst);
101         if prev < 0 {
102             // Make sure we never overflow, we'll never have int::MIN
103             // simultaneous calls to `call_once` to make this value go back to 0
104             self.cnt.store(int::MIN, Ordering::SeqCst);
105             return
106         }
107
108         // If the count is negative, then someone else finished the job,
109         // otherwise we run the job and record how many people will try to grab
110         // this lock
111         let guard = self.mutex.lock();
112         if self.cnt.load(Ordering::SeqCst) > 0 {
113             f();
114             let prev = self.cnt.swap(int::MIN, Ordering::SeqCst);
115             self.lock_cnt.store(prev, Ordering::SeqCst);
116         }
117         drop(guard);
118
119         // Last one out cleans up after everyone else, no leaks!
120         if self.lock_cnt.fetch_add(-1, Ordering::SeqCst) == 1 {
121             unsafe { self.mutex.destroy() }
122         }
123     }
124
125     /// Deprecated
126     #[deprecated = "renamed to `call_once`"]
127     pub fn doit<F>(&'static self, f: F) where F: FnOnce() { self.call_once(f) }
128 }
129
130 #[cfg(test)]
131 mod test {
132     use prelude::v1::*;
133
134     use thread::Thread;
135     use super::{ONCE_INIT, Once};
136     use sync::mpsc::channel;
137
138     #[test]
139     fn smoke_once() {
140         static O: Once = ONCE_INIT;
141         let mut a = 0i;
142         O.call_once(|| a += 1);
143         assert_eq!(a, 1);
144         O.call_once(|| a += 1);
145         assert_eq!(a, 1);
146     }
147
148     #[test]
149     fn stampede_once() {
150         static O: Once = ONCE_INIT;
151         static mut run: bool = false;
152
153         let (tx, rx) = channel();
154         for _ in range(0u, 10) {
155             let tx = tx.clone();
156             Thread::spawn(move|| {
157                 for _ in range(0u, 4) { Thread::yield_now() }
158                 unsafe {
159                     O.call_once(|| {
160                         assert!(!run);
161                         run = true;
162                     });
163                     assert!(run);
164                 }
165                 tx.send(()).unwrap();
166             }).detach();
167         }
168
169         unsafe {
170             O.call_once(|| {
171                 assert!(!run);
172                 run = true;
173             });
174             assert!(run);
175         }
176
177         for _ in range(0u, 10) {
178             rx.recv().unwrap();
179         }
180     }
181 }