Start the LSP thread when the gui thread is ready

This way we can serve preview requests immediately.

This basically makes post_event safe to call before the event loop is entered.
The events will be queued up and sent when the event loop
is created and we have access
to the proxy, which will take over the queue.
This commit is contained in:
Simon Hausmann 2021-04-06 11:38:09 +02:00
parent d9db07db2a
commit b81803774b
4 changed files with 65 additions and 26 deletions

View file

@ -75,8 +75,45 @@ thread_local! {
scoped_tls_hkt::scoped_thread_local!(static CURRENT_WINDOW_TARGET : for<'a> &'a RunningEventLoop<'a>); scoped_tls_hkt::scoped_thread_local!(static CURRENT_WINDOW_TARGET : for<'a> &'a RunningEventLoop<'a>);
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub static GLOBAL_PROXY: once_cell::sync::OnceCell< pub(crate) enum GlobalEventLoopProxyOrEventQueue {
std::sync::Mutex<Option<winit::event_loop::EventLoopProxy<CustomEvent>>>, Proxy(winit::event_loop::EventLoopProxy<CustomEvent>),
Queue(Vec<CustomEvent>),
}
#[cfg(not(target_arch = "wasm32"))]
impl GlobalEventLoopProxyOrEventQueue {
pub(crate) fn send_event(&mut self, event: CustomEvent) {
match self {
GlobalEventLoopProxyOrEventQueue::Proxy(proxy) => proxy.send_event(event).ok().unwrap(),
GlobalEventLoopProxyOrEventQueue::Queue(queue) => {
queue.push(event);
}
};
}
fn set_proxy(&mut self, proxy: winit::event_loop::EventLoopProxy<CustomEvent>) {
match self {
GlobalEventLoopProxyOrEventQueue::Proxy(_) => {}
GlobalEventLoopProxyOrEventQueue::Queue(queue) => {
std::mem::take(queue)
.into_iter()
.for_each(|event| proxy.send_event(event).ok().unwrap());
*self = GlobalEventLoopProxyOrEventQueue::Proxy(proxy);
}
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl Default for GlobalEventLoopProxyOrEventQueue {
fn default() -> Self {
Self::Queue(Vec::new())
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) static GLOBAL_PROXY: once_cell::sync::OnceCell<
std::sync::Mutex<GlobalEventLoopProxyOrEventQueue>,
> = once_cell::sync::OnceCell::new(); > = once_cell::sync::OnceCell::new();
pub(crate) fn with_window_target<T>(callback: impl FnOnce(&dyn EventLoopInterface) -> T) -> T { pub(crate) fn with_window_target<T>(callback: impl FnOnce(&dyn EventLoopInterface) -> T) -> T {
@ -133,8 +170,11 @@ pub fn run() {
let event_loop_proxy = not_running_loop_instance.event_loop_proxy; let event_loop_proxy = not_running_loop_instance.event_loop_proxy;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
*GLOBAL_PROXY.get_or_init(Default::default).lock().unwrap() = GLOBAL_PROXY
Some(event_loop_proxy.clone()); .get_or_init(Default::default)
.lock()
.unwrap()
.set_proxy(event_loop_proxy.clone());
} }
let mut winit_loop = not_running_loop_instance.instance; let mut winit_loop = not_running_loop_instance.instance;

View file

@ -1520,12 +1520,7 @@ impl sixtyfps_corelib::backend::Backend for Backend {
fn post_event(&'static self, event: Box<dyn FnOnce() + Send>) { fn post_event(&'static self, event: Box<dyn FnOnce() + Send>) {
let e = crate::eventloop::CustomEvent::UserEvent(event); let e = crate::eventloop::CustomEvent::UserEvent(event);
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
crate::eventloop::GLOBAL_PROXY crate::eventloop::GLOBAL_PROXY.get_or_init(Default::default).lock().unwrap().send_event(e);
.get_or_init(Default::default)
.lock()
.unwrap()
.as_ref()
.map(|proxy| proxy.send_event(e));
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
crate::eventloop::with_window_target(|event_loop| { crate::eventloop::with_window_target(|event_loop| {
event_loop.event_loop_proxy().send_event(e).ok(); event_loop.event_loop_proxy().send_event(e).ok();

View file

@ -45,6 +45,9 @@ impl<'a> DocumentCache<'a> {
} }
fn main() { fn main() {
// Start the LSP thread with a delay when the gui event loop is up and running, to be able
// to be immediately ready to serve preview requests.
preview::run_in_ui_thread(Box::new(|| {
std::thread::spawn(|| { std::thread::spawn(|| {
match run_lsp_server() { match run_lsp_server() {
Ok(_) => {} Ok(_) => {}
@ -54,11 +57,8 @@ fn main() {
} }
preview::quit_ui_event_loop(); preview::quit_ui_event_loop();
}); });
// TODO: Don't terminate the event loop when the window is closed with Qt }));
// TODO: There's a race condition where theoretically the LSP could receive a preview // TODO: Don't terminate the event loop when the window is closed
// request before the gui event loop has started, which would cause post_event to panic.
// Instead we should start the lsp thread when the gui thread is *ready*, for example through
// a single-shot timer.
preview::start_ui_event_loop(); preview::start_ui_event_loop();
} }

View file

@ -27,7 +27,7 @@ unsafe impl Sync for FutureRunner {}
impl Wake for FutureRunner { impl Wake for FutureRunner {
fn wake(self: Arc<Self>) { fn wake(self: Arc<Self>) {
sixtyfps_rendering_backend_default::backend().post_event(Box::new(move || { run_in_ui_thread(Box::new(move || {
let waker = self.clone().into(); let waker = self.clone().into();
let mut cx = std::task::Context::from_waker(&waker); let mut cx = std::task::Context::from_waker(&waker);
let mut fut_opt = self.fut.lock().unwrap(); let mut fut_opt = self.fut.lock().unwrap();
@ -41,10 +41,14 @@ impl Wake for FutureRunner {
} }
} }
fn run_in_ui_thread(mut fut: Pin<Box<dyn Future<Output = ()>>>) { fn run_future_in_ui_thread(mut fut: Pin<Box<dyn Future<Output = ()>>>) {
Arc::new(FutureRunner { fut: Mutex::new(Some(fut)) }).wake() Arc::new(FutureRunner { fut: Mutex::new(Some(fut)) }).wake()
} }
pub fn run_in_ui_thread(f: Box<dyn FnOnce() + Send>) {
sixtyfps_rendering_backend_default::backend().post_event(f);
}
pub fn start_ui_event_loop() { pub fn start_ui_event_loop() {
sixtyfps_interpreter::run_event_loop(); sixtyfps_interpreter::run_event_loop();
} }
@ -56,7 +60,7 @@ pub fn quit_ui_event_loop() {
} }
pub fn load_preview(path: std::path::PathBuf) { pub fn load_preview(path: std::path::PathBuf) {
run_in_ui_thread(Box::pin(async move { reload_preview(&path).await })); run_future_in_ui_thread(Box::pin(async move { reload_preview(&path).await }));
} }
async fn reload_preview(path: &std::path::Path) { async fn reload_preview(path: &std::path::Path) {