@@ -6,9 +6,11 @@ use std::cell::{Ref, RefCell, RefMut};
6
6
use std:: collections:: BTreeMap ;
7
7
use std:: io:: { self , ErrorKind , IsTerminal , Read , SeekFrom , Write } ;
8
8
use std:: rc:: Rc ;
9
+ use std:: rc:: Weak ;
9
10
10
11
use rustc_target:: abi:: Size ;
11
12
13
+ use crate :: shims:: unix:: linux:: epoll:: EpollReadyEvents ;
12
14
use crate :: shims:: unix:: * ;
13
15
use crate :: * ;
14
16
@@ -27,6 +29,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
27
29
fn read < ' tcx > (
28
30
& mut self ,
29
31
_communicate_allowed : bool ,
32
+ _fd_id : FdId ,
30
33
_bytes : & mut [ u8 ] ,
31
34
_ecx : & mut MiriInterpCx < ' tcx > ,
32
35
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -37,6 +40,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
37
40
fn write < ' tcx > (
38
41
& mut self ,
39
42
_communicate_allowed : bool ,
43
+ _fd_id : FdId ,
40
44
_bytes : & [ u8 ] ,
41
45
_ecx : & mut MiriInterpCx < ' tcx > ,
42
46
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -80,6 +84,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
80
84
fn close < ' tcx > (
81
85
self : Box < Self > ,
82
86
_communicate_allowed : bool ,
87
+ _ecx : & mut MiriInterpCx < ' tcx > ,
83
88
) -> InterpResult < ' tcx , io:: Result < ( ) > > {
84
89
throw_unsup_format ! ( "cannot close {}" , self . name( ) ) ;
85
90
}
@@ -97,6 +102,11 @@ pub trait FileDescription: std::fmt::Debug + Any {
97
102
// so we use a default impl here.
98
103
false
99
104
}
105
+
106
+ /// Check the readiness of file description.
107
+ fn get_epoll_ready_events < ' tcx > ( & self ) -> InterpResult < ' tcx , EpollReadyEvents > {
108
+ throw_unsup_format ! ( "{}: epoll does not support this file description" , self . name( ) ) ;
109
+ }
100
110
}
101
111
102
112
impl dyn FileDescription {
@@ -119,6 +129,7 @@ impl FileDescription for io::Stdin {
119
129
fn read < ' tcx > (
120
130
& mut self ,
121
131
communicate_allowed : bool ,
132
+ _fd_id : FdId ,
122
133
bytes : & mut [ u8 ] ,
123
134
_ecx : & mut MiriInterpCx < ' tcx > ,
124
135
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -142,6 +153,7 @@ impl FileDescription for io::Stdout {
142
153
fn write < ' tcx > (
143
154
& mut self ,
144
155
_communicate_allowed : bool ,
156
+ _fd_id : FdId ,
145
157
bytes : & [ u8 ] ,
146
158
_ecx : & mut MiriInterpCx < ' tcx > ,
147
159
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -170,6 +182,7 @@ impl FileDescription for io::Stderr {
170
182
fn write < ' tcx > (
171
183
& mut self ,
172
184
_communicate_allowed : bool ,
185
+ _fd_id : FdId ,
173
186
bytes : & [ u8 ] ,
174
187
_ecx : & mut MiriInterpCx < ' tcx > ,
175
188
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -195,6 +208,7 @@ impl FileDescription for NullOutput {
195
208
fn write < ' tcx > (
196
209
& mut self ,
197
210
_communicate_allowed : bool ,
211
+ _fd_id : FdId ,
198
212
bytes : & [ u8 ] ,
199
213
_ecx : & mut MiriInterpCx < ' tcx > ,
200
214
) -> InterpResult < ' tcx , io:: Result < usize > > {
@@ -203,36 +217,98 @@ impl FileDescription for NullOutput {
203
217
}
204
218
}
205
219
220
+ /// Structure contains both the file description and its unique identifier.
221
+ #[ derive( Clone , Debug ) ]
222
+ pub struct FileDescWithId < T : FileDescription + ?Sized > {
223
+ id : FdId ,
224
+ file_description : RefCell < Box < T > > ,
225
+ }
226
+
206
227
#[ derive( Clone , Debug ) ]
207
- pub struct FileDescriptionRef ( Rc < RefCell < Box < dyn FileDescription > > > ) ;
228
+ pub struct FileDescriptionRef ( Rc < FileDescWithId < dyn FileDescription > > ) ;
208
229
209
230
impl FileDescriptionRef {
210
- fn new ( fd : impl FileDescription ) -> Self {
211
- FileDescriptionRef ( Rc :: new ( RefCell :: new ( Box :: new ( fd) ) ) )
231
+ fn new ( fd : impl FileDescription , id : FdId ) -> Self {
232
+ FileDescriptionRef ( Rc :: new ( FileDescWithId {
233
+ id,
234
+ file_description : RefCell :: new ( Box :: new ( fd) ) ,
235
+ } ) )
212
236
}
213
237
214
238
pub fn borrow ( & self ) -> Ref < ' _ , dyn FileDescription > {
215
- Ref :: map ( self . 0 . borrow ( ) , |fd| fd. as_ref ( ) )
239
+ Ref :: map ( self . 0 . file_description . borrow ( ) , |fd| fd. as_ref ( ) )
216
240
}
217
241
218
242
pub fn borrow_mut ( & self ) -> RefMut < ' _ , dyn FileDescription > {
219
- RefMut :: map ( self . 0 . borrow_mut ( ) , |fd| fd. as_mut ( ) )
243
+ RefMut :: map ( self . 0 . file_description . borrow_mut ( ) , |fd| fd. as_mut ( ) )
220
244
}
221
245
222
- pub fn close < ' ctx > ( self , communicate_allowed : bool ) -> InterpResult < ' ctx , io:: Result < ( ) > > {
246
+ pub fn close < ' tcx > (
247
+ self ,
248
+ communicate_allowed : bool ,
249
+ ecx : & mut MiriInterpCx < ' tcx > ,
250
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
223
251
// Destroy this `Rc` using `into_inner` so we can call `close` instead of
224
252
// implicitly running the destructor of the file description.
253
+ let id = self . get_id ( ) ;
225
254
match Rc :: into_inner ( self . 0 ) {
226
- Some ( fd) => RefCell :: into_inner ( fd) . close ( communicate_allowed) ,
255
+ Some ( fd) => {
256
+ // Remove entry from the global epoll_event_interest table.
257
+ ecx. machine . epoll_interests . remove ( id) ;
258
+
259
+ RefCell :: into_inner ( fd. file_description ) . close ( communicate_allowed, ecx)
260
+ }
227
261
None => Ok ( Ok ( ( ) ) ) ,
228
262
}
229
263
}
264
+
265
+ pub fn downgrade ( & self ) -> WeakFileDescriptionRef {
266
+ WeakFileDescriptionRef { weak_ref : Rc :: downgrade ( & self . 0 ) }
267
+ }
268
+
269
+ pub fn get_id ( & self ) -> FdId {
270
+ self . 0 . id
271
+ }
272
+
273
+ /// Function used to retrieve the readiness events of a file description and insert
274
+ /// an `EpollEventInstance` into the ready list if the file description is ready.
275
+ pub ( crate ) fn check_and_update_readiness < ' tcx > (
276
+ & self ,
277
+ ecx : & mut InterpCx < ' tcx , MiriMachine < ' tcx > > ,
278
+ ) -> InterpResult < ' tcx , ( ) > {
279
+ use crate :: shims:: unix:: linux:: epoll:: EvalContextExt ;
280
+ ecx. check_and_update_readiness ( self . get_id ( ) , || self . borrow_mut ( ) . get_epoll_ready_events ( ) )
281
+ }
282
+ }
283
+
284
+ /// Holds a weak reference to the actual file description.
285
+ #[ derive( Clone , Debug , Default ) ]
286
+ pub struct WeakFileDescriptionRef {
287
+ weak_ref : Weak < FileDescWithId < dyn FileDescription > > ,
288
+ }
289
+
290
+ impl WeakFileDescriptionRef {
291
+ pub fn upgrade ( & self ) -> Option < FileDescriptionRef > {
292
+ if let Some ( file_desc_with_id) = self . weak_ref . upgrade ( ) {
293
+ return Some ( FileDescriptionRef ( file_desc_with_id) ) ;
294
+ }
295
+ None
296
+ }
230
297
}
231
298
299
+ /// A unique id for file descriptions. While we could use the address, considering that
300
+ /// is definitely unique, the address would expose interpreter internal state when used
301
+ /// for sorting things. So instead we generate a unique id per file description that stays
302
+ /// the same even if a file descriptor is duplicated and gets a new integer file descriptor.
303
+ #[ derive( Debug , Copy , Clone , Default , Eq , PartialEq , Ord , PartialOrd ) ]
304
+ pub struct FdId ( usize ) ;
305
+
232
306
/// The file descriptor table
233
307
#[ derive( Debug ) ]
234
308
pub struct FdTable {
235
- fds : BTreeMap < i32 , FileDescriptionRef > ,
309
+ pub fds : BTreeMap < i32 , FileDescriptionRef > ,
310
+ /// Unique identifier for file description, used to differentiate between various file description.
311
+ next_file_description_id : FdId ,
236
312
}
237
313
238
314
impl VisitProvenance for FdTable {
@@ -243,7 +319,7 @@ impl VisitProvenance for FdTable {
243
319
244
320
impl FdTable {
245
321
fn new ( ) -> Self {
246
- FdTable { fds : BTreeMap :: new ( ) }
322
+ FdTable { fds : BTreeMap :: new ( ) , next_file_description_id : FdId ( 0 ) }
247
323
}
248
324
pub ( crate ) fn init ( mute_stdout_stderr : bool ) -> FdTable {
249
325
let mut fds = FdTable :: new ( ) ;
@@ -260,7 +336,8 @@ impl FdTable {
260
336
261
337
/// Insert a new file description to the FdTable.
262
338
pub fn insert_new ( & mut self , fd : impl FileDescription ) -> i32 {
263
- let file_handle = FileDescriptionRef :: new ( fd) ;
339
+ let file_handle = FileDescriptionRef :: new ( fd, self . next_file_description_id ) ;
340
+ self . next_file_description_id = FdId ( self . next_file_description_id . 0 . strict_add ( 1 ) ) ;
264
341
self . insert_ref_with_min_fd ( file_handle, 0 )
265
342
}
266
343
@@ -337,7 +414,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
337
414
// If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive.
338
415
if let Some ( file_description) = this. machine . fds . fds . insert ( new_fd, dup_fd) {
339
416
// Ignore close error (not interpreter's) according to dup2() doc.
340
- file_description. close ( this. machine . communicate ( ) ) ?. ok ( ) ;
417
+ file_description. close ( this. machine . communicate ( ) , this ) ?. ok ( ) ;
341
418
}
342
419
}
343
420
Ok ( Scalar :: from_i32 ( new_fd) )
@@ -442,7 +519,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
442
519
let Some ( file_description) = this. machine . fds . remove ( fd) else {
443
520
return Ok ( Scalar :: from_i32 ( this. fd_not_found ( ) ?) ) ;
444
521
} ;
445
- let result = file_description. close ( this. machine . communicate ( ) ) ?;
522
+ let result = file_description. close ( this. machine . communicate ( ) , this ) ?;
446
523
// return `0` if close is successful
447
524
let result = result. map ( |( ) | 0i32 ) ;
448
525
Ok ( Scalar :: from_i32 ( this. try_unwrap_io_result ( result) ?) )
@@ -499,7 +576,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
499
576
// `usize::MAX` because it is bounded by the host's `isize`.
500
577
let mut bytes = vec ! [ 0 ; usize :: try_from( count) . unwrap( ) ] ;
501
578
let result = match offset {
502
- None => fd. borrow_mut ( ) . read ( communicate, & mut bytes, this) ,
579
+ None => fd. borrow_mut ( ) . read ( communicate, fd . get_id ( ) , & mut bytes, this) ,
503
580
Some ( offset) => {
504
581
let Ok ( offset) = u64:: try_from ( offset) else {
505
582
let einval = this. eval_libc ( "EINVAL" ) ;
@@ -509,7 +586,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
509
586
fd. borrow_mut ( ) . pread ( communicate, & mut bytes, offset, this)
510
587
}
511
588
} ;
512
- drop ( fd) ;
513
589
514
590
// `File::read` never returns a value larger than `count`, so this cannot fail.
515
591
match result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) {
@@ -558,7 +634,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
558
634
} ;
559
635
560
636
let result = match offset {
561
- None => fd. borrow_mut ( ) . write ( communicate, & bytes, this) ,
637
+ None => fd. borrow_mut ( ) . write ( communicate, fd . get_id ( ) , & bytes, this) ,
562
638
Some ( offset) => {
563
639
let Ok ( offset) = u64:: try_from ( offset) else {
564
640
let einval = this. eval_libc ( "EINVAL" ) ;
@@ -568,7 +644,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
568
644
fd. borrow_mut ( ) . pwrite ( communicate, & bytes, offset, this)
569
645
}
570
646
} ;
571
- drop ( fd) ;
572
647
573
648
let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
574
649
Ok ( Scalar :: from_target_isize ( this. try_unwrap_io_result ( result) ?, this) )
0 commit comments