Skip to content

Commit fa6fedc

Browse files
Rollup merge of #155197 - RalfJung:miri, r=RalfJung
miri subtree update Subtree update of `miri` to rust-lang/miri@871d3d0. Created using https://github.com/rust-lang/josh-sync. r? @ghost
2 parents 540f43a + edf04e1 commit fa6fedc

14 files changed

Lines changed: 235 additions & 88 deletions

File tree

src/tools/miri/genmc-sys/build.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ mod downloading {
3131
pub(crate) const GENMC_COMMIT: &str = "22d3d0b44dedb4e8e1aae3330e546465e4664529";
3232

3333
/// Ensure that a local GenMC repo is present and set to the correct commit.
34-
/// Return the path of the GenMC repo and whether the checked out commit was changed.
35-
pub(crate) fn download_genmc() -> (PathBuf, bool) {
34+
/// Return the path of the GenMC repo clone.
35+
pub(crate) fn download_genmc() -> PathBuf {
3636
let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH);
3737
let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid.");
3838

@@ -44,7 +44,7 @@ mod downloading {
4444
&& head_commit.id() == commit_oid
4545
{
4646
// Fast path: The expected commit is already checked out.
47-
return (genmc_download_path, false);
47+
return genmc_download_path;
4848
}
4949
// Check if the local repository already contains the commit we need, download it otherwise.
5050
let commit = update_local_repo(&repo, commit_oid);
@@ -61,7 +61,7 @@ mod downloading {
6161
}
6262
};
6363

64-
(genmc_download_path, true)
64+
genmc_download_path
6565
}
6666

6767
fn get_remote(repo: &Repository) -> Remote<'_> {
@@ -141,7 +141,7 @@ mod downloading {
141141
}
142142

143143
/// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs
144-
fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
144+
fn compile_cpp_dependencies(genmc_path: &Path) {
145145
// Give each step a separate build directory to prevent interference.
146146
let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap());
147147
let genmc_build_dir = out_dir.join("genmc");
@@ -156,7 +156,6 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
156156

157157
let mut config = cmake::Config::new(genmc_path);
158158
config
159-
.always_configure(always_configure) // We force running the configure step when the GenMC commit changed.
160159
.out_dir(genmc_build_dir)
161160
.profile(GENMC_CMAKE_PROFILE)
162161
.define("BUILD_LLI", "OFF")
@@ -208,8 +207,7 @@ fn main() {
208207
}
209208

210209
// Select which path to use for the GenMC repo:
211-
let (genmc_path, always_configure) = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH")
212-
{
210+
let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") {
213211
let genmc_src_path =
214212
PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path");
215213
assert!(
@@ -219,18 +217,14 @@ fn main() {
219217
);
220218
// Rebuild files in the given path change.
221219
println!("cargo::rerun-if-changed={}", genmc_src_path.display());
222-
// We disable `always_configure` when working with a local repository,
223-
// since it increases compile times when working on `genmc-sys`.
224-
(genmc_src_path, false)
220+
genmc_src_path
225221
} else {
226222
// Download GenMC if required and ensure that the correct commit is checked out.
227-
// If anything changed in the downloaded repository (e.g., the commit),
228-
// we set `always_configure` to ensure there are no weird configs from previous builds.
229223
downloading::download_genmc()
230224
};
231225

232226
// Build all required components:
233-
compile_cpp_dependencies(&genmc_path, always_configure);
227+
compile_cpp_dependencies(&genmc_path);
234228

235229
// Only rebuild if anything changes:
236230
// Note that we don't add the downloaded GenMC repo, since that should never be modified

src/tools/miri/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
55e86c996809902e8bbad512cfb4d2c18be446d9
1+
4c4205163abcbd08948b3efab796c543ba1ea687

src/tools/miri/src/bin/miri.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,9 @@ impl rustc_driver::Callbacks for MiriDepCompilerCalls {
309309
// We need to add #[used] symbols to exported_symbols for `lookup_link_section`.
310310
// FIXME handle this somehow in rustc itself to avoid this hack.
311311
local_providers.queries.exported_non_generic_symbols = |tcx, LocalCrate| {
312-
let reachable_set = tcx
313-
.with_stable_hashing_context(|mut hcx| tcx.reachable_set(()).to_sorted(&mut hcx, true));
312+
let reachable_set = tcx.with_stable_hashing_context(|mut hcx| {
313+
tcx.reachable_set(()).to_sorted(&mut hcx, true)
314+
});
314315
tcx.arena.alloc_from_iter(
315316
// This is based on:
316317
// https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63

src/tools/miri/src/borrow_tracker/stacked_borrows/item.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use crate::borrow_tracker::BorTag;
77
pub struct Item(u64);
88

99
// An Item contains 3 bitfields:
10-
// * Bits 0-61 store a BorTag.
11-
// * Bits 61-63 store a Permission.
12-
// * Bit 64 stores a flag which indicates if we might have a protector.
10+
// * Bits 0-60 store a BorTag (61 bits).
11+
// * Bits 61-62 store a Permission (2 bits).
12+
// * Bit 63 stores a flag which indicates if we might have a protector (1 bit).
1313
// This is purely an optimization: if the bit is set, the tag *might* be
1414
// in `protected_tags`, but if the bit is not set then the tag is definitely
1515
// not in `protected_tags`.

src/tools/miri/src/concurrency/blocking_io.rs

Lines changed: 110 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::collections::BTreeMap;
12
use std::io;
23
use std::time::Duration;
34

45
use mio::event::Source;
56
use mio::{Events, Interest, Poll, Token};
67
use rustc_data_structures::fx::FxHashMap;
78

9+
use crate::shims::{FdId, FileDescriptionRef};
810
use crate::*;
911

1012
/// Capacity of the event queue which can be polled at a time.
@@ -18,6 +20,14 @@ pub trait WithSource {
1820
fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()>;
1921
}
2022

23+
/// An interest receiver defines the action that should be taken when
24+
/// the associated [`Interest`] is fulfilled.
25+
#[derive(Debug, Hash, PartialEq, Clone, Copy, Eq, PartialOrd, Ord)]
26+
pub enum InterestReceiver {
27+
/// The specified thread should be unblocked.
28+
UnblockThread(ThreadId),
29+
}
30+
2131
/// Manager for managing blocking host I/O in a non-blocking manner.
2232
/// We use [`Poll`] to poll for new I/O events from the OS for sources
2333
/// registered using this manager.
@@ -34,9 +44,10 @@ pub struct BlockingIoManager {
3444
/// This is not part of the state and only stored to avoid allocating a
3545
/// new buffer for every poll.
3646
events: Events,
37-
/// Map between threads which are currently blocked and the
38-
/// underlying I/O source.
39-
sources: FxHashMap<ThreadId, Box<dyn WithSource>>,
47+
/// Map from source ids to the actual sources and their registered receivers
48+
/// together with their associated interests.
49+
sources:
50+
BTreeMap<FdId, (FileDescriptionRef<dyn WithSource>, FxHashMap<InterestReceiver, Interest>)>,
4051
}
4152

4253
impl BlockingIoManager {
@@ -46,7 +57,7 @@ impl BlockingIoManager {
4657
let manager = Self {
4758
poll: communicate.then_some(Poll::new()?),
4859
events: Events::with_capacity(IO_EVENT_CAPACITY),
49-
sources: FxHashMap::default(),
60+
sources: BTreeMap::default(),
5061
};
5162
Ok(manager)
5263
}
@@ -59,8 +70,12 @@ impl BlockingIoManager {
5970
/// specified duration.
6071
/// - If the timeout is [`None`] the poll blocks indefinitely until an event occurs.
6172
///
62-
/// Returns all threads that are ready because they received an I/O event.
63-
pub fn poll(&mut self, timeout: Option<Duration>) -> Result<Vec<ThreadId>, io::Error> {
73+
/// Returns the interest receivers for all file descriptions which received an I/O event together
74+
/// with the file description they were registered for.
75+
pub fn poll(
76+
&mut self,
77+
timeout: Option<Duration>,
78+
) -> Result<Vec<(InterestReceiver, FileDescriptionRef<dyn WithSource>)>, io::Error> {
6479
let poll =
6580
self.poll.as_mut().expect("Blocking I/O should not be called with isolation enabled");
6681

@@ -70,56 +85,120 @@ impl BlockingIoManager {
7085
let ready = self
7186
.events
7287
.iter()
73-
.map(|event| {
88+
.flat_map(|event| {
7489
let token = event.token();
75-
ThreadId::new_unchecked(token.0.try_into().unwrap())
90+
// We know all tokens are valid `FdId`.
91+
let fd_id = FdId::new_unchecked(token.0);
92+
let (source, interests) =
93+
self.sources.get(&fd_id).expect("Source should be registered");
94+
assert_eq!(source.id(), fd_id);
95+
// Because we allow spurious wake-ups, we mark all interests as ready even
96+
// though some may not have been fulfilled.
97+
interests.keys().map(move |receiver| (*receiver, source.clone()))
7698
})
7799
.collect::<Vec<_>>();
78100

79-
// Deregister all ready sources as we only want to receive one event per thread.
80-
ready.iter().for_each(|thread_id| self.deregister(*thread_id));
101+
// Deregister all ready sources as we only want to receive one event per receiver.
102+
ready.iter().for_each(|(receiver, source)| self.deregister(source.id(), *receiver));
81103

82104
Ok(ready)
83105
}
84106

85-
/// Register a blocking I/O source for a thread together with it's poll interests.
86-
///
87-
/// The source will be deregistered automatically once an event for it is received.
107+
/// Register an interest for a blocking I/O source.
88108
///
89109
/// As the OS can always produce spurious wake-ups, it's the callers responsibility to
90110
/// verify the requested I/O interests are really ready and to register again if they're not.
91-
pub fn register(&mut self, source: Box<dyn WithSource>, thread: ThreadId, interests: Interest) {
111+
///
112+
/// It's assumed that no interest is already registered for this source with the same reason!
113+
pub fn register(
114+
&mut self,
115+
source_fd: FileDescriptionRef<dyn WithSource>,
116+
receiver: InterestReceiver,
117+
interest: Interest,
118+
) {
92119
let poll =
93120
self.poll.as_ref().expect("Blocking I/O should not be called with isolation enabled");
94121

95-
let token = Token(thread.to_u32().to_usize());
122+
let id = source_fd.id();
123+
let token = Token(id.to_usize());
124+
125+
let Some((_, current_interests)) = self.sources.get_mut(&id) else {
126+
// The source is not yet registered.
127+
128+
// Treat errors from registering as fatal. On UNIX hosts this can only
129+
// fail due to system resource errors (e.g. ENOMEM or ENOSPC).
130+
source_fd
131+
.with_source(&mut |source| poll.registry().register(source, token, interest))
132+
.unwrap();
133+
134+
self.sources.insert(id, (source_fd, FxHashMap::from_iter([(receiver, interest)])));
135+
return;
136+
};
137+
138+
// The source is already registered. We need to check whether we need to
139+
// reregister because the provided interest contains new interests for the source.
96140

97-
// Treat errors from registering as fatal. On UNIX hosts this can only
141+
let old_interest =
142+
interest_union(current_interests).expect("Source should contain at least one interest");
143+
144+
current_interests
145+
.try_insert(receiver, interest)
146+
.unwrap_or_else(|_| panic!("Receiver should be unique"));
147+
148+
let new_interest = old_interest.add(interest);
149+
150+
// Reregister the source since the overall interests might have changed.
151+
152+
// Treat errors from reregistering as fatal. On UNIX hosts this can only
98153
// fail due to system resource errors (e.g. ENOMEM or ENOSPC).
99-
source
100-
.with_source(&mut |source| source.register(poll.registry(), token, interests))
154+
source_fd
155+
.with_source(&mut |source| poll.registry().reregister(source, token, new_interest))
101156
.unwrap();
102-
self.sources
103-
.try_insert(thread, source)
104-
.unwrap_or_else(|_| panic!("A thread cannot be registered twice at the same time"));
105157
}
106158

107-
/// Deregister the event source for a thread. Returns the kind of I/O the thread was
108-
/// blocked on.
109-
fn deregister(&mut self, thread: ThreadId) {
159+
/// Deregister an interest from a blocking I/O source.
160+
///
161+
/// The receiver is assumed to be registered for the provided source!
162+
pub fn deregister(&mut self, source_id: FdId, receiver: InterestReceiver) {
110163
let poll =
111164
self.poll.as_ref().expect("Blocking I/O should not be called with isolation enabled");
112165

113-
let Some(source) = self.sources.remove(&thread) else {
114-
panic!("Attempt to deregister a token which isn't registered")
166+
let token = Token(source_id.to_usize());
167+
let (fd, current_interests) =
168+
self.sources.get_mut(&source_id).expect("Source should be registered");
169+
170+
current_interests
171+
.remove(&receiver)
172+
.unwrap_or_else(|| panic!("Receiver should be registered for source"));
173+
174+
let Some(new_interest) = interest_union(current_interests) else {
175+
// There are no longer any interests in this source.
176+
// We can thus deregister the source from the poll.
177+
178+
// Treat errors from deregistering as fatal. On UNIX hosts this can only
179+
// fail due to system resource errors (e.g. ENOMEM or ENOSPC).
180+
fd.with_source(&mut |source| poll.registry().deregister(source)).unwrap();
181+
self.sources.remove(&source_id);
182+
return;
115183
};
116184

117-
// Treat errors from deregistering as fatal. On UNIX hosts this can only
185+
// Reregister the source since the overall interests might have changed.
186+
187+
// Treat errors from reregistering as fatal. On UNIX hosts this can only
118188
// fail due to system resource errors (e.g. ENOMEM or ENOSPC).
119-
source.with_source(&mut |source| source.deregister(poll.registry())).unwrap();
189+
fd.with_source(&mut |source| poll.registry().reregister(source, token, new_interest))
190+
.unwrap();
120191
}
121192
}
122193

194+
/// Get the union of all interests for a source. Returns `None` if the map is empty.
195+
fn interest_union(interests: &FxHashMap<InterestReceiver, Interest>) -> Option<Interest> {
196+
interests
197+
.values()
198+
.copied()
199+
.fold(None, |acc, interest| acc.map(|acc: Interest| acc.add(interest)).or(Some(interest)))
200+
}
201+
123202
impl<'tcx> EvalContextExt<'tcx> for MiriInterpCx<'tcx> {}
124203
pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
125204
/// Block the current thread until some interests on an I/O source
@@ -132,15 +211,15 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
132211
#[inline]
133212
fn block_thread_for_io(
134213
&mut self,
135-
source: impl WithSource + 'static,
214+
source_fd: FileDescriptionRef<dyn WithSource>,
136215
interests: Interest,
137216
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
138217
callback: DynUnblockCallback<'tcx>,
139218
) {
140219
let this = self.eval_context_mut();
141220
this.machine.blocking_io.register(
142-
Box::new(source),
143-
this.machine.threads.active_thread(),
221+
source_fd,
222+
InterestReceiver::UnblockThread(this.machine.threads.active_thread()),
144223
interests,
145224
);
146225
this.block_thread(BlockReason::IO, timeout, callback);

src/tools/miri/src/concurrency/thread.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_span::{DUMMY_SP, Span};
1818
use rustc_target::spec::Os;
1919

2020
use crate::concurrency::GlobalDataRaceHandler;
21+
use crate::concurrency::blocking_io::InterestReceiver;
2122
use crate::shims::tls;
2223
use crate::*;
2324

@@ -822,7 +823,12 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
822823
Err(e) => panic!("unexpected error while polling: {e}"),
823824
};
824825

825-
ready.into_iter().try_for_each(|thread_id| this.unblock_thread(thread_id, BlockReason::IO))
826+
ready.into_iter().try_for_each(|(receiver, _source)| {
827+
match receiver {
828+
InterestReceiver::UnblockThread(thread_id) =>
829+
this.unblock_thread(thread_id, BlockReason::IO),
830+
}
831+
})
826832
}
827833

828834
/// Find all threads with expired timeouts, unblock them and execute their timeout callbacks.

src/tools/miri/src/shims/files.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ use crate::*;
1919
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
2020
pub struct FdId(usize);
2121

22+
impl FdId {
23+
pub fn to_usize(self) -> usize {
24+
self.0
25+
}
26+
27+
/// Create a new fd id from a `usize` without checking if this fd exists.
28+
pub fn new_unchecked(id: usize) -> Self {
29+
Self(id)
30+
}
31+
}
32+
2233
#[derive(Debug, Clone)]
2334
struct FdIdWith<T: ?Sized> {
2435
id: FdId,

src/tools/miri/src/shims/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub mod time;
2323
pub mod tls;
2424
pub mod unwind;
2525

26-
pub use self::files::FdTable;
26+
pub use self::files::{FdId, FdTable, FileDescriptionRef};
2727
#[cfg(all(feature = "native-lib", unix))]
2828
pub use self::native_lib::trace::{init_sv, register_retcode_sv};
2929
pub use self::unix::{DirTable, EpollInterestTable};

src/tools/miri/src/shims/unix/fd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::shims::unix::*;
1515
use crate::*;
1616

1717
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
18-
pub(crate) enum FlockOp {
18+
pub enum FlockOp {
1919
SharedLock { nonblocking: bool },
2020
ExclusiveLock { nonblocking: bool },
2121
Unlock,

src/tools/miri/src/shims/unix/socket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,7 @@ impl VisitProvenance for FileDescriptionRef<Socket> {
13881388
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
13891389
}
13901390

1391-
impl WithSource for FileDescriptionRef<Socket> {
1391+
impl WithSource for Socket {
13921392
fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
13931393
let mut state = self.state.borrow_mut();
13941394
match &mut *state {

0 commit comments

Comments
 (0)