@@ -355,7 +355,7 @@ values *into* the buffer's memory. Buffers are represented by the following 3
355
355
abstract Python classes:
356
356
``` python
357
357
class Buffer :
358
- MAX_LENGTH = 2 ** 30 - 1
358
+ MAX_LENGTH = 2 ** 28 - 1
359
359
t: ValType
360
360
remain: Callable[[], int ]
361
361
@@ -1056,7 +1056,7 @@ stream.)
1056
1056
``` python
1057
1057
RevokeBuffer = Callable[[], None ]
1058
1058
OnPartialCopy = Callable[[RevokeBuffer], None ]
1059
- OnCopyDone = Callable[[], None ]
1059
+ OnCopyDone = Callable[[Literal[ ' completed ' , ' cancelled ' ] ], None ]
1060
1060
1061
1061
class ReadableStream :
1062
1062
t: ValType
@@ -1069,7 +1069,8 @@ The key operation is `read` which works as follows:
1069
1069
* ` read ` is non-blocking, returning ` 'blocked' ` if it would have blocked.
1070
1070
* The ` On* ` callbacks are only called * after* ` read ` returns ` 'blocked' ` .
1071
1071
* ` OnCopyDone ` is called to indicate that the caller has regained ownership of
1072
- the buffer.
1072
+ the buffer and whether this was due to the read/write completing or
1073
+ being cancelled.
1073
1074
* ` OnPartialCopy ` is called to indicate a partial write has been made to the
1074
1075
buffer, but there may be further writes made in the future, so the caller
1075
1076
has * not* regained ownership of the buffer.
@@ -1122,21 +1123,21 @@ If set, the `pending_*` fields record the `Buffer` and `On*` callbacks of a
1122
1123
` read ` . Closing the readable or writable end of a stream or cancelling a ` read `
1123
1124
or ` write ` notifies any pending ` read ` or ` write ` via its ` OnCopyDone `
1124
1125
callback, which lets the other side know that ownership of the ` Buffer ` has
1125
- been returned:
1126
+ been returned and why :
1126
1127
``` python
1127
- def reset_and_notify_pending (self ):
1128
+ def reset_and_notify_pending (self , why ):
1128
1129
pending_on_copy_done = self .pending_on_copy_done
1129
1130
self .reset_pending()
1130
- pending_on_copy_done()
1131
+ pending_on_copy_done(why )
1131
1132
1132
1133
def cancel (self ):
1133
- self .reset_and_notify_pending()
1134
+ self .reset_and_notify_pending(' cancelled ' )
1134
1135
1135
1136
def close (self ):
1136
1137
if not self .closed_:
1137
1138
self .closed_ = True
1138
1139
if self .pending_buffer:
1139
- self .reset_and_notify_pending()
1140
+ self .reset_and_notify_pending(' completed ' )
1140
1141
1141
1142
def closed (self ):
1142
1143
return self .closed_
@@ -1180,7 +1181,7 @@ but in the opposite direction. Both are implemented by a single underlying
1180
1181
if self .pending_buffer.remain() > 0 :
1181
1182
self .pending_on_partial_copy(self .reset_pending)
1182
1183
else :
1183
- self .reset_and_notify_pending()
1184
+ self .reset_and_notify_pending(' completed ' )
1184
1185
return ' done'
1185
1186
```
1186
1187
@@ -1241,10 +1242,10 @@ and closing once a value has been read-from or written-to the given buffer:
1241
1242
class FutureEnd (StreamEnd ):
1242
1243
def close_after_copy (self , copy_op , buffer , on_copy_done ):
1243
1244
assert (buffer.remain() == 1 )
1244
- def on_copy_done_wrapper ():
1245
+ def on_copy_done_wrapper (why ):
1245
1246
if buffer.remain() == 0 :
1246
1247
self .stream.close()
1247
- on_copy_done()
1248
+ on_copy_done(why )
1248
1249
ret = copy_op(buffer, on_partial_copy = None , on_copy_done = on_copy_done_wrapper)
1249
1250
if ret == ' done' and buffer.remain() == 0 :
1250
1251
self .stream.close()
@@ -3552,7 +3553,8 @@ multiple partial copies before having to context-switch back.
3552
3553
``` python
3553
3554
if opts.sync:
3554
3555
final_revoke_buffer = None
3555
- def on_partial_copy (revoke_buffer ):
3556
+ def on_partial_copy (revoke_buffer , why = ' completed' ):
3557
+ assert (why == ' completed' )
3556
3558
nonlocal final_revoke_buffer
3557
3559
final_revoke_buffer = revoke_buffer
3558
3560
if not async_copy.done():
@@ -3563,6 +3565,8 @@ multiple partial copies before having to context-switch back.
3563
3565
await task.wait_on(async_copy, sync = True )
3564
3566
final_revoke_buffer()
3565
3567
```
3568
+ (When non-cooperative threads are added, the assertion that synchronous copies
3569
+ can only be ` completed ` , and not ` cancelled ` , will no longer hold.)
3566
3570
3567
3571
In the asynchronous case, the ` on_* ` callbacks set a pending event on the
3568
3572
` Waitable ` which will be delivered to core wasm when core wasm calls
@@ -3573,36 +3577,46 @@ allowing multiple partial copies to complete in the interim, reducing overall
3573
3577
context-switching overhead.
3574
3578
``` python
3575
3579
else :
3576
- def copy_event (revoke_buffer ):
3580
+ def copy_event (why , revoke_buffer ):
3577
3581
revoke_buffer()
3578
3582
e.copying = False
3579
- return (event_code, i, pack_copy_result(task, buffer, e ))
3583
+ return (event_code, i, pack_copy_result(task, e, buffer, why ))
3580
3584
def on_partial_copy (revoke_buffer ):
3581
- e.set_event(partial(copy_event, revoke_buffer))
3582
- def on_copy_done ():
3583
- e.set_event(partial(copy_event, revoke_buffer = lambda :()))
3585
+ e.set_event(partial(copy_event, ' completed ' , revoke_buffer))
3586
+ def on_copy_done (why ):
3587
+ e.set_event(partial(copy_event, why, revoke_buffer = lambda :()))
3584
3588
if e.copy(buffer, on_partial_copy, on_copy_done) != ' done' :
3585
3589
e.copying = True
3586
3590
return [BLOCKED ]
3587
- return [pack_copy_result(task, buffer, e )]
3591
+ return [pack_copy_result(task, e, buffer, ' completed ' )]
3588
3592
```
3589
3593
However the copy completes, the results are reported to the caller via
3590
3594
` pack_copy_result ` :
3591
3595
``` python
3592
- BLOCKED = 0x ffff_ffff
3593
- CLOSED = 0x 8000_0000
3596
+ BLOCKED = 0x ffff_ffff
3597
+ COMPLETED = 0x 0
3598
+ CLOSED = 0x 1
3599
+ CANCELLED = 0x 2
3594
3600
3595
- def pack_copy_result (task , buffer , e ):
3596
- if buffer.progress or not e.stream.closed():
3597
- assert (buffer.progress <= Buffer. MAX_LENGTH < BLOCKED )
3598
- assert ( not (buffer.progress & CLOSED ))
3599
- return buffer.progress
3601
+ def pack_copy_result (task , e , buffer , why ):
3602
+ if e.stream.closed():
3603
+ result = CLOSED
3604
+ elif why == ' cancelled ' :
3605
+ result = CANCELLED
3600
3606
else :
3601
- return CLOSED
3602
- ```
3603
- The order of tests here indicates that, if some progress was made and then the
3604
- stream was closed, only the progress is reported and the ` CLOSED ` status is
3605
- left to be discovered next time.
3607
+ assert (why == ' completed' )
3608
+ assert (not isinstance (e, FutureEnd))
3609
+ result = COMPLETED
3610
+ assert (buffer.progress <= Buffer.MAX_LENGTH < 2 ** 28 )
3611
+ packed = result | (buffer.progress << 4 )
3612
+ assert (packed != BLOCKED )
3613
+ return packed
3614
+ ```
3615
+ The ` result ` indicates whether the stream was closed by the other end, the
3616
+ copy was cancelled by this end (via ` {stream,future}.cancel-{read,write} ` ) or,
3617
+ otherwise, completed successfully. In all cases, any number of elements (from
3618
+ ` 0 ` to ` n ` ) may have * first* been copied into or out of the buffer passed to
3619
+ the ` read ` or ` write ` and so this number is packed into the ` i32 ` result.
3606
3620
3607
3621
3608
3622
### 🔀 ` canon {stream,future}.cancel-{read,write} `
0 commit comments