Skip to content

Commit 606cd3d

Browse files
committed
Relax the temporary same-instance copying limitation
1 parent a0c6dac commit 606cd3d

File tree

4 files changed

+48
-30
lines changed

4 files changed

+48
-30
lines changed

design/mvp/CanonicalABI.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ remains blocked:
14501450
self.set_pending(inst, dst_buffer, on_copy, on_copy_done)
14511451
else:
14521452
assert(self.t == dst_buffer.t == self.pending_buffer.t)
1453-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
1453+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
14541454
if self.pending_buffer.remain() > 0:
14551455
if dst_buffer.remain() > 0:
14561456
n = min(dst_buffer.remain(), self.pending_buffer.remain())
@@ -1462,11 +1462,12 @@ remains blocked:
14621462
self.set_pending(inst, dst_buffer, on_copy, on_copy_done)
14631463
```
14641464
Currently, there is a trap when both the `read` and `write` come from the same
1465-
component instance and there is a non-empty element type. This trap will be
1466-
removed in a subsequent release; the reason for the trap is that when lifting
1467-
and lowering can alias the same memory, interleavings can be complex and must
1468-
be handled carefully. Future improvements to the Canonical ABI ([lazy lowering])
1469-
can greatly simplify this interleaving and be more practical to implement.
1465+
component instance and there is a non-empty, non-number element type. This trap
1466+
will be removed in a subsequent release; the reason for the trap is that when
1467+
lifting and lowering can alias the same memory, interleavings can be complex
1468+
and must be handled carefully. Future improvements to the Canonical ABI ([lazy
1469+
lowering]) can greatly simplify this interleaving and be more practical to
1470+
implement.
14701471

14711472
The `write` method implements `WritableStream.write` and is called by the
14721473
`stream.write` built-in (noting that the host cannot be passed the writable end
@@ -1483,7 +1484,7 @@ pending:
14831484
self.set_pending(inst, src_buffer, on_copy, on_copy_done)
14841485
else:
14851486
assert(self.t == src_buffer.t == self.pending_buffer.t)
1486-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
1487+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
14871488
if self.pending_buffer.remain() > 0:
14881489
if src_buffer.remain() > 0:
14891490
n = min(src_buffer.remain(), self.pending_buffer.remain())
@@ -1506,6 +1507,15 @@ notifying the reader end and allowing it to rendezvous with a non-zero-length
15061507
`read` and make progress. See the [stream readiness] section in the async
15071508
explainer for more background on purpose of zero-length reads and writes.
15081509

1510+
The `none_or_number_type` predicate used above includes both the integer and
1511+
floating point number types:
1512+
```python
1513+
def none_or_number_type(t):
1514+
return t is None or isinstance(t, U8Type | U16Type | U32Type | U64Type |
1515+
S8Type | S16Type | S32Type | S64Type |
1516+
F32Type | F64Type)
1517+
```
1518+
15091519
The two ends of a stream are stored as separate elements in the component
15101520
instance's table and each end has a separate `CopyState` that reflects what
15111521
*that end* is currently doing or has done. This `state` field is factored
@@ -1649,7 +1659,7 @@ end was dropped before receiving a value.
16491659
if not self.pending_buffer:
16501660
self.set_pending(inst, dst_buffer, on_copy_done)
16511661
else:
1652-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
1662+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
16531663
dst_buffer.write(self.pending_buffer.read(1))
16541664
self.reset_and_notify_pending(CopyResult.COMPLETED)
16551665
on_copy_done(CopyResult.COMPLETED)
@@ -1661,14 +1671,14 @@ end was dropped before receiving a value.
16611671
elif not self.pending_buffer:
16621672
self.set_pending(inst, src_buffer, on_copy_done)
16631673
else:
1664-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
1674+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
16651675
self.pending_buffer.write(src_buffer.read(1))
16661676
self.reset_and_notify_pending(CopyResult.COMPLETED)
16671677
on_copy_done(CopyResult.COMPLETED)
16681678
```
16691679
As with streams, the `# temporary` limitation shown above is that a future
16701680
cannot be read and written from the same component instance when it has a
1671-
non-empty value type.
1681+
non-empty, non-number value type.
16721682

