pub fn spawn_local<F: Future + 'static>(
fut: F,
) -> Result<JoinHandle<F::Output>, EventLoopError>
target_has_atomic="ptr"
only.Expand description
Spawns a Future
to execute in the Slint event loop.
This function is intended to be invoked only from the main Slint thread that runs the event loop.
For spawning a Send
future from a different thread, this function should be called from a closure
passed to invoke_from_event_loop()
.
This function is typically called from a UI callback.
§Example
slint::spawn_local(async move {
// your async code goes here
}).unwrap();
§Compatibility with Tokio and other runtimes
The runtime used to execute the future on the main thread is platform-dependent,
for instance, it could be the winit event loop. Therefore, futures that assume a specific runtime
may not work. This may be an issue if you call .await
on a future created by another
runtime, or pass the future directly to spawn_local
.
Futures from the smol runtime always hand off their work to separate I/O threads that run in parallel to the Slint event loop.
The Tokio runtime is subject to the following constraints:
- Tokio futures require entering the context of a global Tokio runtime.
- Tokio futures aren’t guaranteed to hand off their work to separate threads and may therefore not complete, because the Slint runtime can’t drive the Tokio runtime.
- Tokio futures require regular yielding to the Tokio runtime for fairness, a constraint that also can’t be met by Slint.
- Tokio’s current-thread schedule cannot be used in Slint main thread, because Slint cannot yield to it.
To address these constraints, use async_compat’s Compat::new() to implicitly allocate a shared, multi-threaded Tokio runtime that will be used for Tokio futures.
The following little example demonstrates the use of Tokio’s TcpStream
to
read from a network socket. The entire future passed to spawn_local()
is wrapped in Compat::new()
to make it run:
// A dummy TCP server that once reports "Hello World"
use std::io::Write;
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = listener.local_addr().unwrap();
let server = std::thread::spawn(move || {
let mut stream = listener.incoming().next().unwrap().unwrap();
stream.write("Hello World".as_bytes()).unwrap();
});
let slint_future = async move {
use tokio::io::AsyncReadExt;
let mut stream = tokio::net::TcpStream::connect(local_addr).await.unwrap();
let mut data = Vec::new();
stream.read_to_end(&mut data).await.unwrap();
assert_eq!(data, "Hello World".as_bytes());
slint::quit_event_loop().unwrap();
};
// Wrap the future that includes Tokio futures in async_compat's `Compat` to ensure
// presence of a Tokio run-time.
slint::spawn_local(async_compat::Compat::new(slint_future)).unwrap();
slint::run_event_loop_until_quit().unwrap();
server.join().unwrap();
The use of #[tokio::main]
is not recommended. If it’s necessary to use though, wrap the call to enter the Slint
event loop in a call to tokio::task::block_in_place
:
// Wrap the call to run_event_loop to ensure presence of a Tokio run-time.
tokio::task::block_in_place(slint::run_event_loop).unwrap();