@@ -791,8 +791,8 @@ counter is used below to record the number of unmet obligations to drop the
791
791
streams and futures connected to this ` Subtask ` .
792
792
``` python
793
793
def drop (self ):
794
- trap_if(self .enqueued)
795
794
trap_if(self .state != CallState.DONE )
795
+ assert (not self .enqueued)
796
796
self .supertask.todo -= 1
797
797
```
798
798
@@ -1058,24 +1058,29 @@ and ensures that streams-of-borrows are dropped before the end of the call,
1058
1058
just like normal ` borrow ` handles.
1059
1059
1060
1060
Given the above logic, the [ readable and writable ends] of a stream can be
1061
- concretely implemented by the following two classes. The ` copy ` , ` cancel_copy `
1062
- and ` drop ` methods are called polymorphically by the common ` async_copy `
1063
- routine shared by the ` stream.read ` and ` stream.write ` built-ins below.
1061
+ concretely implemented by the following two classes. The readable end
1062
+ inherits ` StreamHandle ` 's constructor, which takes an already-created abstract
1063
+ ` ReadableStream ` passed into the component. In contrast, constructing a
1064
+ writable end constructs a fresh ` ReadableStreamGuestImpl ` that will later
1065
+ be given to the readable end paired with this writable end. The ` copy ` ,
1066
+ ` cancel_copy ` and ` drop ` methods are called polymorphically by the common
1067
+ ` async_copy ` routine shared by the ` stream.read ` and ` stream.write ` built-ins
1068
+ below.
1064
1069
``` python
1065
1070
class ReadableStreamHandle (StreamHandle ):
1066
1071
async def copy (self , dst , on_block ):
1067
1072
await self .stream.read(dst, on_block)
1068
1073
async def cancel_copy (self , dst , on_block ):
1069
1074
await self .stream.cancel_read(dst, on_block)
1070
1075
1071
- class WritableStreamHandle (ReadableStreamGuestImpl , StreamHandle ):
1076
+ class WritableStreamHandle (StreamHandle ):
1072
1077
def __init__ (self , t , inst ):
1073
- ReadableStreamGuestImpl. __init__ ( self , inst)
1074
- StreamHandle.__init__ (self , self , t)
1078
+ stream = ReadableStreamGuestImpl( inst)
1079
+ StreamHandle.__init__ (self , stream , t)
1075
1080
async def copy (self , src , on_block ):
1076
- await self .write(src, on_block)
1081
+ await self .stream. write(src, on_block)
1077
1082
async def cancel_copy (self , src , on_block ):
1078
- await self .cancel_write(src, on_block)
1083
+ await self .stream. cancel_write(src, on_block)
1079
1084
```
1080
1085
1081
1086
Given the above definitions of how ` stream ` works, a ` future ` can simply be
@@ -1097,24 +1102,24 @@ class ReadableFutureHandle(FutureHandle):
1097
1102
if dst.remain() == 0 :
1098
1103
self .stream.close()
1099
1104
1100
- class WritableFutureHandle (ReadableStreamGuestImpl , FutureHandle ):
1105
+ class WritableFutureHandle (FutureHandle ):
1101
1106
def __init__ (self , t , inst ):
1102
- ReadableStreamGuestImpl. __init__ ( self , inst)
1103
- FutureHandle.__init__ (self , self , t)
1107
+ stream = ReadableStreamGuestImpl( inst)
1108
+ FutureHandle.__init__ (self , stream , t)
1104
1109
1105
1110
async def copy (self , src , on_block ):
1106
1111
assert (src.remain() == 1 )
1107
- await self .write(src, on_block)
1112
+ await self .stream. write(src, on_block)
1108
1113
if src.remain() == 0 :
1109
- self .close()
1114
+ self .stream. close()
1110
1115
1111
1116
async def cancel_copy (self , src , on_block ):
1112
1117
await self .cancel_write(src, on_block)
1113
1118
if src.remain() == 0 :
1114
- self .close()
1119
+ self .stream. close()
1115
1120
1116
1121
def drop (self ):
1117
- trap_if(not self .closed())
1122
+ trap_if(not self .stream. closed())
1118
1123
FutureHandle.drop(self )
1119
1124
```
1120
1125
The overridden ` WritableFutureHandle.drop ` method traps if the internal stream
@@ -2061,12 +2066,13 @@ def lower_future(cx, v, t):
2061
2066
2062
2067
def lower_async_value (ReadableHandleT , WritableHandleT , cx , v , t ):
2063
2068
assert (isinstance (v, ReadableStream))
2064
- if isinstance (v, WritableHandleT ) and cx.inst is v.impl:
2065
- i = cx.inst.waitables.array.index(v)
2066
- assert (v .paired)
2067
- v .paired = False
2069
+ if isinstance (v, ReadableStreamGuestImpl ) and cx.inst is v.impl:
2070
+ [h] = [h for h in cx.inst.waitables.array if h and h.stream is v]
2071
+ assert (h .paired)
2072
+ h .paired = False
2068
2073
if contains_borrow(t):
2069
- v.borrow_scope = None
2074
+ h.borrow_scope = None
2075
+ i = cx.inst.waitables.array.index(h)
2070
2076
assert (2 ** 31 > Table.MAX_LENGTH >= i)
2071
2077
return i | (2 ** 31 )
2072
2078
else :
@@ -2091,12 +2097,14 @@ it itself holds the `WritableStreamHandle` for. Without specially handling
2091
2097
this case, this would lead to copies from a single linear memory into itself
2092
2098
which is both inefficient and raises subtle semantic interleaving questions
2093
2099
that we would rather avoid. To avoid both, this case is detected and the
2094
- ` ReadableStream ` is "unwrapped" to writable handle, returning the existing
2100
+ ` ReadableStream ` is "unwrapped" to the writable handle, returning the existing
2095
2101
index of it in the ` waitables ` table, setting the high bit to signal this fact
2096
2102
to guest code. Guest code must therefore handle this special case by
2097
2103
collapsing the two ends of the stream to work fully without guest code (since
2098
2104
the Canonical ABI is now wholly unnecessary to pass values from writer to
2099
- reader).
2105
+ reader). The O(N) searches through the ` waitables ` table are expected to be
2106
+ optimized away by instead storing a pointer or index of the writable handle in
2107
+ the stream itself (alongside the ` impl ` field).
2100
2108
2101
2109
2102
2110
### Flattening
@@ -2969,6 +2977,27 @@ execute, however tasks in *other* component instances may execute. This allows
2969
2977
a long-running task in one component to avoid starving other components
2970
2978
without needing support full reentrancy.
2971
2979
2980
+ ### 🔀 ` canon subtask.drop `
2981
+
2982
+ For a canonical definition:
2983
+ ``` wasm
2984
+ (canon subtask.drop (core func $f))
2985
+ ```
2986
+ validation specifies:
2987
+ * ` $f ` is given type ` (func (param i32)) `
2988
+
2989
+ Calling ` $f ` removes the subtask at the given index from the current
2990
+ component instance's ` watiable ` table, performing the guards and bookkeeping
2991
+ defined by ` Subtask.drop() ` .
2992
+ ``` python
2993
+ async def canon_subtask_drop (task , i ):
2994
+ trap_if(not task.inst.may_leave)
2995
+ h = task.inst.waitables.remove(i)
2996
+ trap_if(not isinstance (h, Subtask))
2997
+ h.drop()
2998
+ return []
2999
+ ```
3000
+
2972
3001
### 🔀 ` canon {stream,future}.new `
2973
3002
2974
3003
For canonical definitions:
@@ -3177,22 +3206,41 @@ new `waitable` element allocated; the cancellation is simply reported as a
3177
3206
normal ` {STREAM,FUTURE}_{READ,WRITE} ` event by the original, now-unblocked
3178
3207
` read ` or ` write ` .
3179
3208
3180
- ### 🔀 ` canon waitable.drop `
3209
+ ### 🔀 ` canon {stream,future}.close-{readable,writable} `
3181
3210
3182
- For a canonical definition :
3211
+ For canonical definitions :
3183
3212
``` wasm
3184
- (canon waitable.drop (core func $f))
3213
+ (canon stream.close-readable $t (core func $f))
3214
+ (canon stream.close-writable $t (core func $f))
3215
+ (canon future.close-readable $t (core func $f))
3216
+ (canon future.close-writable $t (core func $f))
3185
3217
```
3186
3218
validation specifies:
3187
3219
* ` $f ` is given type ` (func (param i32)) `
3188
3220
3189
- Calling ` $f ` removes the indicated waitable (subtask, stream or future) from
3190
- the instance's table, trapping if various conditions aren't met in the
3191
- waitable's ` drop() ` method.
3221
+ Calling ` $f ` removes the readable or writable end of the stream or future at
3222
+ the given index from the current component instance's ` waitable ` table,
3223
+ performing the guards and bookkeeping defined by
3224
+ ` {Readable,Writable}{Stream,Future}Handle.drop() ` above.
3192
3225
``` python
3193
- async def canon_waitable_drop (task , i ):
3226
+ async def canon_stream_close_readable (t , task , i ):
3227
+ return await close_async_value(ReadableStreamHandle, t, task, i)
3228
+
3229
+ async def canon_stream_close_writable (t , task , i ):
3230
+ return await close_async_value(WritableStreamHandle, t, task, i)
3231
+
3232
+ async def canon_future_close_readable (t , task , i ):
3233
+ return await close_async_value(ReadableFutureHandle, t, task, i)
3234
+
3235
+ async def canon_future_close_writable (t , task , i ):
3236
+ return await close_async_value(WritableFutureHandle, t, task, i)
3237
+
3238
+ async def close_async_value (HandleT , t , task , i ):
3194
3239
trap_if(not task.inst.may_leave)
3195
- task.inst.waitables.remove(i).drop()
3240
+ h = task.inst.waitables.remove(i)
3241
+ trap_if(not isinstance (h, HandleT))
3242
+ trap_if(h.t != t)
3243
+ h.drop()
3196
3244
return []
3197
3245
```
3198
3246
0 commit comments