Skip to content

Commit d90acac

Browse files
committed
Auto merge of rust-lang#3533 - Luv-Ray:file-descriptors-to-refcount-references, r=RalfJung
Make file descriptors into refcount references fixes rust-lang#3525 Remove `fn dup` in `trait FileDescription`, define `struct FileDescriptor(Rc<RefCell<dyn FileDescription>>)`, and use `BTreeMap<i32, FileDescriptor>` in `FdTable`. --- There are some refactors similar to the following form: ```rust { // origin: if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { // write file_descriptor this.try_unwrap_io_result(result) } else { this.fd_not_found() } } { // now: let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { return this.fd_not_found(); }; // write file_descriptor drop(file_descriptor); this.try_unwrap_io_result(result) } ``` The origin form can't compile because as using `RefCell` to get interior mutability, `fn get_mut` return `Option<std::cell::RefMut<'_, dyn FileDescription>>` instead of `Option<&mut dyn FileDescription>` now, and the `deref_mut` on `file_descriptor: RefMut` will cause borrow `this` as mutable more than once at a time. So this form of refactors and manual drops are are implemented to avoid borrowing `this` at the same time.
2 parents 37bd816 + 459c6ce commit d90acac

File tree

6 files changed

+193
-201
lines changed

6 files changed

+193
-201
lines changed

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

Lines changed: 86 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
//! standard file descriptors (stdin/stdout/stderr).
33
44
use std::any::Any;
5+
use std::cell::{Ref, RefCell, RefMut};
56
use std::collections::BTreeMap;
67
use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
8+
use std::rc::Rc;
79

810
use rustc_middle::ty::TyCtxt;
911
use rustc_target::abi::Size;
@@ -12,7 +14,7 @@ use crate::shims::unix::*;
1214
use crate::*;
1315

1416
/// Represents an open file descriptor.
15-
pub trait FileDescriptor: std::fmt::Debug + Any {
17+
pub trait FileDescription: std::fmt::Debug + Any {
1618
fn name(&self) -> &'static str;
1719

1820
fn read<'tcx>(
@@ -44,21 +46,18 @@ pub trait FileDescriptor: std::fmt::Debug + Any {
4446
fn close<'tcx>(
4547
self: Box<Self>,
4648
_communicate_allowed: bool,
47-
) -> InterpResult<'tcx, io::Result<i32>> {
49+
) -> InterpResult<'tcx, io::Result<()>> {
4850
throw_unsup_format!("cannot close {}", self.name());
4951
}
5052

51-
/// Return a new file descriptor *that refers to the same underlying object*.
52-
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
53-
5453
fn is_tty(&self, _communicate_allowed: bool) -> bool {
5554
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
5655
// so we use a default impl here.
5756
false
5857
}
5958
}
6059

61-
impl dyn FileDescriptor {
60+
impl dyn FileDescription {
6261
#[inline(always)]
6362
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
6463
(self as &dyn Any).downcast_ref()
@@ -70,7 +69,7 @@ impl dyn FileDescriptor {
7069
}
7170
}
7271

73-
impl FileDescriptor for io::Stdin {
72+
impl FileDescription for io::Stdin {
7473
fn name(&self) -> &'static str {
7574
"stdin"
7675
}
@@ -88,16 +87,12 @@ impl FileDescriptor for io::Stdin {
8887
Ok(Read::read(self, bytes))
8988
}
9089

91-
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
92-
Ok(Box::new(io::stdin()))
93-
}
94-
9590
fn is_tty(&self, communicate_allowed: bool) -> bool {
9691
communicate_allowed && self.is_terminal()
9792
}
9893
}
9994

100-
impl FileDescriptor for io::Stdout {
95+
impl FileDescription for io::Stdout {
10196
fn name(&self) -> &'static str {
10297
"stdout"
10398
}
@@ -120,16 +115,12 @@ impl FileDescriptor for io::Stdout {
120115
Ok(result)
121116
}
122117

123-
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
124-
Ok(Box::new(io::stdout()))
125-
}
126-
127118
fn is_tty(&self, communicate_allowed: bool) -> bool {
128119
communicate_allowed && self.is_terminal()
129120
}
130121
}
131122

