data_race: vector indices can be reused immediately when the thread i… · rust-lang/rust@e6bb468

@@ -47,7 +47,7 @@ use std::{

4747

};

48484949

use rustc_ast::Mutability;

50-

use rustc_data_structures::fx::{FxHashMap, FxHashSet};

50+

use rustc_data_structures::fx::FxHashSet;

5151

use rustc_index::{Idx, IndexVec};

5252

use rustc_middle::{mir, ty::Ty};

5353

use rustc_span::Span;

@@ -1432,13 +1432,6 @@ pub struct GlobalState {

14321432

/// active vector-clocks catch up with the threads timestamp.

14331433

reuse_candidates: RefCell<FxHashSet<VectorIdx>>,

143414341435-

/// This contains threads that have terminated, but not yet joined

1436-

/// and so cannot become re-use candidates until a join operation

1437-

/// occurs.

1438-

/// The associated vector index will be moved into re-use candidates

1439-

/// after the join operation occurs.

1440-

terminated_threads: RefCell<FxHashMap<ThreadId, VectorIdx>>,

1441-14421435

/// The timestamp of last SC fence performed by each thread

14431436

last_sc_fence: RefCell<VClock>,

14441437

@@ -1466,7 +1459,6 @@ impl GlobalState {

14661459

vector_info: RefCell::new(IndexVec::new()),

14671460

thread_info: RefCell::new(IndexVec::new()),

14681461

reuse_candidates: RefCell::new(FxHashSet::default()),

1469-

terminated_threads: RefCell::new(FxHashMap::default()),

14701462

last_sc_fence: RefCell::new(VClock::default()),

14711463

last_sc_write: RefCell::new(VClock::default()),

14721464

track_outdated_loads: config.track_outdated_loads,

@@ -1500,8 +1492,6 @@ impl GlobalState {

15001492

fn find_vector_index_reuse_candidate(&self) -> Option<VectorIdx> {

15011493

let mut reuse = self.reuse_candidates.borrow_mut();

15021494

let vector_clocks = self.vector_clocks.borrow();

1503-

let vector_info = self.vector_info.borrow();

1504-

let terminated_threads = self.terminated_threads.borrow();

15051495

for &candidate in reuse.iter() {

15061496

let target_timestamp = vector_clocks[candidate].clock[candidate];

15071497

if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| {

@@ -1511,9 +1501,7 @@ impl GlobalState {

1511150115121502

// The vector represents a thread that has terminated and hence cannot

15131503

// report a data-race with the candidate index.

1514-

let thread_id = vector_info[clock_idx];

1515-

let vector_terminated =

1516-

reuse.contains(&clock_idx) || terminated_threads.contains_key(&thread_id);

1504+

let vector_terminated = reuse.contains(&clock_idx);

1517150515181506

// The vector index cannot report a race with the candidate index

15191507

// and hence allows the candidate index to be re-used.

@@ -1603,55 +1591,38 @@ impl GlobalState {

16031591

/// thread (the joinee, the thread that someone waited on) and the current thread (the joiner,

16041592

/// the thread who was waiting).

16051593

#[inline]

1606-

pub fn thread_joined(

1607-

&mut self,

1608-

thread_mgr: &ThreadManager<'_, '_>,

1609-

joiner: ThreadId,

1610-

joinee: ThreadId,

1611-

) {

1612-

let clocks_vec = self.vector_clocks.get_mut();

1613-

let thread_info = self.thread_info.get_mut();

1614-1615-

// Load the vector clock of the current thread.

1616-

let current_index = thread_info[joiner]

1617-

.vector_index

1618-

.expect("Performed thread join on thread with no assigned vector");

1619-

let current = &mut clocks_vec[current_index];

1594+

pub fn thread_joined(&mut self, threads: &ThreadManager<'_, '_>, joinee: ThreadId) {

1595+

let thread_info = self.thread_info.borrow();

1596+

let thread_info = &thread_info[joinee];

1620159716211598

// Load the associated vector clock for the terminated thread.

1622-

let join_clock = thread_info[joinee]

1599+

let join_clock = thread_info

16231600

.termination_vector_clock

16241601

.as_ref()

1625-

.expect("Joined with thread but thread has not terminated");

1626-1627-

// The join thread happens-before the current thread

1628-

// so update the current vector clock.

1629-

// Is not a release operation so the clock is not incremented.

1630-

current.clock.join(join_clock);

1602+

.expect("joined with thread but thread has not terminated");

1603+

// Acquire that into the current thread.

1604+

self.acquire_clock(join_clock, threads);

1631160516321606

// Check the number of live threads, if the value is 1

16331607

// then test for potentially disabling multi-threaded execution.

1634-

if thread_mgr.get_live_thread_count() == 1 {

1635-

// May potentially be able to disable multi-threaded execution.

1636-

let current_clock = &clocks_vec[current_index];

1637-

if clocks_vec

1638-

.iter_enumerated()

1639-

.all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])

1640-

{

1641-

// All thread terminations happen-before the current clock

1642-

// therefore no data-races can be reported until a new thread

1643-

// is created, so disable multi-threaded execution.

1644-

self.multi_threaded.set(false);

1608+

// This has to happen after `acquire_clock`, otherwise there'll always

1609+

// be some thread that has not synchronized yet.

1610+

if let Some(current_index) = thread_info.vector_index {

1611+

if threads.get_live_thread_count() == 1 {

1612+

let vector_clocks = self.vector_clocks.get_mut();

1613+

// May potentially be able to disable multi-threaded execution.

1614+

let current_clock = &vector_clocks[current_index];

1615+

if vector_clocks

1616+

.iter_enumerated()

1617+

.all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])

1618+

{

1619+

// All thread terminations happen-before the current clock

1620+

// therefore no data-races can be reported until a new thread

1621+

// is created, so disable multi-threaded execution.

1622+

self.multi_threaded.set(false);

1623+

}

16451624

}

16461625

}

1647-1648-

// If the thread is marked as terminated but not joined

1649-

// then move the thread to the re-use set.

1650-

let termination = self.terminated_threads.get_mut();

1651-

if let Some(index) = termination.remove(&joinee) {

1652-

let reuse = self.reuse_candidates.get_mut();

1653-

reuse.insert(index);

1654-

}

16551626

}

1656162716571628

/// On thread termination, the vector-clock may re-used

@@ -1663,29 +1634,17 @@ impl GlobalState {

16631634

/// `thread_joined`.

16641635

#[inline]

16651636

pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_, '_>) {

1637+

let current_thread = thread_mgr.active_thread();

16661638

let current_index = self.active_thread_index(thread_mgr);

166716391668-

// Increment the clock to a unique termination timestamp.

1669-

let vector_clocks = self.vector_clocks.get_mut();

1670-

let current_clocks = &mut vector_clocks[current_index];

1671-

current_clocks

1672-

.increment_clock(current_index, thread_mgr.active_thread_ref().current_span());

1673-1674-

// Load the current thread id for the executing vector.

1675-

let vector_info = self.vector_info.get_mut();

1676-

let current_thread = vector_info[current_index];

1677-1678-

// Load the current thread metadata, and move to a terminated

1679-

// vector state. Setting up the vector clock all join operations

1680-

// will use.

1681-

let thread_info = self.thread_info.get_mut();

1682-

let current = &mut thread_info[current_thread];

1683-

current.termination_vector_clock = Some(current_clocks.clock.clone());

1684-1685-

// Add this thread as a candidate for re-use after a thread join

1686-

// occurs.

1687-

let termination = self.terminated_threads.get_mut();

1688-

termination.insert(current_thread, current_index);

1640+

// Store the terminaion clock.

1641+

let terminaion_clock = self.release_clock(thread_mgr).clone();

1642+

self.thread_info.get_mut()[current_thread].termination_vector_clock =

1643+

Some(terminaion_clock);

1644+1645+

// Add this thread's clock index as a candidate for re-use.

1646+

let reuse = self.reuse_candidates.get_mut();

1647+

reuse.insert(current_index);

16891648

}

1690164916911650

/// Attempt to perform a synchronized operation, this