logoalt Hacker News

gpderettatoday at 9:59 AM3 repliesview on HN

The essence of the sender/receiver proposal is essentially this: - first start with a sync function

      result_t foo(params_t);
      ...
      auto result = foo(params);
- make it async by adding a continuation:

      void async_foo(params_t params, invokable<result_t> cont) { cont(foo(params)); };
      ...
      invokable<result_t> auot receiver= [](result_t result) {...};
      async_foo(params, receiver);
- then curry it:

      auto curried_async_foo(params_t params) { 
        return [params](invokable<result_t> cont) { 
           async_foo(params, cont);
        };}
      ...
      auto op = curried_async_foo(params);
      op(receiver);
- finally modify the curried variant to add another required evaluation round:

      auto foo_sender(param_t params) {
        return [params](invokable<result_t> cont)  {
           return [params, cont]{ 
              async_foo(params, cont);
        };};}
      ...
      auto sender = foo_sender(params);
      auto operation = sender(receiver);
      operation();
The actual library uses structures with named methods instead of callables (so you would do operation.start() for example), plus a separate continuation for the error path (by having the receiver concept implement two named functions), and cancellation support. Also the final operation is required to be address stable until it is completed.

The complexity is of course in the details, and I didn't fully appreciate it until I tried to implement a stripped down version of the model (and I'm sure I'm still missing a lot).

The model does work very well with coroutines and can avoid or defer a lot of the expected memory allocations of async operations.


Replies

ottoday at 11:16 AM

> can avoid or defer a lot of the expected memory allocations of async operations

Is this true in realistic use cases or only in minimal demos? From what I've seen, as soon as your code is complex enough that you need two compilation units, you need some higher level async abstraction, like coroutines.

And as soon as you have coroutines, you need to type-erase both the senders and the scheduler, so you have at least couple of allocations per continuation.

show 2 replies
fithisuxtoday at 2:29 PM

Very helpful comment

raverbashingtoday at 10:17 AM

Thanks, your comment has explained this better than the article

And it does look like an interesting proposal

show 1 reply