132-
impl FileDescriptor for io::Stderr {
123+
impl FileDescription for io::Stderr {
133124
fn name(&self) -> &'static str {
134125
"stderr"
135126
}
@@ -145,10 +136,6 @@ impl FileDescriptor for io::Stderr {
145136
Ok(Write::write(&mut { self }, bytes))
146137
}
147138

148-
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
149-
Ok(Box::new(io::stderr()))
150-
}
151-
152139
fn is_tty(&self, communicate_allowed: bool) -> bool {
153140
communicate_allowed && self.is_terminal()
154141
}
@@ -158,7 +145,7 @@ impl FileDescriptor for io::Stderr {
158145
#[derive(Debug)]
159146
pub struct NullOutput;
160147

161-
impl FileDescriptor for NullOutput {
148+
impl FileDescription for NullOutput {
162149
fn name(&self) -> &'static str {
163150
"stderr and stdout"
164151
}
@@ -172,16 +159,30 @@ impl FileDescriptor for NullOutput {
172159
// We just don't write anything, but report to the user that we did.
173160
Ok(Ok(bytes.len()))
174161
}
162+
}
163+
164+
#[derive(Clone, Debug)]
165+
pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);
166+
167+
impl FileDescriptor {
168+
pub fn new<T: FileDescription>(fd: T) -> Self {
169+
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
170+
}
175171

176-
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
177-
Ok(Box::new(NullOutput))
172+
pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> {
173+
// Destroy this `Rc` using `into_inner` so we can call `close` instead of
174+
// implicitly running the destructor of the file description.
175+
match Rc::into_inner(self.0) {
176+
Some(fd) => RefCell::into_inner(fd).close(communicate_allowed),
177+
None => Ok(Ok(())),
178+
}
178179
}
179180
}
180181

181182
/// The file descriptor table
182183
#[derive(Debug)]
183184
pub struct FdTable {
184-
pub fds: BTreeMap<i32, Box<dyn FileDescriptor>>,
185+
pub fds: BTreeMap<i32, FileDescriptor>,
185186
}
186187

187188
impl VisitProvenance for FdTable {
@@ -192,28 +193,24 @@ impl VisitProvenance for FdTable {
192193

193194
impl FdTable {
194195
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
195-
let mut fds: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new();
196-
fds.insert(0i32, Box::new(io::stdin()));
196+
let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
197+
fds.insert(0i32, FileDescriptor::new(io::stdin()));
197198
if mute_stdout_stderr {
198-
fds.insert(1i32, Box::new(NullOutput));
199-
fds.insert(2i32, Box::new(NullOutput));
199+
fds.insert(1i32, FileDescriptor::new(NullOutput));
200+
fds.insert(2i32, FileDescriptor::new(NullOutput));
200201
} else {
201-
fds.insert(1i32, Box::new(io::stdout()));
202-
fds.insert(2i32, Box::new(io::stderr()));
202+
fds.insert(1i32, FileDescriptor::new(io::stdout()));
203+
fds.insert(2i32, FileDescriptor::new(io::stderr()));
203204
}
204205
FdTable { fds }
205206
}
206207

207-
pub fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
208+
pub fn insert_fd(&mut self, file_handle: FileDescriptor) -> i32 {
208209
self.insert_fd_with_min_fd(file_handle, 0)
209210
}
210211

211212
/// Insert a new FD that is at least `min_fd`.
212-
pub fn insert_fd_with_min_fd(
213-
&mut self,
214-
file_handle: Box<dyn FileDescriptor>,
215-
min_fd: i32,
216-
) -> i32 {
213+
pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 {
217214
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
218215
// between used FDs, the find_map combinator will return it. If the first such unused FD
219216
// is after all other used FDs, the find_map combinator will return None, and we will use
@@ -239,15 +236,22 @@ impl FdTable {
239236
new_fd
240237
}
241238

242-
pub fn get(&self, fd: i32) -> Option<&dyn FileDescriptor> {
243-
Some(&**self.fds.get(&fd)?)
239+
pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
240+
let fd = self.fds.get(&fd)?;
241+
Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref()))
244242
}
245243

246-
pub fn get_mut(&mut self, fd: i32) -> Option<&mut dyn FileDescriptor> {
247-
Some(&mut **self.fds.get_mut(&fd)?)
244+
pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
245+
let fd = self.fds.get(&fd)?;
246+
Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut()))
248247
}
249248

