@@ -146,8 +146,7 @@ impl FileDescription for AnonSocket {
146
146
// corresponding ErrorKind variant.
147
147
throw_unsup_format ! ( "reading from the write end of a pipe" ) ;
148
148
} ;
149
- let mut readbuf = readbuf. borrow_mut ( ) ;
150
- if readbuf. buf . is_empty ( ) {
149
+ if readbuf. borrow ( ) . buf . is_empty ( ) {
151
150
if self . peer_fd ( ) . upgrade ( ) . is_none ( ) {
152
151
// Socketpair with no peer and empty buffer.
153
152
// 0 bytes successfully read indicates end-of-file.
@@ -167,31 +166,8 @@ impl FileDescription for AnonSocket {
167
166
}
168
167
}
169
168
}
170
-
171
- // Synchronize with all previous writes to this buffer.
172
- // FIXME: this over-synchronizes; a more precise approach would be to
173
- // only sync with the writes whose data we will read.
174
- ecx. acquire_clock ( & readbuf. clock ) ;
175
-
176
- // Do full read / partial read based on the space available.
177
- // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
178
- let actual_read_size = readbuf. buf . read ( & mut bytes) . unwrap ( ) ;
179
-
180
- // Need to drop before others can access the readbuf again.
181
- drop ( readbuf) ;
182
-
183
- // A notification should be provided for the peer file description even when it can
184
- // only write 1 byte. This implementation is not compliant with the actual Linux kernel
185
- // implementation. For optimization reasons, the kernel will only mark the file description
186
- // as "writable" when it can write more than a certain number of bytes. Since we
187
- // don't know what that *certain number* is, we will provide a notification every time
188
- // a read is successful. This might result in our epoll emulation providing more
189
- // notifications than the real system.
190
- if let Some ( peer_fd) = self . peer_fd ( ) . upgrade ( ) {
191
- ecx. check_and_update_readiness ( & peer_fd) ?;
192
- }
193
-
194
- ecx. return_read_success ( ptr, & bytes, actual_read_size, dest)
169
+ // TODO: We might need to decide what to do if peer_fd is closed when read is blocked.
170
+ anonsocket_read ( self , self . peer_fd ( ) . upgrade ( ) , & mut bytes, ptr, dest, ecx)
195
171
}
196
172
197
173
fn write < ' tcx > (
@@ -221,9 +197,8 @@ impl FileDescription for AnonSocket {
221
197
// corresponding ErrorKind variant.
222
198
throw_unsup_format ! ( "writing to the reading end of a pipe" ) ;
223
199
} ;
224
- let mut writebuf = writebuf. borrow_mut ( ) ;
225
- let data_size = writebuf. buf . len ( ) ;
226
- let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( data_size) ;
200
+ let available_space =
201
+ MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( writebuf. borrow ( ) . buf . len ( ) ) ;
227
202
if available_space == 0 {
228
203
if self . is_nonblock {
229
204
// Non-blocking socketpair with a full buffer.
@@ -233,24 +208,85 @@ impl FileDescription for AnonSocket {
233
208
throw_unsup_format ! ( "socketpair/pipe/pipe2 write: blocking isn't supported yet" ) ;
234
209
}
235
210
}
236
- // Remember this clock so `read` can synchronize with us.
237
- ecx. release_clock ( |clock| {
238
- writebuf. clock . join ( clock) ;
239
- } ) ;
240
- // Do full write / partial write based on the space available.
241
- let actual_write_size = len. min ( available_space) ;
242
- let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
243
- writebuf. buf . extend ( & bytes[ ..actual_write_size] ) ;
211
+ anonsocket_write ( available_space, & peer_fd, ptr, len, dest, ecx)
212
+ }
213
+ }
244
214
245
- // Need to stop accessing peer_fd so that it can be notified.
246
- drop ( writebuf) ;
215
+ /// Write to AnonSocket based on the space available and return the written byte size.
216
+ fn anonsocket_write < ' tcx > (
217
+ available_space : usize ,
218
+ peer_fd : & FileDescriptionRef ,
219
+ ptr : Pointer ,
220
+ len : usize ,
221
+ dest : & MPlaceTy < ' tcx > ,
222
+ ecx : & mut MiriInterpCx < ' tcx > ,
223
+ ) -> InterpResult < ' tcx > {
224
+ let Some ( writebuf) = & peer_fd. downcast :: < AnonSocket > ( ) . unwrap ( ) . readbuf else {
225
+ // FIXME: This should return EBADF, but there's no nice way to do that as there's no
226
+ // corresponding ErrorKind variant.
227
+ throw_unsup_format ! ( "writing to the reading end of a pipe" )
228
+ } ;
229
+ let mut writebuf = writebuf. borrow_mut ( ) ;
230
+
231
+ // Remember this clock so `read` can synchronize with us.
232
+ ecx. release_clock ( |clock| {
233
+ writebuf. clock . join ( clock) ;
234
+ } ) ;
235
+ // Do full write / partial write based on the space available.
236
+ let actual_write_size = len. min ( available_space) ;
237
+ let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
238
+ writebuf. buf . extend ( & bytes[ ..actual_write_size] ) ;
239
+
240
+ // Need to stop accessing peer_fd so that it can be notified.
241
+ drop ( writebuf) ;
242
+
243
+ // Notification should be provided for peer fd as it became readable.
244
+ // The kernel does this even if the fd was already readable before, so we follow suit.
245
+ ecx. check_and_update_readiness ( peer_fd) ?;
246
+
247
+ ecx. return_write_success ( actual_write_size, dest)
248
+ }
247
249
248
- // Notification should be provided for peer fd as it became readable.
249
- // The kernel does this even if the fd was already readable before, so we follow suit.
250
+ /// Read from AnonSocket and return the number of bytes read.
251
+ fn anonsocket_read < ' tcx > (
252
+ anonsocket : & AnonSocket ,
253
+ peer_fd : Option < FileDescriptionRef > ,
254
+ bytes : & mut [ u8 ] ,
255
+ ptr : Pointer ,
256
+ dest : & MPlaceTy < ' tcx > ,
257
+ ecx : & mut MiriInterpCx < ' tcx > ,
258
+ ) -> InterpResult < ' tcx > {
259
+ let Some ( readbuf) = & anonsocket. readbuf else {
260
+ // FIXME: This should return EBADF, but there's no nice way to do that as there's no
261
+ // corresponding ErrorKind variant.
262
+ throw_unsup_format ! ( "reading from the write end of a pipe" )
263
+ } ;
264
+ let mut readbuf = readbuf. borrow_mut ( ) ;
265
+
266
+ // Synchronize with all previous writes to this buffer.
267
+ // FIXME: this over-synchronizes; a more precise approach would be to
268
+ // only sync with the writes whose data we will read.
269
+ ecx. acquire_clock ( & readbuf. clock ) ;
270
+
271
+ // Do full read / partial read based on the space available.
272
+ // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
273
+ let actual_read_size = readbuf. buf . read ( bytes) . unwrap ( ) ;
274
+
275
+ // Need to drop before others can access the readbuf again.
276
+ drop ( readbuf) ;
277
+
278
+ // A notification should be provided for the peer file description even when it can
279
+ // only write 1 byte. This implementation is not compliant with the actual Linux kernel
280
+ // implementation. For optimization reasons, the kernel will only mark the file description
281
+ // as "writable" when it can write more than a certain number of bytes. Since we
282
+ // don't know what that *certain number* is, we will provide a notification every time
283
+ // a read is successful. This might result in our epoll emulation providing more
284
+ // notifications than the real system.
285
+ if let Some ( peer_fd) = peer_fd {
250
286
ecx. check_and_update_readiness ( & peer_fd) ?;
251
-
252
- ecx. return_write_success ( actual_write_size, dest)
253
287
}
288
+
289
+ ecx. return_read_success ( ptr, bytes, actual_read_size, dest)
254
290
}
255
291
256
292
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
0 commit comments