Await on join handle inside async?

I have a video player that plays a stream and should return whenever the video producer, the decoder or the renderer fails

pub async fn play_video(s: &MyStream) -> Result<(), Error> {
    tokio::select! {
        r = s.video_producer.run() => {
            Ok(r?)
        }
        r = s.decoder.run() => {
            Ok(r?)
        }
        //Renderer would go here, but it can't cause it's not `Send`
    }
}

However, since a Renderer usually has to be on a thread and not move to another, it's not Send, so it cannot be used inside an async call.

My solution was to spawn a thread and create the renderer inside the thread, so it never has to be moved from threads:

let renderer_join_handle = std::thread::spawn(move || {
    //...
})

I get a JoinHandle<Result<(),RendererError>> for which I can detect errors and return. However, it's not like the tokio select where it returns immediately. Is there a way to continuously poll this JoinHandle in the tokio select, thus async? Or is there a better solution that I'm not aware?

Basically I wanted the Renderer to be like the video producer and the decoder, and return its error on the tokio select.

I can't comment on whether or not your approach is the most appropriate one. With regards to making your attempted solution work: Waiting for a spawned thread in an async context is not possible directly through the thread's JoinHandle. You can however give the thread a oneshot channel and have it return its result or error when it's finished through that channel.

The receiving end is a future that will be completed once the result has been sent or the sender has been dropped (the latter would happen if the thread panicked).

There's also spawn_blocking which might be an appropriate alternative to an ordinary std::thread::spawn.

1 Like

If you need this for a non-Tokio thread or task, then use a tokio::sync::oneshot channel to wait for its completion instead of using the join handle.

any reason to use tokio thread spawn instead of a simple thread spawn? There's no tokio context yet when I launch the renderer thread, so a std::thread would be easier

I believe the reason it was suggested is that spawn_blocking provides a join handle that can be used from async code.