1
+ //! Atomic References
2
+ //!
3
+ //! These types act similarially to the Atomic types from std::sync::atomic,
4
+ //! Except that instead of containing an integer type or a pointer, they contain
5
+ //! an `Option<&'a T>` value.
6
+ //!
7
+ //! Like other option values, these types present operations which, when used
8
+ //! correctly, synchronize updates between threads. This type is a form of
9
+ //! interior mutability, like `Cell<T>`, `RefCell<T>`, or `Mutex<T>`.
10
+ //!
11
+ //! To store an atomic reference in a static variable, a the macro
12
+ //! `static_atomic_ref!` must be used. A static initializer like
13
+ //! `ATOMIC_REF_INIT` is not possible due to the need to be generic over any
14
+ //! reference target type.
15
+ //!
16
+ //! This type in static position is often used for lazy global initialization.
17
+ //!
18
+ //! `AtomicRef` may only contain `Sized` types, as unsized types have wide
19
+ //! pointers which cannot be atomically written to or read from.
20
+ //!
21
+ //!
22
+ //! # Examples
23
+ //!
24
+ //! Static logger state
25
+ //!
26
+ //! ```
27
+ //! #[macro_use]
28
+ //! extern crate atomic_ref;
29
+ //! use atomic_ref::AtomicRef;
30
+ //! use std::sync::atomic::Ordering;
31
+ //! use std::io::{stdout, Write};
32
+ //!
33
+ //! // Define the idea of a logger
34
+ //! trait Logger {
35
+ //! fn log(&self, msg: &str) {}
36
+ //! }
37
+ //! struct LoggerInfo {
38
+ //! logger: &'static (Logger + Sync)
39
+ //! }
40
+ //!
41
+ //! // The methods for working with our currently defined static logger
42
+ //! static_atomic_ref! {
43
+ //! static LOGGER: AtomicRef<LoggerInfo>;
44
+ //! }
45
+ //! fn log(msg: &str) -> bool {
46
+ //! if let Some(info) = LOGGER.load(Ordering::SeqCst) {
47
+ //! info.logger.log(msg);
48
+ //! true
49
+ //! } else {
50
+ //! false
51
+ //! }
52
+ //! }
53
+ //! fn set_logger(logger: Option<&'static LoggerInfo>) {
54
+ //! LOGGER.store(logger, Ordering::SeqCst);
55
+ //! }
56
+ //!
57
+ //! // Defining the standard out example logger
58
+ //! struct StdoutLogger;
59
+ //! impl Logger for StdoutLogger {
60
+ //! fn log(&self, msg: &str) {
61
+ //! stdout().write(msg.as_bytes());
62
+ //! }
63
+ //! }
64
+ //! static STDOUT_LOGGER: LoggerInfo = LoggerInfo { logger: &StdoutLogger };
65
+ //!
66
+ //! fn main() {
67
+ //! let res = log("This will fail");
68
+ //! assert!(!res);
69
+ //! set_logger(Some(&STDOUT_LOGGER));
70
+ //! let res = log("This will succeed");
71
+ //! assert!(res);
72
+ //! }
73
+ //! ```
74
+
1
75
use std:: sync:: atomic:: { AtomicUsize , ATOMIC_USIZE_INIT , Ordering } ;
2
76
use std:: marker:: PhantomData ;
3
77
use std:: fmt;
4
78
use std:: default:: Default ;
5
79
80
+ /// A mutable Option<&'a, T> type which can be safely shared between threads.
6
81
#[ repr( C ) ]
7
82
pub struct AtomicRef < ' a , T : ' a > {
8
83
data : AtomicUsize ,
9
84
_marker : PhantomData < & ' a T > ,
10
85
}
11
86
12
- /// We cannot have an ATOMIC_REF_INIT method, as we can't make a const which is
13
- /// templated over a type
87
+ /// You will probably never need to use this type. It exists mostly for internal
88
+ /// use in the `static_atomic_ref!` macro.
89
+ ///
90
+ /// Unlike `AtomicUsize` and its ilk, we cannot have an `ATOMIC_REF_INIT` const
91
+ /// which is initialized to `None`, as constants cannot be generic over a type
92
+ /// parameter. This is the same reason why `AtomicPtr` does not have an
93
+ /// `ATOMIC_PTR_INIT` const.
14
94
///
15
- /// Instead, we define it for a specific type (in this case &'static u8), and
16
- /// define a macro static_atomic_ref! which uses unsafe code to allow the
17
- /// creation of other types based on this layout.
95
+ /// Instead, we have a single const for `&'static u8`, and take advantage of the
96
+ /// fact that all AtomicRef types have identical layout to implement the
97
+ /// `static_atomic_ref!` macro.
98
+ ///
99
+ /// Please use `static_atomic_ref!` instead of this constant if you need to
100
+ /// implement a static atomic reference variable.
18
101
pub const ATOMIC_U8_REF_INIT : AtomicRef < ' static , u8 > = AtomicRef {
19
102
data : ATOMIC_USIZE_INIT ,
20
103
_marker : PhantomData ,
21
104
} ;
22
105
106
+ /// A macro to define a statically allocated `AtomicRef<'static, T>` which is
107
+ /// initialized to `None`.
108
+ ///
109
+ /// # Examples
110
+ ///
111
+ /// ```
112
+ /// # #[macro_use]
113
+ /// # extern crate atomic_ref;
114
+ /// use std::sync::atomic::Ordering;
115
+ ///
116
+ /// static_atomic_ref! {
117
+ /// static SOME_REFERENCE: AtomicRef<i32>;
118
+ /// pub static PUB_REFERENCE: AtomicRef<u64>;
119
+ /// }
120
+ ///
121
+ /// fn main() {
122
+ /// let a: Option<&'static i32> = SOME_REFERENCE.load(Ordering::SeqCst);
123
+ /// assert_eq!(a, None);
124
+ /// }
125
+ /// ```
23
126
#[ macro_export]
24
127
macro_rules! static_atomic_ref {
25
128
( $( #[ $attr: meta] ) * static $N: ident : AtomicRef <$T: ty>; $( $t: tt) * ) => {
@@ -60,48 +163,171 @@ macro_rules! static_atomic_ref {
60
163
( ) => ( ) ;
61
164
}
62
165
166
+ /// An internal helper function for converting `Option<&'a T>` values to usize
167
+ /// for storing in the `AtomicUsize`.
63
168
fn from_opt < ' a , T > ( p : Option < & ' a T > ) -> usize {
64
169
match p {
65
170
Some ( p) => p as * const T as usize ,
66
171
None => 0 ,
67
172
}
68
173
}
69
174
175
+ /// An internal helper function for converting `usize` values stored in the
176
+ /// `AtomicUsize` back into `Option<&'a T>` values.
70
177
unsafe fn to_opt < ' a , T > ( p : usize ) -> Option < & ' a T > {
71
178
( p as * const T ) . as_ref ( )
72
179
}
73
180
74
181
impl < ' a , T > AtomicRef < ' a , T > {
182
+ /// Creates a new `AtomicRef`.
183
+ ///
184
+ /// # Examples
185
+ ///
186
+ /// ```
187
+ /// use atomic_ref::AtomicRef;
188
+ ///
189
+ /// static VALUE: i32 = 10;
190
+ /// let atomic_ref = AtomicRef::new(Some(&VALUE));
191
+ /// ```
75
192
pub fn new ( p : Option < & ' a T > ) -> AtomicRef < ' a , T > {
76
193
AtomicRef {
77
194
data : AtomicUsize :: new ( from_opt ( p) ) ,
78
195
_marker : PhantomData ,
79
196
}
80
197
}
81
198
199
+ /// Loads the value stored in the `AtomicRef`.
200
+ ///
201
+ /// `load` takes an `Ordering` argument which describes the memory ordering of this operation.
202
+ ///
203
+ /// # Panics
204
+ ///
205
+ /// Panics if `order` is `Release` or `AcqRel`.
206
+ ///
207
+ /// # Examples
208
+ ///
209
+ /// ```
210
+ /// use std::sync::atomic::Ordering;
211
+ /// use atomic_ref::AtomicRef;
212
+ ///
213
+ /// static VALUE: i32 = 10;
214
+ ///
215
+ /// let some_ref = AtomicRef::new(Some(&VALUE));
216
+ /// assert_eq!(some_ref.load(Ordering::Relaxed), Some(&10));
217
+ /// ```
82
218
pub fn load ( & self , ordering : Ordering ) -> Option < & ' a T > {
83
219
unsafe {
84
220
to_opt ( self . data . load ( ordering) )
85
221
}
86
222
}
87
223
224
+ /// Stores a value into the `AtomicRef`.
225
+ ///
226
+ /// `store` takes an `Ordering` argument which describes the memory ordering of this operation.
227
+ ///
228
+ /// # Panics
229
+ ///
230
+ /// Panics if `order` is `Acquire` or `AcqRel`.
231
+ ///
232
+ /// # Examples
233
+ ///
234
+ /// ```
235
+ /// use std::sync::atomic::Ordering;
236
+ /// use atomic_ref::AtomicRef;
237
+ ///
238
+ /// static VALUE: i32 = 10;
239
+ ///
240
+ /// let some_ptr = AtomicRef::new(None);
241
+ /// some_ptr.store(Some(&VALUE), Ordering::Relaxed);
242
+ /// ```
88
243
pub fn store ( & self , ptr : Option < & ' a T > , order : Ordering ) {
89
244
self . data . store ( from_opt ( ptr) , order)
90
245
}
91
246
247
+ /// Stores a value into the `AtomicRef`, returning the old value.
248
+ ///
249
+ /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation.
250
+ ///
251
+ /// # Examples
252
+ ///
253
+ /// ```
254
+ /// use std::sync::atomic::Ordering;
255
+ /// use atomic_ref::AtomicRef;
256
+ ///
257
+ /// static VALUE: i32 = 10;
258
+ /// static OTHER_VALUE: i32 = 20;
259
+ ///
260
+ /// let some_ptr = AtomicRef::new(Some(&VALUE));
261
+ /// let value = some_ptr.swap(Some(&OTHER_VALUE), Ordering::Relaxed);
262
+ /// ```
92
263
pub fn swap ( & self , p : Option < & ' a T > , order : Ordering ) -> Option < & ' a T > {
93
264
unsafe {
94
265
to_opt ( self . data . swap ( from_opt ( p) , order) )
95
266
}
96
267
}
97
268
269
+ /// Stores a value into the `AtomicRef` if the current value is the "same" as
270
+ /// the `current` value.
271
+ ///
272
+ /// The return value is always the previous value. If it the "same" as
273
+ /// `current`, then the value was updated.
274
+ ///
275
+ /// This method considers two `Option<&'a T>`s to be the "same" if they are
276
+ /// both `Some` and have the same pointer value, or if they are both `None`.
277
+ /// This method does not use `Eq` or `PartialEq` for comparison.
278
+ ///
279
+ /// `compare_and_swap` also takes an `Ordering` argument which describes the
280
+ /// memory ordering of this operation.
281
+ ///
282
+ /// # Examples
283
+ ///
284
+ /// ```
285
+ /// use std::sync::atomic::Ordering;
286
+ /// use atomic_ref::AtomicRef;
287
+ ///
288
+ /// static VALUE: i32 = 10;
289
+ /// static OTHER_VALUE: i32 = 20;
290
+ ///
291
+ /// let some_ptr = AtomicRef::new(Some(&VALUE));
292
+ /// let value = some_ptr.compare_and_swap(Some(&OTHER_VALUE), None, Ordering::Relaxed);
293
+ /// ```
98
294
pub fn compare_and_swap ( & self , current : Option < & ' a T > , new : Option < & ' a T > , order : Ordering )
99
295
-> Option < & ' a T > {
100
296
unsafe {
101
297
to_opt ( self . data . compare_and_swap ( from_opt ( current) , from_opt ( new) , order) )
102
298
}
103
299
}
104
300
301
+ /// Stores a value into the `AtomicRef` if the current value is the "same" as
302
+ /// the `current` value.
303
+ ///
304
+ /// The return value is a result indicating whether the new value was
305
+ /// written, and containing the previous value. On success this value is
306
+ /// guaranteed to be the "same" as `new`.
307
+ ///
308
+ /// This method considers two `Option<&'a T>`s to be the "same" if they are
309
+ /// both `Some` and have the same pointer value, or if they are both `None`.
310
+ /// This method does not use `Eq` or `PartialEq` for comparison.
311
+ ///
312
+ /// `compare_exchange` takes two `Ordering` arguments to describe the memory
313
+ /// ordering of this operation. The first describes the required ordering if
314
+ /// the operation succeeds while the second describes the required ordering
315
+ /// when the operation fails. The failure ordering can't be `Release` or
316
+ /// `AcqRel` and must be equivalent or weaker than the success ordering.
317
+ ///
318
+ /// # Examples
319
+ ///
320
+ /// ```
321
+ /// use std::sync::atomic::Ordering;
322
+ /// use atomic_ref::AtomicRef;
323
+ ///
324
+ /// static VALUE: i32 = 10;
325
+ /// static OTHER_VALUE: i32 = 20;
326
+ ///
327
+ /// let some_ptr = AtomicRef::new(Some(&VALUE));
328
+ /// let value = some_ptr.compare_exchange(Some(&OTHER_VALUE), None,
329
+ /// Ordering::SeqCst, Ordering::Relaxed);
330
+ /// ```
105
331
pub fn compare_exchange ( & self , current : Option < & ' a T > , new : Option < & ' a T > ,
106
332
success : Ordering , failure : Ordering )
107
333
-> Result < Option < & ' a T > , Option < & ' a T > > {
@@ -114,6 +340,39 @@ impl<'a, T> AtomicRef<'a, T> {
114
340
}
115
341
}
116
342
343
+ /// Stores a value into the pointer if the current value is the same as the `current` value.
344
+ ///
345
+ /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when the
346
+ /// comparison succeeds, which can result in more efficient code on some platforms. The
347
+ /// return value is a result indicating whether the new value was written and containing the
348
+ /// previous value.
349
+ ///
350
+ /// `compare_exchange_weak` takes two `Ordering` arguments to describe the memory
351
+ /// ordering of this operation. The first describes the required ordering if the operation
352
+ /// succeeds while the second describes the required ordering when the operation fails. The
353
+ /// failure ordering can't be `Release` or `AcqRel` and must be equivalent or weaker than the
354
+ /// success ordering.
355
+ ///
356
+ /// # Examples
357
+ ///
358
+ /// ```
359
+ /// use std::sync::atomic::Ordering;
360
+ /// use atomic_ref::AtomicRef;
361
+ ///
362
+ /// static VALUE: i32 = 10;
363
+ /// static OTHER_VALUE: i32 = 20;
364
+ ///
365
+ /// let some_ptr = AtomicRef::new(Some(&VALUE));
366
+ ///
367
+ /// let mut old = some_ptr.load(Ordering::Relaxed);
368
+ /// loop {
369
+ /// match some_ptr.compare_exchange_weak(old, Some(&VALUE),
370
+ /// Ordering::SeqCst, Ordering::Relaxed) {
371
+ /// Ok(_) => break,
372
+ /// Err(x) => old = x,
373
+ /// }
374
+ /// }
375
+ /// ```
117
376
pub fn compare_exchange_weak ( & self , current : Option < & ' a T > , new : Option < & ' a T > ,
118
377
success : Ordering , failure : Ordering )
119
378
-> Result < Option < & ' a T > , Option < & ' a T > > {
0 commit comments