Skip to content

Commit 04053c1

Browse files
authored
fix race in MemoTableTypes (#912)
1 parent 09627e4 commit 04053c1

File tree

2 files changed

+35
-15
lines changed

2 files changed

+35
-15
lines changed

src/sync.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ pub mod shim {
6666
/// A polyfill for `std::sync::OnceLock`.
6767
pub struct OnceLock<T>(Mutex<bool>, UnsafeCell<MaybeUninit<T>>);
6868

69+
impl<T> Default for OnceLock<T> {
70+
fn default() -> Self {
71+
OnceLock::new()
72+
}
73+
}
74+
6975
impl<T> OnceLock<T> {
7076
pub const fn new() -> OnceLock<T> {
7177
OnceLock(Mutex::new(false), UnsafeCell::new(MaybeUninit::uninit()))

src/table/memo.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
ptr::{self, NonNull},
66
};
77

8+
use portable_atomic::hint::spin_loop;
89
use thin_vec::ThinVec;
910

1011
use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -47,6 +48,7 @@ struct MemoEntry {
4748
atomic_memo: AtomicPtr<DummyMemo>,
4849
}
4950

51+
#[derive(Default)]
5052
pub struct MemoEntryType {
5153
data: OnceLock<MemoEntryTypeData>,
5254
}
@@ -120,22 +122,34 @@ impl MemoTableTypes {
120122
memo_type: &MemoEntryType,
121123
) {
122124
let memo_ingredient_index = memo_ingredient_index.as_usize();
123-
while memo_ingredient_index >= self.types.count() {
124-
self.types.push(MemoEntryType {
125-
data: OnceLock::new(),
126-
});
125+
126+
// Try to create our entry if it has not already been created.
127+
if memo_ingredient_index >= self.types.count() {
128+
while self.types.push(MemoEntryType::default()) < memo_ingredient_index {}
129+
}
130+
131+
loop {
132+
let Some(memo_entry_type) = self.types.get(memo_ingredient_index) else {
133+
// It's possible that someone else began pushing to our index but has not
134+
// completed the entry's initialization yet, as `boxcar` is lock-free. This
135+
// is extremely unlikely given initialization is just a handful of instructions.
136+
// Additionally, this function is generally only called on startup, so we can
137+
// just spin here.
138+
spin_loop();
139+
continue;
140+
};
141+
142+
memo_entry_type
143+
.data
144+
.set(
145+
*memo_type.data.get().expect(
146+
"cannot provide an empty `MemoEntryType` for `MemoEntryType::set()`",
147+
),
148+
)
149+
.ok()
150+
.expect("memo type should only be set once");
151+
break;
127152
}
128-
let memo_entry_type = self.types.get(memo_ingredient_index).unwrap();
129-
memo_entry_type
130-
.data
131-
.set(
132-
*memo_type
133-
.data
134-
.get()
135-
.expect("cannot provide an empty `MemoEntryType` for `MemoEntryType::set()`"),
136-
)
137-
.ok()
138-
.expect("memo type should only be set once");
139153
}
140154

141155
/// # Safety

0 commit comments

Comments
 (0)