@@ -146,8 +146,7 @@ impl FileDescription for AnonSocket {
146146 // corresponding ErrorKind variant.
147147 throw_unsup_format ! ( "reading from the write end of a pipe" ) ;
148148 } ;
149- let mut readbuf = readbuf. borrow_mut ( ) ;
150- if readbuf. buf . is_empty ( ) {
149+ if readbuf. borrow ( ) . buf . is_empty ( ) {
151150 if self . peer_fd ( ) . upgrade ( ) . is_none ( ) {
152151 // Socketpair with no peer and empty buffer.
153152 // 0 bytes successfully read indicates end-of-file.
@@ -167,31 +166,8 @@ impl FileDescription for AnonSocket {
167166 }
168167 }
169168 }
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)
195171 }
196172
197173 fn write < ' tcx > (
@@ -221,9 +197,8 @@ impl FileDescription for AnonSocket {
221197 // corresponding ErrorKind variant.
222198 throw_unsup_format ! ( "writing to the reading end of a pipe" ) ;
223199 } ;
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 ( ) ) ;
227202 if available_space == 0 {
228203 if self . is_nonblock {
229204 // Non-blocking socketpair with a full buffer.
@@ -233,24 +208,85 @@ impl FileDescription for AnonSocket {
233208 throw_unsup_format ! ( "socketpair/pipe/pipe2 write: blocking isn't supported yet" ) ;
234209 }
235210 }
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+ }
244214
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+ }
247249
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 {
250286 ecx. check_and_update_readiness ( & peer_fd) ?;
251-
252- ecx. return_write_success ( actual_write_size, dest)
253287 }
288+
289+ ecx. return_read_success ( ptr, bytes, actual_read_size, dest)
254290}
255291
256292impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
0 commit comments