Skip to content

Commit cc4a0df

Browse files
committed
Add test to check if we get SetError::AlreadyInitError or IsInitializingError
Document each unsafe usage with safety commit and include reasoning
1 parent e5585d2 commit cc4a0df

File tree

3 files changed

+49
-68
lines changed

3 files changed

+49
-68
lines changed

.DS_Store

-10 KB
Binary file not shown.

tokio/src/sync/set_once.rs

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
4242
/// tokio::spawn(async move { second_cl.set(10) });
4343
///
4444
/// let res = arc.get(); // returns None
45-
///
4645
/// arc.wait().await; // lets wait until the value is set
4746
///
4847
/// println!("{:?}", arc.get());
@@ -53,38 +52,21 @@ use std::sync::atomic::{AtomicBool, Ordering};
5352
/// initialized once on first use, but need no further changes. The `SetOnce`
5453
/// in Tokio allows the initialization procedure to be asynchronous.
5554
///
56-
/// # Examples
55+
/// # Example
5756
///
5857
/// ```
59-
/// use tokio::sync::SetOnce;
58+
/// use tokio::sync::{SetOnce, SetError};
6059
///
6160
///
6261
/// static ONCE: SetOnce<u32> = SetOnce::const_new();
6362
///
6463
/// #[tokio::main]
65-
/// async fn main() {
66-
/// let result = ONCE.set(2).await;
67-
/// assert_eq!(*result, 2);
68-
/// }
69-
/// ```
70-
///
71-
/// It is often useful to write a wrapper method for accessing the value.
72-
///
73-
/// ```
74-
/// use tokio::sync::SetOnce;
75-
///
76-
/// static ONCE: SetOnce<u32> = SetOnce::const_new();
77-
///
78-
/// fn get_global_integer() -> &'static u32 {
79-
/// ONCE.set(2);
80-
/// ONCE.get().unwrap()
81-
/// }
64+
/// async fn main() -> Result<(), SetError<u32>> {
65+
/// ONCE.set(2)?;
66+
/// let result = ONCE.get();
67+
/// assert_eq!(result, Some(&2));
8268
///
83-
/// #[tokio::main]
84-
/// async fn main() {
85-
/// let result = get_global_integer();
86-
///
87-
/// assert_eq!(*result, 2);
69+
/// Ok(())
8870
/// }
8971
/// ```
9072
pub struct SetOnce<T> {
@@ -124,11 +106,14 @@ impl<T: Eq> Eq for SetOnce<T> {}
124106
impl<T> Drop for SetOnce<T> {
125107
fn drop(&mut self) {
126108
if self.initialized() {
109+
// SAFETY: We're inside the drop implementation of SetOnce
110+
// AND we're also initalized. This is the best way to ensure
111+
// out data gets dropped
127112
unsafe {
128113
let _ = self.value.with_mut(|ptr| ptr::read(ptr).assume_init());
129114
}
130-
131-
*self.value_set.get_mut() = false;
115+
// no need to set the flag to false as this set once is being
116+
// dropped
132117
}
133118
}
134119
}
@@ -166,20 +151,21 @@ impl<T> SetOnce<T> {
166151
/// # Example
167152
///
168153
/// ```
169-
/// use tokio::sync::SetOnce;
154+
/// use tokio::sync::{SetOnce, SetError};
170155
///
171156
/// static ONCE: SetOnce<u32> = SetOnce::const_new();
172157
///
173-
/// fn get_global_integer() -> &'static u32 {
174-
/// ONCE.set(2);
175-
/// ONCE.get().unwrap();
158+
/// fn get_global_integer() -> Result<Option<&'static u32>, SetError<u32>> {
159+
/// ONCE.set(2)?;
160+
/// Ok(ONCE.get())
176161
/// }
177162
///
178163
/// #[tokio::main]
179-
/// async fn main() {
180-
/// let result = get_global_integer();
164+
/// async fn main() -> Result<(), SetError<u32>> {
165+
/// let result = get_global_integer()?;
181166
///
182-
/// assert_eq!(*result, 2);
167+
/// assert_eq!(result, Some(&2));
168+
/// Ok(())
183169
/// }
184170
/// ```
185171
///
@@ -221,14 +207,15 @@ impl<T> SetOnce<T> {
221207
///
222208
/// static ONCE: SetOnce<u32> = SetOnce::const_new_with(1);
223209
///
224-
/// fn get_global_integer() -> &'static u32 {
225-
/// ONCE.get().unwrap();
210+
/// fn get_global_integer() -> Option<&'static u32> {
211+
/// ONCE.get()
226212
/// }
227213
///
228214
/// #[tokio::main]
229215
/// async fn main() {
230216
/// let result = get_global_integer();
231-
/// assert_eq!(*result, 1);
217+
///
218+
/// assert_eq!(result, Some(&1));
232219
/// }
233220
/// ```
234221
///
@@ -246,8 +233,8 @@ impl<T> SetOnce<T> {
246233
/// Returns `true` if the `SetOnce` currently contains a value, and `false`
247234
/// otherwise.
248235
pub fn initialized(&self) -> bool {
249-
// Using acquire ordering so any threads that read a true from this
250-
// atomic is able to read the value.
236+
// Using acquire ordering so we're able to read/catch any writes that
237+
// are done with `Ordering::Release`
251238
self.value_set.load(Ordering::Acquire)
252239
}
253240

@@ -315,32 +302,26 @@ impl<T> SetOnce<T> {
315302
/// Returns `None` if the cell is empty.
316303
pub fn into_inner(mut self) -> Option<T> {
317304
if self.initialized() {
318-
// SAFETY: The SetOnce is initialized, we can assume the value is
319-
// initialized and return that, when we return the value we give the
320-
// drop handler to someone else and drop is called automatically
321-
//
322-
// We set the value_set as value as we just took the value out
323-
// and the drop implementation will do nothing.
305+
// Since we have taken ownership of self, its drop implementation
306+
// will be called by the end of this function, to prevent a double
307+
// free we will set the value_set to false so that the drop
308+
// implementation does not try to drop the value again.
324309
*self.value_set.get_mut() = false;
310+
311+
// SAFETY: The SetOnce is currently initialized, we can assume the
312+
// value is initialized and return that, when we return the value
313+
// we give the drop handler to the return scope.
325314
Some(unsafe { self.value.with_mut(|ptr| ptr::read(ptr).assume_init()) })
326315
} else {
327316
None
328317
}
329-
// the drop of self is called here again but it doesn't do anything since
330-
// if we dont return early then value is not initalized.
331-
}
332-
333-
/// Takes ownership of the current value, leaving the cell empty. Returns
334-
/// `None` if the cell is empty.
335-
pub fn take(&mut self) -> Option<T> {
336-
std::mem::take(self).into_inner()
337318
}
338319

339320
/// Waits until the `SetOnce` has been initialized. Once the `SetOnce` is
340321
/// initialized the wakers are notified and the Future returned from this
341322
/// function completes.
342323
///
343-
/// If this function is called after the `SetOnce` is initalized then
324+
/// If this function is called after the `SetOnce` is initialized then
344325
/// empty future is returned which completes immediately.
345326
pub async fn wait(&self) {
346327
if !self.initialized() {

tokio/tests/sync_set_once.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ fn from() {
105105
assert_eq!(*cell.get().unwrap(), 2);
106106
}
107107

108-
async fn advance_time_and_set(cell: &'static SetOnce<u32>, v: u32) -> Result<(), SetError<u32>> {
109-
time::advance(Duration::from_millis(1)).await;
110-
cell.set(v)
111-
}
112-
113108
#[test]
114109
fn set_and_get() {
115110
let rt = runtime::Builder::new_current_thread()
@@ -144,7 +139,7 @@ fn set_twice() {
144139
}
145140

146141
#[test]
147-
fn set_while_initializing() {
142+
fn set_while_initializing_or_already_init() {
148143
let rt = runtime::Builder::new_current_thread()
149144
.enable_time()
150145
.build()
@@ -153,24 +148,29 @@ fn set_while_initializing() {
153148
static ONCE: SetOnce<u32> = SetOnce::const_new();
154149

155150
rt.block_on(async {
156-
time::pause();
157-
158151
let handle1 = rt.spawn(async {
159-
time::sleep(Duration::from_millis(2)).await;
152+
time::sleep(Duration::from_millis(1)).await;
160153
let set_val = 5;
161154
ONCE.set(set_val)?;
162155

163-
Ok(set_val)
156+
Ok::<u32, SetError<u32>>(set_val)
164157
});
165-
let handle2 = rt.spawn(async { advance_time_and_set(&ONCE, 10).await });
166158

167-
time::advance(Duration::from_millis(2)).await;
159+
let handle2 = rt.spawn(async {
160+
time::sleep(Duration::from_millis(1)).await;
161+
let set_val = 10;
162+
ONCE.set(set_val)?;
163+
164+
Ok::<u32, SetError<u32>>(set_val)
165+
});
168166

169-
let result1: Result<u32, SetError<u32>> = handle1.await.unwrap();
167+
let result1 = handle1.await.unwrap();
170168
let result2 = handle2.await.unwrap();
171169

172170
assert_eq!(result1, Ok(5));
173-
assert!(result2.err().unwrap().is_initializing_err());
171+
let err = result2.err().unwrap();
172+
173+
assert!(err.is_initializing_err() || err.is_already_init_err());
174174
});
175175
}
176176

0 commit comments

Comments
 (0)