@@ -415,8 +415,8 @@ class BufferGuestImpl(Buffer):
415415 length: int
416416
417417 def __init__ (self , t , cx , ptr , length ):
418- trap_if(length == 0 or length > Buffer.MAX_LENGTH )
419- if t:
418+ trap_if(length > Buffer.MAX_LENGTH )
419+ if t and length > 0 :
420420 trap_if(ptr != align_to(ptr, alignment(t)))
421421 trap_if(ptr + length * elem_size(t) > len (cx.opts.memory))
422422 self .cx = cx
@@ -1299,10 +1299,13 @@ class ReadableStreamGuestImpl(ReadableStream):
12991299 self .reset_pending()
13001300
13011301 def reset_pending (self ):
1302- self .pending_inst = None
1303- self .pending_buffer = None
1304- self .pending_on_partial_copy = None
1305- self .pending_on_copy_done = None
1302+ self .set_pending(None , None , None , None )
1303+
1304+ def set_pending (self , inst , buffer , on_partial_copy , on_copy_done ):
1305+ self .pending_inst = inst
1306+ self .pending_buffer = buffer
1307+ self .pending_on_partial_copy = on_partial_copy
1308+ self .pending_on_copy_done = on_copy_done
13061309```
13071310If set, the ` pending_* ` fields record the ` Buffer ` and ` On* ` callbacks of a
13081311` read ` or ` write ` that is waiting to rendezvous with a complementary ` write ` or
@@ -1356,27 +1359,45 @@ but in the opposite direction. Both are implemented by a single underlying
13561359 if self .closed_:
13571360 return ' done'
13581361 elif not self .pending_buffer:
1359- self .pending_inst = inst
1360- self .pending_buffer = buffer
1361- self .pending_on_partial_copy = on_partial_copy
1362- self .pending_on_copy_done = on_copy_done
1362+ self .set_pending(inst, buffer, on_partial_copy, on_copy_done)
13631363 return ' blocked'
13641364 else :
13651365 trap_if(inst is self .pending_inst) # temporary
1366- ncopy = min (src.remain(), dst.remain())
1367- assert (ncopy > 0 )
1368- dst.write(src.read(ncopy))
13691366 if self .pending_buffer.remain() > 0 :
1370- self .pending_on_partial_copy(self .reset_pending)
1367+ if buffer.remain() > 0 :
1368+ dst.write(src.read(min (src.remain(), dst.remain())))
1369+ if self .pending_buffer.remain() > 0 :
1370+ self .pending_on_partial_copy(self .reset_pending)
1371+ else :
1372+ self .reset_and_notify_pending(' completed' )
1373+ return ' done'
13711374 else :
1372- self .reset_and_notify_pending(' completed' )
1373- return ' done'
1374- ```
1375- Currently, there is a trap when both the ` read ` and ` write ` come from the same
1376- component instance, but this trapping condition will be removed in a subsequent
1377- release. The reason for this trap is that when lifting and lowering can alias
1378- the same memory, interleaving must be handled carefully. Future improvements to
1379- the Canonical ABI ([ lazy lowering] ) can greatly simplify this interleaving.
1375+ if buffer.remain() > 0 or buffer is dst:
1376+ self .reset_and_notify_pending(' completed' )
1377+ self .set_pending(inst, buffer, on_partial_copy, on_copy_done)
1378+ return ' blocked'
1379+ else :
1380+ return ' done'
1381+ ```
1382+ The meaning of a ` read ` or ` write ` when the length is ` 0 ` is that the caller is
1383+ querying the "readiness" of the other side. When a ` 0 ` -length read/write
1384+ rendezvous with a non-` 0 ` -length read/write, only the ` 0 ` -length read/write
1385+ completes; the non-` 0 ` -length read/write is kept pending (and ready for a
1386+ subsequent rendezvous).
1387+
1388+ In the corner case where a ` 0 ` -length read * and* write rendezvous, only the
1389+ * writer* is notified of readiness. To avoid livelock, the Canonical ABI
1390+ requires that a writer * must* (eventually) follow a completed ` 0 ` -length write
1391+ with a non-` 0 ` -length write that is allowed to block (allowing the reader end
1392+ to run and rendezvous with its own non-` 0 ` -length read). To implement a
1393+ traditional ` O_NONBLOCK ` ` write() ` or ` sendmsg() ` API, a writer can use a
1394+ buffering scheme in which, after ` select() ` (or a similar API) signals a file
1395+ descriptor is ready to write, the next ` O_NONBLOCK ` ` write() ` /` sendmsg() ` on
1396+ that file descriptor copies to an internal buffer and suceeds, issuing an
1397+ ` async ` ` stream.write ` in the background and waiting for completion before
1398+ signalling readiness again. Note that buffering only occurs when streaming
1399+ between two components using non-blocking I/O; if either side is the host or a
1400+ component using blocking or completion-based I/O, no buffering is necessary.
13801401
13811402Given the above, we can define the ` {Readable,Writable}StreamEnd ` classes that
13821403are actually stored in the ` waitables ` table. The classes are almost entirely
0 commit comments