@@ -415,8 +415,8 @@ class BufferGuestImpl(Buffer):
415
415
length: int
416
416
417
417
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 :
420
420
trap_if(ptr != align_to(ptr, alignment(t)))
421
421
trap_if(ptr + length * elem_size(t) > len (cx.opts.memory))
422
422
self .cx = cx
@@ -1299,10 +1299,13 @@ class ReadableStreamGuestImpl(ReadableStream):
1299
1299
self .reset_pending()
1300
1300
1301
1301
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
1306
1309
```
1307
1310
If set, the ` pending_* ` fields record the ` Buffer ` and ` On* ` callbacks of a
1308
1311
` 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
1356
1359
if self .closed_:
1357
1360
return ' done'
1358
1361
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)
1363
1363
return ' blocked'
1364
1364
else :
1365
1365
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))
1369
1366
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'
1371
1374
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.
1380
1401
1381
1402
Given the above, we can define the ` {Readable,Writable}StreamEnd ` classes that
1382
1403
are actually stored in the ` waitables ` table. The classes are almost entirely
0 commit comments