16731683
Lastly, the `{Readable,Writable}FutureEnd` classes are mostly symmetric with
16741684
`{Readable,Writable}StreamEnd`, with the only difference being that

design/mvp/Concurrency.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,9 @@ signalled by performing a `0`-length read or write (see the [Stream State]
411411
section in the Canonical ABI explainer for details).
412412

413413
As a temporary limitation, if a `read` and `write` for a single stream or
414-
future occur from within the same component and the element type is non-empty,
415-
there is a trap. In the future this limitation will be removed.
414+
future occur from within the same component and the element type is a
415+
non-empty, non-number type, there is a trap. In the future this limitation will
416+
be removed.
416417

417418
The `T` element type of streams and futures is optional, such that `future` and
418419
`stream` can be written in WIT without a trailing `<T>`. In this case, the

design/mvp/canonical-abi/definitions.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ def read(self, inst, dst_buffer, on_copy, on_copy_done):
851851
self.set_pending(inst, dst_buffer, on_copy, on_copy_done)
852852
else:
853853
assert(self.t == dst_buffer.t == self.pending_buffer.t)
854-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
854+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
855855
if self.pending_buffer.remain() > 0:
856856
if dst_buffer.remain() > 0:
857857
n = min(dst_buffer.remain(), self.pending_buffer.remain())
@@ -869,7 +869,7 @@ def write(self, inst, src_buffer, on_copy, on_copy_done):
869869
self.set_pending(inst, src_buffer, on_copy, on_copy_done)
870870
else:
871871
assert(self.t == src_buffer.t == self.pending_buffer.t)
872-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
872+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
873873
if self.pending_buffer.remain() > 0:
874874
if src_buffer.remain() > 0:
875875
n = min(src_buffer.remain(), self.pending_buffer.remain())
@@ -882,6 +882,11 @@ def write(self, inst, src_buffer, on_copy, on_copy_done):
882882
self.reset_and_notify_pending(CopyResult.COMPLETED)
883883
self.set_pending(inst, src_buffer, on_copy, on_copy_done)
884884

885+
def none_or_number_type(t):
886+
return t is None or isinstance(t, U8Type | U16Type | U32Type | U64Type |
887+
S8Type | S16Type | S32Type | S64Type |
888+
F32Type | F64Type)
889+
885890
class CopyState(Enum):
886891
IDLE = 1
887892
SYNC_COPYING = 2
@@ -983,7 +988,7 @@ def read(self, inst, dst_buffer, on_copy_done):
983988
if not self.pending_buffer:
984989
self.set_pending(inst, dst_buffer, on_copy_done)
985990
else:
986-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
991+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
987992
dst_buffer.write(self.pending_buffer.read(1))
988993
self.reset_and_notify_pending(CopyResult.COMPLETED)
989994
on_copy_done(CopyResult.COMPLETED)
@@ -995,7 +1000,7 @@ def write(self, inst, src_buffer, on_copy_done):
9951000
elif not self.pending_buffer:
9961001
self.set_pending(inst, src_buffer, on_copy_done)
9971002
else:
998-
trap_if(inst is self.pending_inst and self.t is not None) # temporary
1003+
trap_if(inst is self.pending_inst and not none_or_number_type(self.t)) # temporary
9991004
self.pending_buffer.write(src_buffer.read(1))
10001005
self.reset_and_notify_pending(CopyResult.COMPLETED)
10011006
on_copy_done(CopyResult.COMPLETED)

design/mvp/canonical-abi/run_tests.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,46 +2476,46 @@ def on_resolve(results):
24762476
assert(got[0] == 42)
24772477

24782478

2479-
def test_self_empty():
2479+
def test_self_copy(elemt):
24802480
store = Store()
24812481
inst = ComponentInstance(store)
2482-
mem = bytearray(24)
2482+
mem = bytearray(40)
24832483
sync_opts = mk_opts(memory=mem, async_=False)
24842484
async_opts = mk_opts(memory=mem, async_=True)
24852485

