Description
Currently as-specified it's required that for an open stream/future that an error is provided when dropping it, I believe for both the reader and the writer end. (please correct me if I'm wrong about this though). In other words this test currently passes:
async def test_dropping_future_ends():
inst = ComponentInstance()
lower_opts = mk_opts()
host_ft1 = FuncType([FutureType(U8Type()), BoolType()],[])
async def host_func(task, on_start, on_return, on_block):
[future, close_now] = on_start()
if close_now:
future.close(None)
on_return([])
lift_opts = mk_opts()
async def core_func(task, args):
assert(not args)
[wfi] = await canon_future_new(U8Type(), task)
[] = await canon_lower(lower_opts, host_ft1, host_func, task, [wfi, 1])
[] = await canon_future_close_writable(U8Type(), task, wfi, 0)
[wfi] = await canon_future_new(U8Type(), task)
[] = await canon_lower(lower_opts, host_ft1, host_func, task, [wfi, 0])
try:
[] = await canon_future_close_writable(U8Type(), task, wfi, 0)
except Trap:
return []
assert(False)
await canon_lift(lift_opts, inst, FuncType([],[]), core_func, None, lambda:[], lambda _:(), Task.sync_on_block)
Here core_func
created two futures and closed two futures. In both cases it provides no error context but it wasn't required to in the first place because host_func
closed the future, but it's required to in the second case bcause host_func
didn't close the future.
This to me seems like it can create a hazard for composing components together. Whether or not you're allowed to close a future/stream without an error-context is dependent on what the other end has done in some other component. If another end closed prematurely you're allowed to close without an error, but if one day that other end is refactored to keep the end open a little longer than all of a sudden the original caller will break since it should have provided an error-context but forgot to.
I was talking a bit about this with @vados-cosmonic and @dicej today and it was brought up that whether or not an error-context is required at all has design implications on WASI APIs (e.g. future<option<T>>
if you're required to close-with-error vs future<T>
if close-with-no-error is an option).
Overall, assuming my understanding here is right in that close-with-error is dependent on what the other side has done, I'm curious about futures/streams/error-context in a few different ways:
- One possible fix to this is to maintain state for each half of the future/stream so a components requirement to close-with-error is purely local to the component, not dependent on what the other end is doing. I'm personally not a fan of adding more infrastructure in this regard.
- Ideally I would prefer to drop the close-requires-error requirement altogether. I'm not familiar with the original motivation, however, so I'm basically curious to learn up more on that.
- Is it in theory possible to remove
error-context
entirely? Or defer it to a future WIT extension to a new type? If the above "ideal bullet" for me is done then if it's optional to close-on-error, then it seems like allfuture<T>
is roughly equivalent tofuture<result<T, error-context>>
except in a slightly more complicated manner. In such a situation I'd prefer to drop the built-in integration of futures/streams witherror-context
entirely and leave that up to WIT-level APIs to definefuture<result<T, error-context>>
. (or something like that)