Skip to content

Commit dc035a2

Browse files
committed
Track which reference belongs to with store
Add a unique id to stores, and keep track of this id in references. When a reference is used with a different store, this will panic with a message explaining that the user might be trying to reuse object across executions.
1 parent f673005 commit dc035a2

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

src/rt/object.rs

+58-5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ use serde::{Deserialize, Serialize};
1515
pub(super) struct Store<T = Entry> {
1616
/// Stored state for all objects.
1717
entries: Vec<T>,
18+
19+
/// A unique id for this store, used to track references
20+
id: StoreId,
1821
}
1922

23+
24+
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
25+
pub(super) struct StoreId(usize);
26+
27+
2028
pub(super) trait Object: Sized {
2129
type Entry;
2230

@@ -40,6 +48,10 @@ pub(super) struct Ref<T = ()> {
4048
/// Index in the store
4149
index: usize,
4250

51+
/// Id of the store that created this reference. Optional, since `Ref`s can also be created from
52+
/// a bare `usize` index
53+
store_id: StoreId,
54+
4355
_p: PhantomData<T>,
4456
}
4557

@@ -147,6 +159,7 @@ impl<T> Store<T> {
147159
pub(super) fn with_capacity(capacity: usize) -> Store<T> {
148160
Store {
149161
entries: Vec::with_capacity(capacity),
162+
id: StoreId::new(),
150163
}
151164
}
152165

@@ -162,6 +175,10 @@ impl<T> Store<T> {
162175
self.entries.reserve_exact(additional);
163176
}
164177

178+
pub(super) fn get_id(&self) -> StoreId {
179+
self.id
180+
}
181+
165182
/// Insert an object into the store
166183
pub(super) fn insert<O>(&mut self, item: O) -> Ref<O>
167184
where
@@ -172,6 +189,7 @@ impl<T> Store<T> {
172189

173190
Ref {
174191
index,
192+
store_id: self.id,
175193
_p: PhantomData,
176194
}
177195
}
@@ -183,25 +201,29 @@ impl<T> Store<T> {
183201

184202
pub(crate) fn clear(&mut self) {
185203
self.entries.clear();
204+
self.id = StoreId::new();
186205
}
187206

188207
pub(super) fn iter_ref<O>(&self) -> impl DoubleEndedIterator<Item = Ref<O>> + '_
189208
where
190209
O: Object<Entry = T>,
191210
{
211+
let store_id = self.id;
212+
192213
self.entries
193214
.iter()
194215
.enumerate()
195216
.filter(|(_, e)| O::get_ref(e).is_some())
196-
.map(|(index, _)| Ref {
217+
.map(move |(index, _)| Ref {
197218
index,
219+
store_id,
198220
_p: PhantomData,
199221
})
200222
}
201223

202-
pub(super) fn iter_mut<'a, O>(&'a mut self) -> impl DoubleEndedIterator<Item = &mut O>
203-
where
204-
O: Object<Entry = T> + 'a,
224+
pub(super) fn iter_mut<'a, O>(&'a mut self) -> impl DoubleEndedIterator<Item=&mut O>
225+
where
226+
O: Object<Entry=T> + 'a,
205227
{
206228
self.entries.iter_mut().filter_map(O::get_mut)
207229
}
@@ -259,11 +281,27 @@ impl Store {
259281
}
260282
}
261283

284+
impl StoreId {
285+
pub(crate) fn new() -> StoreId {
286+
use std::sync::atomic::AtomicUsize;
287+
use std::sync::atomic::Ordering::Relaxed;
288+
289+
// The number picked here is arbitrary. It is mostly to avoid collision
290+
// with "zero" to aid with debugging.
291+
static NEXT_ID: AtomicUsize = AtomicUsize::new(41_123_456);
292+
293+
let next = NEXT_ID.fetch_add(1, Relaxed);
294+
295+
StoreId(next)
296+
}
297+
}
298+
262299
impl<T> Ref<T> {
263300
/// Erase the type marker
264301
pub(super) fn erase(self) -> Ref<()> {
265302
Ref {
266303
index: self.index,
304+
store_id: self.store_id,
267305
_p: PhantomData,
268306
}
269307
}
@@ -274,25 +312,38 @@ impl<T> Ref<T> {
274312
}
275313

276314
impl<T: Object> Ref<T> {
315+
/// Panic if the reference belongs to a different store
316+
fn assert_store_id(self, store: &Store<T::Entry>) {
317+
assert_eq!(
318+
self.store_id,
319+
store.id,
320+
"Tried to access an object using a reference that belongs to a different store. \
321+
This might indicate you are trying to reuse an object from an earlier execution"
322+
)
323+
}
324+
277325
/// Get a reference to the object associated with this reference from the store
278326
pub(super) fn get(self, store: &Store<T::Entry>) -> &T {
327+
self.assert_store_id(&store);
279328
T::get_ref(&store.entries[self.index])
280329
.expect("[loom internal bug] unexpected object stored at reference")
281330
}
282331

283332
/// Get a mutable reference to the object associated with this reference
284333
/// from the store
285334
pub(super) fn get_mut(self, store: &mut Store<T::Entry>) -> &mut T {
335+
self.assert_store_id(&store);
286336
T::get_mut(&mut store.entries[self.index])
287337
.expect("[loom internal bug] unexpected object stored at reference")
288338
}
289339
}
290340

291341
impl Ref {
292342
/// Convert a store index `usize` into a ref
293-
pub(super) fn from_usize(index: usize) -> Ref {
343+
pub(super) fn from_usize(index: usize, store_id: StoreId) -> Ref {
294344
Ref {
295345
index,
346+
store_id,
296347
_p: PhantomData,
297348
}
298349
}
@@ -303,6 +354,7 @@ impl Ref {
303354
{
304355
T::get_ref(&store.entries[self.index]).map(|_| Ref {
305356
index: self.index,
357+
store_id: self.store_id,
306358
_p: PhantomData,
307359
})
308360
}
@@ -312,6 +364,7 @@ impl<T> Clone for Ref<T> {
312364
fn clone(&self) -> Ref<T> {
313365
Ref {
314366
index: self.index,
367+
store_id: self.store_id,
315368
_p: PhantomData,
316369
}
317370
}

src/rt/path.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl Path {
166166
pub(super) fn branch_load(&mut self) -> usize {
167167
assert!(!self.is_traversed(), "[loom internal bug]");
168168

169-
let load = object::Ref::from_usize(self.pos)
169+
let load = object::Ref::from_usize(self.pos, self.branches.get_id())
170170
.downcast::<Load>(&self.branches)
171171
.expect("Reached unexpected exploration state. Is the model fully deterministic?")
172172
.get(&self.branches);
@@ -184,7 +184,7 @@ impl Path {
184184
self.branches.insert(Spurious(false));
185185
}
186186

187-
let spurious = object::Ref::from_usize(self.pos)
187+
let spurious = object::Ref::from_usize(self.pos, self.branches.get_id())
188188
.downcast::<Spurious>(&self.branches)
189189
.expect("Reached unexpected exploration state. Is the model fully deterministic?")
190190
.get(&self.branches)
@@ -274,7 +274,7 @@ impl Path {
274274
schedule.preemptions = preemptions;
275275
}
276276

277-
let schedule = object::Ref::from_usize(self.pos)
277+
let schedule = object::Ref::from_usize(self.pos, self.branches.get_id())
278278
.downcast::<Schedule>(&self.branches)
279279
.expect("Reached unexpected exploration state. Is the model fully deterministic?")
280280
.get(&self.branches);
@@ -290,7 +290,7 @@ impl Path {
290290
}
291291

292292
pub(super) fn backtrack(&mut self, point: usize, thread_id: thread::Id) {
293-
let schedule = object::Ref::from_usize(point)
293+
let schedule = object::Ref::from_usize(point, self.branches.get_id())
294294
.downcast::<Schedule>(&self.branches)
295295
.unwrap()
296296
.get_mut(&mut self.branches);
@@ -344,7 +344,7 @@ impl Path {
344344
// This is depth-first tree traversal.
345345
//
346346
for last in (0..self.branches.len()).rev() {
347-
let last = object::Ref::from_usize(last);
347+
let last = object::Ref::from_usize(last, self.branches.get_id());
348348

349349
// Remove all objects that were created **after** this branch
350350
self.branches.truncate(last);

tests/cell_reuse.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![deny(warnings, rust_2018_idioms)]
2+
3+
use loom::cell::Cell;
4+
5+
thread_local! {
6+
static ACTIVE_FRAME: Cell<()> = Cell::new(());
7+
}
8+
9+
#[test]
10+
#[should_panic="Tried to access an object using a reference that belongs to a different store. This might indicate you are trying to reuse an object from an earlier execution"]
11+
fn test_cell_reuse() {
12+
loom::model(|| {
13+
let handle_a = loom::thread::spawn(|| {
14+
let _ = ACTIVE_FRAME.with(Cell::get);
15+
});
16+
let handle_b = loom::thread::spawn(|| ());
17+
handle_a.join().unwrap();
18+
handle_b.join().unwrap();
19+
});
20+
}

0 commit comments

Comments
 (0)