24862486
ft = FuncType([],[])
24872487
def core_func(thread, args):
24882488
[seti] = canon_waitable_set_new(thread)
24892489

2490-
[packed] = canon_future_new(FutureType(None), thread)
2490+
[packed] = canon_future_new(FutureType(elemt), thread)
24912491
rfi,wfi = unpack_new_ends(packed)
24922492

2493-
[ret] = canon_future_write(FutureType(None), async_opts, thread, wfi, 0xdeadbeef)
2493+
[ret] = canon_future_write(FutureType(elemt), async_opts, thread, wfi, 0)
24942494
assert(ret == definitions.BLOCKED)
24952495

2496-
[ret] = canon_future_read(FutureType(None), async_opts, thread, rfi, 0xdeadbeef)
2496+
[ret] = canon_future_read(FutureType(elemt), async_opts, thread, rfi, 0)
24972497
assert(ret == CopyResult.COMPLETED)
2498-
[] = canon_future_drop_readable(FutureType(None), thread, rfi)
2498+
[] = canon_future_drop_readable(FutureType(elemt), thread, rfi)
24992499

25002500
[] = canon_waitable_join(thread, wfi, seti)
25012501
[event] = canon_waitable_set_wait(True, mem, thread, seti, 0)
25022502
assert(event == EventCode.FUTURE_WRITE)
25032503
assert(mem[0] == wfi)
25042504
assert(mem[4] == CopyResult.COMPLETED)
2505-
[] = canon_future_drop_writable(FutureType(None), thread, wfi)
2505+
[] = canon_future_drop_writable(FutureType(elemt), thread, wfi)
25062506

2507-
[packed] = canon_stream_new(StreamType(None), thread)
2507+
[packed] = canon_stream_new(StreamType(elemt), thread)
25082508
rsi,wsi = unpack_new_ends(packed)
2509-
[ret] = canon_stream_write(StreamType(None), async_opts, thread, wsi, 10000, 3)
2509+
[ret] = canon_stream_write(StreamType(elemt), async_opts, thread, wsi, 0, 3)
25102510
assert(ret == definitions.BLOCKED)
25112511

2512-
[ret] = canon_stream_read(StreamType(None), async_opts, thread, rsi, 2000, 1)
2512+
[ret] = canon_stream_read(StreamType(elemt), async_opts, thread, rsi, 0, 1)
25132513
result,n = unpack_result(ret)
25142514
assert(n == 1 and result == CopyResult.COMPLETED)
2515-
[ret] = canon_stream_read(StreamType(None), async_opts, thread, rsi, 2000, 4)
2515+
[ret] = canon_stream_read(StreamType(elemt), async_opts, thread, rsi, 0, 4)
25162516
result,n = unpack_result(ret)
25172517
assert(n == 2 and result == CopyResult.COMPLETED)
2518-
[] = canon_stream_drop_readable(StreamType(None), thread, rsi)
2518+
[] = canon_stream_drop_readable(StreamType(elemt), thread, rsi)
25192519

25202520
[] = canon_waitable_join(thread, wsi, seti)
25212521
[event] = canon_waitable_set_wait(True, mem, thread, seti, 0)
@@ -2524,7 +2524,7 @@ def core_func(thread, args):
25242524
result,n = unpack_result(mem[4])
25252525
assert(result == CopyResult.DROPPED)
25262526
assert(n == 3)
2527-
[] = canon_stream_drop_writable(StreamType(None), thread, wsi)
2527+
[] = canon_stream_drop_writable(StreamType(elemt), thread, wsi)
25282528

25292529
[] = canon_waitable_set_drop(thread, seti)
25302530
return []
@@ -2743,7 +2743,9 @@ def core_consumer(thread, args):
27432743
test_cancel_copy()
27442744
test_futures()
27452745
test_cancel_subtask()
2746-
test_self_empty()
2746+
test_self_copy(None)
2747+
test_self_copy(U8Type())
2748+
test_self_copy(F64Type())
27472749
test_async_flat_params()
27482750
test_threads()
27492751
test_thread_cancel_callback()

0 commit comments

Comments
 (0)