Skip to content

Commit 26f16e8

Browse files
committed
Add a Once type for values which are only written once
1 parent 60d0cbe commit 26f16e8

File tree

1 file changed

+129
-0
lines changed
  • src/librustc_data_structures

1 file changed

+129
-0
lines changed

src/librustc_data_structures/sync.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use std::collections::HashMap;
3333
use std::hash::{Hash, BuildHasher};
3434
use std::cmp::Ordering;
35+
use std::marker::PhantomData;
3536
use std::fmt::Debug;
3637
use std::fmt::Formatter;
3738
use std::fmt;
@@ -241,6 +242,134 @@ impl<K: Eq + Hash, V: Eq, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S>
241242
}
242243
}
243244

245+
/// A type whose inner value can be written once and then will stay read-only
246+
// This contains a PhantomData<T> since this type conceptually owns a T outside the Mutex once
247+
// initialized. This ensures that Once<T> is Sync only if T is. If we did not have PhantomData<T>
248+
// we could send a &Once<Cell<bool>> to multiple threads and call `get` on it to get access
249+
// to &Cell<bool> on those threads.
250+
pub struct Once<T>(Lock<Option<T>>, PhantomData<T>);
251+
252+
impl<T> Once<T> {
253+
/// Creates an Once value which is uninitialized
254+
#[inline(always)]
255+
pub fn new() -> Self {
256+
Once(Lock::new(None), PhantomData)
257+
}
258+
259+
/// Consumes the value and returns Some(T) if it was initialized
260+
#[inline(always)]
261+
pub fn into_inner(self) -> Option<T> {
262+
self.0.into_inner()
263+
}
264+
265+
/// Tries to initialize the inner value to `value`.
266+
/// Returns `None` if the inner value was uninitialized and `value` was consumed setting it
267+
/// otherwise if the inner value was already set it returns `value` back to the caller
268+
#[inline]
269+
pub fn try_set(&self, value: T) -> Option<T> {
270+
let mut lock = self.0.lock();
271+
if lock.is_some() {
272+
return Some(value);
273+
}
274+
*lock = Some(value);
275+
None
276+
}
277+
278+
/// Tries to initialize the inner value to `value`.
279+
/// Returns `None` if the inner value was uninitialized and `value` was consumed setting it
280+
/// otherwise if the inner value was already set it asserts that `value` is equal to the inner
281+
/// value and then returns `value` back to the caller
282+
#[inline]
283+
pub fn try_set_same(&self, value: T) -> Option<T> where T: Eq {
284+
let mut lock = self.0.lock();
285+
if let Some(ref inner) = *lock {
286+
assert!(*inner == value);
287+
return Some(value);
288+
}
289+
*lock = Some(value);
290+
None
291+
}
292+
293+
/// Tries to initialize the inner value to `value` and panics if it was already initialized
294+
#[inline]
295+
pub fn set(&self, value: T) {
296+
assert!(self.try_set(value).is_none());
297+
}
298+
299+
/// Tries to initialize the inner value by calling the closure while ensuring that no-one else
300+
/// can access the value in the mean time by holding a lock for the duration of the closure.
301+
/// If the value was already initialized the closure is not called and `false` is returned,
302+
/// otherwise if the value from the closure initializes the inner value, `true` is returned
303+
#[inline]
304+
pub fn init_locking<F: FnOnce() -> T>(&self, f: F) -> bool {
305+
let mut lock = self.0.lock();
306+
if lock.is_some() {
307+
return false;
308+
}
309+
*lock = Some(f());
310+
true
311+
}
312+
313+
/// Tries to initialize the inner value by calling the closure without ensuring that no-one
314+
/// else can access it. This mean when this is called from multiple threads, multiple
315+
/// closures may concurrently be computing a value which the inner value should take.
316+
/// Only one of these closures are used to actually initialize the value.
317+
/// If some other closure already set the value,
318+
/// we return the value our closure computed wrapped in a `Option`.
319+
/// If our closure set the value, `None` is returned.
320+
/// If the value is already initialized, the closure is not called and `None` is returned.
321+
#[inline]
322+
pub fn init_nonlocking<F: FnOnce() -> T>(&self, f: F) -> Option<T> {
323+
if self.0.lock().is_some() {
324+
None
325+
} else {
326+
self.try_set(f())
327+
}
328+
}
329+
330+
/// Tries to initialize the inner value by calling the closure without ensuring that no-one
331+
/// else can access it. This mean when this is called from multiple threads, multiple
332+
/// closures may concurrently be computing a value which the inner value should take.
333+
/// Only one of these closures are used to actually initialize the value.
334+
/// If some other closure already set the value, we assert that it our closure computed
335+
/// a value equal to the value aready set and then
336+
/// we return the value our closure computed wrapped in a `Option`.
337+
/// If our closure set the value, `None` is returned.
338+
/// If the value is already initialized, the closure is not called and `None` is returned.
339+
#[inline]
340+
pub fn init_nonlocking_same<F: FnOnce() -> T>(&self, f: F) -> Option<T> where T: Eq {
341+
if self.0.lock().is_some() {
342+
None
343+
} else {
344+
self.try_set_same(f())
345+
}
346+
}
347+
348+
/// Tries to get a reference to the inner value, returns `None` if it is not yet initialized
349+
#[inline(always)]
350+
pub fn try_get(&self) -> Option<&T> {
351+
let lock = &*self.0.lock();
352+
if let Some(ref inner) = *lock {
353+
// This is safe since we won't mutate the inner value
354+
unsafe { Some(&*(inner as *const T)) }
355+
} else {
356+
None
357+
}
358+
}
359+
360+
/// Gets reference to the inner value, panics if it is not yet initialized
361+
#[inline(always)]
362+
pub fn get(&self) -> &T {
363+
self.try_get().expect("value was not set")
364+
}
365+
366+
/// Gets reference to the inner value, panics if it is not yet initialized
367+
#[inline(always)]
368+
pub fn borrow(&self) -> &T {
369+
self.get()
370+
}
371+
}
372+
244373
impl<T: Copy + Debug> Debug for LockCell<T> {
245374
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
246375
f.debug_struct("LockCell")

0 commit comments

Comments
 (0)