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.
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