mod executor {
use std::future::Future;
use std::sync::Arc;
use std::task::{Context, Poll, Wake};
use std::thread::{self, Thread};
struct ThreadWaker(Thread);
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
}
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
let mut fut = Box::pin(fut);
let t = thread::current();
let waker = Arc::new(ThreadWaker(t)).into();
let mut cx = Context::from_waker(&waker);
loop {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(res) => return res,
Poll::Pending => thread::park(),
}
}
}
}
#[test]
fn main() {
i_slint_backend_testing::init_integration_test_with_mock_time();
std::thread::spawn(|| {
assert_eq!(
slint::spawn_local(async {
panic!("the future shouldn't be run since we're in a thread")
})
.map(drop),
Err(slint::EventLoopError::NoEventLoopProvider)
);
})
.join()
.unwrap();
slint::invoke_from_event_loop(|| {
let handle = slint::spawn_local(async { String::from("Hello") }).unwrap();
slint::spawn_local(async move { panic!("Aborted task") }).unwrap().abort();
let handle2 = slint::spawn_local(async move { handle.await + ", World" }).unwrap();
std::thread::spawn(move || {
let x = executor::block_on(handle2);
assert_eq!(x, "Hello, World");
});
})
.unwrap();
slint::invoke_from_event_loop(|| {
let handle_one = slint::spawn_local(async { "Hello, World!" }).unwrap();
assert!(!handle_one.is_finished());
let handle_two = slint::spawn_local(async move {
assert!(handle_one.is_finished());
})
.unwrap();
std::thread::spawn(move || {
let _ = executor::block_on(handle_two);
});
slint::quit_event_loop().unwrap();
})
.unwrap();
slint::run_event_loop().unwrap();
}
#[test]
fn with_context() {
use i_slint_core::SlintContext;
let ctx = SlintContext::new(Box::new(i_slint_backend_testing::TestingBackend::new(
i_slint_backend_testing::TestingBackendOptions {
mock_time: true,
threading: true,
..Default::default()
},
)));
let handle = ctx.spawn_local(async { String::from("Hello") }).unwrap();
ctx.spawn_local(async move { panic!("Aborted task") }).unwrap().abort();
let handle2 = ctx.spawn_local(async move { handle.await + ", World" }).unwrap();
let proxy = ctx.event_loop_proxy().unwrap();
std::thread::spawn(move || {
let x = executor::block_on(handle2);
assert_eq!(x, "Hello, World");
proxy.quit_event_loop().unwrap();
});
ctx.run_event_loop().unwrap()
}
mod terminating_backend {
use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use slint::platform::{EventLoopProxy, Platform, PlatformError, WindowAdapter};
enum Event {
Quit,
Invoke(Box<dyn FnOnce() + Send>),
}
#[derive(Clone, Default)]
pub struct Backend {
queue: Arc<Mutex<VecDeque<Event>>>,
terminated: Arc<AtomicBool>,
}
impl EventLoopProxy for Backend {
fn quit_event_loop(&self) -> Result<(), slint::EventLoopError> {
self.queue.lock().unwrap().push_back(Event::Quit);
Ok(())
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), slint::EventLoopError> {
if self.terminated.load(Ordering::Relaxed) {
return Err(slint::EventLoopError::EventLoopTerminated);
}
self.queue.lock().unwrap().push_back(Event::Invoke(event));
Ok(())
}
}
impl Platform for Backend {
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
Err(PlatformError::Unsupported)
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
Some(Box::new(self.clone()))
}
fn run_event_loop(&self) -> Result<(), PlatformError> {
while let Some(event) = self.queue.lock().unwrap().pop_front() {
match event {
Event::Quit => break,
Event::Invoke(event) => event(),
}
}
self.terminated.store(true, Ordering::Relaxed);
Ok(())
}
}
}
#[test]
fn wake_after_event_loop_terminated() {
use i_slint_core::SlintContext;
use std::sync::{Arc, Mutex};
use std::task::{Poll, Waker};
let ctx = SlintContext::new(Box::new(terminating_backend::Backend::default()));
let waker: Arc<Mutex<Option<Waker>>> = Arc::default();
let waker_clone = waker.clone();
let _handle = ctx
.spawn_local(std::future::poll_fn(move |cx| -> Poll<()> {
*waker_clone.lock().unwrap() = Some(cx.waker().clone());
Poll::Pending
}))
.unwrap();
ctx.event_loop_proxy().unwrap().quit_event_loop().unwrap();
ctx.run_event_loop().unwrap();
waker.lock().unwrap().take().unwrap().wake();
}