sustrik / libdill

Structured concurrency in C
MIT License
1.68k stars 156 forks source link

'choose' only works with handles from chmake #134

Closed saltzm closed 6 years ago

saltzm commented 6 years ago

Been using libdill for a little while and I'm really enjoying it. I just got around to trying choose for the first time and realized that it doesn't work with handles created from crlf_accept, tcp_accept, or anything that's not created directly from chmake (their hquery functions don't accept dill_chan_type and so set errno = ENOTSUP). Digging into the implementation it seems like this is because, rather than using any kind of virtual function call or function pointer, the CHSEND and CHRECV ops are hardcoded to just use the simple memcpy that's used for chsend and chrecv.

My use case is to exec a child coroutine, and I need to listen for a 'cancel' from the client (which is currently just using a crlf handle) while the child executes, and if I receive a cancel before the child executes, I want to call hclose (and do some other stuff) on the child coro, but if the child finishes first, then I want to finish normally and ignore the client. My original idea was that I would use choose to listen to both the client and a handle that's shared with the child, and the child signals on that handle when it's finished. That way I can easily see which happens first. Something like:

struct chclause waiting[] = {
    {CHRECV, client_ch, &msg, sizeof(msg)}, 
    {CHRECV, child_ch, &child_rc, sizeof(child_rc)}};
int recvd = choose(waiting, 2, -1);
switch (recvd) {
    case 0: 
         // Check message, errno, if everything looks right, then cancel child
         break;
    case 1:
         // Finish happily
    default:
}

The immediate workaround that comes to mind is to create a separate coroutine listen_for_cancel that would use mrecv to wait for the client and then have listen_for_cancel signal the parent using a normal channel created with chmake, and then I could use choose to listen between the listener coro and the executing child coro.

It's not a very difficult workaround, so it might not be worth changing the choose implementation, but I was wondering if there was something I'm misunderstanding or if I'm understanding correctly and this is just a design choice. Would it be worth adding a choose alternative that could use some kind of virtual function call (or even a function pointer passed as a parameter) for handling the incoming (or outgoing) message?

saltzm commented 6 years ago

As a side note, is there a better way for a child coroutine to signal completion to a parent? I've just been passing a handle to the child that the child can send a message to (or hdone or whatever, I suppose) when it's done executing.

saltzm commented 6 years ago

I realize now I was conflating 'channel' with 'handle', thinking about a channel as any handle you can send and receive from. But I realized 'channel' is only used to refer to things created with chmake. So this makes more sense now. I still think it could make sense to have a more generic version of choose.