cosync icon indicating copy to clipboard operation
cosync copied to clipboard

Borrowed inputs to tasks

Open osa1 opened this issue 2 years ago • 2 comments

I'm trying to store my coroutine in a heap-allocated struct (or just heap-allocate the coroutine) and pass borrowed inputs to it in run_until_stall.

As far as I understand, this is not possible with the current API. The problem is the type parameter of Cosync. If my input has borrowed bits, I need a type parameter like:

struct MyInput<'a> {
    borrowed_bit: &'a MyBorrowedData,
    other_stuff: OtherStuff,
}

That type parameter that propogates into all the references to Cosync:

type MyCoro<'a> = Cosync<MyInput<'a>>;

which means I can't heap-allocate MyCoro: 'static is the only lifetime I can pass to MyCoro, but that's the not lifetime as I will be passing stack-allocated borrowed values to run_until_stall.

I'm not sure how to best solve this problem in Rust. I'm wondering if you know a workaround. To solve this at the API level we would need to somehow avoid the type parameter for the coroutine input in the Cosync type. However if we do that then I don't know how to implement the run_until_stall method as it needs to know the coroutine input type.

Any thoughts on this?


The actual use case: In an app (not a game) I have an event source. Whenever an event arrives my callback is called with & and &mut, and value arguments (passed by the event loop to my callback). This callback is practically a coroutine: it takes an input (event data), it may run an action (or yield a value), or wait for more input. cosyn makes it much simpler to implement than an hand-written coroutine as I need to implement a lot of states manually.

The problem is the callback needs to be heap allocated and passed to the event loop and I don't have control over the event loop code to change this.

osa1 avatar May 15 '22 08:05 osa1

Unfortunately, it's not just that it's not possible in the current api, it's actually an issue in Rust itself.

On a fundamental issue, Cosync should never have a lifetime on it, even if its input has a lifetime on it, in a similar way that:

struct Foo<T: Fn(&mut ())>(T);

is valid rust, and yet internally defers the lifetime of the parameter to the call site. My understanding based on the nomicon here https://doc.rust-lang.org/nomicon/hrtb.html is that the HRTB is magically placed by the compiler.

However, unlike the other examples, Cosync only knows of a T, which might be ?'static. As far as I can tell, it isn't possible to reason about the T that way. I tried for a significant amount of time here on this branch: https://github.com/sanbox-irl/cosync/commits/testing. Unfortunately, I suspect this is one of those things that HRTs will help with, though I'm less a type guy and more a hammer my head against the types till it works guy, so I'm not sure what'll happen there.


Okay, onto your use case itself. Is it possible for you to pass OtherStuff into the stack of the coroutine on creation, rather than as a parameter? It would probably rely on OtherStuff being an Arc<RwLock<T>> kind of thing (or just an Arc), and then only use MyBorrowedData as the parameter.

What do you think?

sanbox-irl avatar May 18 '22 08:05 sanbox-irl

Okay, onto your use case itself. Is it possible for you to pass OtherStuff into the stack of the coroutine on creation, rather than as a parameter? It would probably rely on OtherStuff being an Arc<RwLock<T>> kind of thing (or just an Arc), and then only use MyBorrowedData as the parameter.

What do you think?

I think this would work when I have one borrowed input, but not when I have two (as in my actual use case). When I have two I need to pack them in a struct, and the struct requires lifetime parameters as it has borrowed data. So it cannot be used as CosyncInput type argument.

osa1 avatar May 21 '22 07:05 osa1