250-
pub fn remove(&mut self, fd: i32) -> Option<Box<dyn FileDescriptor>> {
249+
pub fn dup(&self, fd: i32) -> Option<FileDescriptor> {
250+
let fd = self.fds.get(&fd)?;
251+
Some(fd.clone())
252+
}
253+
254+
pub fn remove(&mut self, fd: i32) -> Option<FileDescriptor> {
251255
self.fds.remove(&fd)
252256
}
253257

@@ -296,17 +300,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
296300
}
297301
let start = this.read_scalar(&args[2])?.to_i32()?;
298302

299-
match this.machine.fds.get_mut(fd) {
300-
Some(file_descriptor) => {
301-
let dup_result = file_descriptor.dup();
302-
match dup_result {
303-
Ok(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
304-
Err(e) => {
305-
this.set_last_error_from_io_error(e.kind())?;
306-
Ok(-1)
307-
}
308-
}
309-
}
303+
match this.machine.fds.dup(fd) {
304+
Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
310305
None => this.fd_not_found(),
311306
}
312307
} else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") {
@@ -330,6 +325,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
330325

331326
Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.remove(fd) {
332327
let result = file_descriptor.close(this.machine.communicate())?;
328+
// return `0` if close is successful
329+
let result = result.map(|()| 0i32);
333330
this.try_unwrap_io_result(result)?
334331
} else {
335332
this.fd_not_found()?
@@ -369,32 +366,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
369366
.min(u64::try_from(isize::MAX).unwrap());
370367
let communicate = this.machine.communicate();
371368

372-
if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
373-
trace!("read: FD mapped to {:?}", file_descriptor);
374-
// We want to read at most `count` bytes. We are sure that `count` is not negative
375-
// because it was a target's `usize`. Also we are sure that its smaller than
376-
// `usize::MAX` because it is bounded by the host's `isize`.
377-
let mut bytes = vec![0; usize::try_from(count).unwrap()];
378-
// `File::read` never returns a value larger than `count`,
379-
// so this cannot fail.
380-
let result = file_descriptor
381-
.read(communicate, &mut bytes, *this.tcx)?
382-
.map(|c| i64::try_from(c).unwrap());
383-
384-
match result {
385-
Ok(read_bytes) => {
386-
// If reading to `bytes` did not fail, we write those bytes to the buffer.
387-
this.write_bytes_ptr(buf, bytes)?;
388-
Ok(read_bytes)
389-
}
390-
Err(e) => {
391-
this.set_last_error_from_io_error(e.kind())?;
392-
Ok(-1)
393-
}
394-
}
395-
} else {
369+
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
396370
trace!("read: FD not found");
397-
this.fd_not_found()
371+
return this.fd_not_found();
372+
};
373+
374+
trace!("read: FD mapped to {:?}", file_descriptor);
375+
// We want to read at most `count` bytes. We are sure that `count` is not negative
376+
// because it was a target's `usize`. Also we are sure that its smaller than
377+
// `usize::MAX` because it is bounded by the host's `isize`.
378+
let mut bytes = vec![0; usize::try_from(count).unwrap()];
379+
// `File::read` never returns a value larger than `count`,
380+
// so this cannot fail.
381+
let result = file_descriptor
382+
.read(communicate, &mut bytes, *this.tcx)?
383+
.map(|c| i64::try_from(c).unwrap());
384+
drop(file_descriptor);
385+
386+
match result {
387+
Ok(read_bytes) => {
388+
// If reading to `bytes` did not fail, we write those bytes to the buffer.
389+
this.write_bytes_ptr(buf, bytes)?;
390+
Ok(read_bytes)
391+
}
392+
Err(e) => {
393+
this.set_last_error_from_io_error(e.kind())?;
394+
Ok(-1)
395+
}
398396
}
399397
}
400398

@@ -419,13 +417,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
419417
let communicate = this.machine.communicate();
420418

421419
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
422-
if let Some(file_descriptor) = this.machine.fds.get_mut(fd) {
423-
let result = file_descriptor
424-
.write(communicate, &bytes, *this.tcx)?
425-
.map(|c| i64::try_from(c).unwrap());
426-
this.try_unwrap_io_result(result)
427-
} else {
428-
this.fd_not_found()
429-
}
420+
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
421+
return this.fd_not_found();
422+
};
423+
424+
let result = file_descriptor
425+
.write(communicate, &bytes, *this.tcx)?
426+
.map(|c| i64::try_from(c).unwrap());
427+
drop(file_descriptor);
428+
429+
this.try_unwrap_io_result(result)
430430
}
431431
}

0 commit comments

Comments
 (0)