How to impl an async loop app?

I want impl a event-loop app base on FuturesUnordered


use futures::{self, stream::FuturesUnordered,  StreamExt};

type Async = Pin<Box< /** ? */>>;
struct RuntimeState {
    pub pending_ops: FuturesUnordered<Async>
}

fn main () {

    let state = RuntimeState {
        pending_ops: FuturesUnordered::new()
    }

    loop { // here start loop op in pending_ops

        poll_fn((cx)=>{
            let task = state.pending_ops.take(1)
            
            task(state).poll_next_unpin(cx);
        })

    }

}

fn some_async_fn(state){

    // I want push an item into pending_ops like this 
    state.pending_ops.push(Box::pin(async move |state: &mut RuntimeState| -> anyhow::Result<()> {

        // some async ops

    }))

}

I don't know how to complete it

The structure of the rest of the program suggests that the type Async you want is:

type Async = Pin<Box<dyn Future<Output = anyhow::Result<()>> + 'static>>;

This is such a common type to want that futures has a type alias for it which lets you avoid writing those four nested <>:

use futures::future::BoxFuture;

type Async = BoxFuture<'static, anyhow::Result<()>>;

These are not quite the same, because BoxFuture also requires the future implement Send, for use in multithreaded executors. If that's undesired, use futures::future::LocalBoxFuture instead.

no , the Async should record a function , because it will be receive an argument.

Ah, then the type you want to fill in

type Async = Box<
    dyn for<'a> FnOnce(&'a mut RuntimeState) 
        -> BoxFuture<'a, anyhow::Result<()>>
>;

but you cannot actually use that. If the RuntimeState owns the tasks, then the tasks cannot have an &mut RuntimeState — that would be a self-referential borrow, and also even if that were possible, only one task could exist at a time since the &mut RuntimeState is unique.

You need to avoid both &mut and use interior mutability, and probably Arc too — like how tokio has the Handle type which is cloneable and lets you spawn tasks in a Runtime. That way, each task can have its own handle to whatever state you want them to share.

what mean is the for keyword, It's the first time I've seen this kind of writing

Is it possible to remove the task from the pending_ops to execute it, thereby dereferencing the state, and then restore the future if it is not ready?

These are the same:

dyn for<'a> FnOnce(&'a mut RuntimeState) -> BoxFuture<'a, anyhow::Result<()>>
dyn FnOnce(&mut RuntimeState) -> BoxFuture<'_, anyhow::Result<()>>

It's a higher-ranked type that implements

FnOnce(&'a mut RuntimeState) -> BoxFuture<'a, anyhow::Result<()>>

for every lifetime 'a.

See also.

You need to spell out the for<'a, 'b, ..> .. when

  • not using fn pointers or the Fn traits (which have special sugar)
  • elision doesn't mean what you want
    • i.e. the same cases where you have to name lifetimes when writing a function declaration
1 Like

If I understand correctly, you're hoping that while the task is not using the RuntimeState then you can have the task be not currently borrowing it. That is not possible, because the task might yield while in the middle of using the &mut RuntimeState. The closest you can get is to use interior mutability: let the task have access to something that contains a Mutex<RuntimeState> or similar, so that the task can temporarily get the &mut. But it will be better if you never give the task a &mut RuntimeState at all because that is very powerful, allowing totally replacing the state; instead, offer it a handle with only the operations that make sense for individual tasks to do. Then with that narrower interface, you might find some even better strategy for state management than a Mutex.

sorry, this is very simplified code, It may not be accurately stated.
in fact,I‘m trying to build a JsRuntime base rusty_v8. now, it's still a demo

Before I successful impl the event loop by borrow javascript code to record a callback, and pass the index to rust code. I‘m not sure this is a correct behavior or not.
Now, I want impl the event loop by pure ruct code base the future, then I trigger the issue , I don't no how to fix it. can you help me to resolve it

my code repo: GitHub - ofzo/edon: A sample Tsruntime implementation by rust with tokio
on master branch it can build normally and run the async code

on fixup/native-async branch, it can build successful, but it will throw borrowed error like this

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.