Fix initialization race conditions in WebAssembly
WebGL is still broken, but oh well... Time to move on.
This commit is contained in:
parent
5f25943449
commit
65c8e08b44
1 changed files with 42 additions and 58 deletions
|
|
@ -200,20 +200,22 @@ where
|
||||||
Runtime::new(executor, proxy.clone())
|
Runtime::new(executor, proxy.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let (application, task) = runtime.enter(|| P::new(flags));
|
let (program, task) = runtime.enter(|| P::new(flags));
|
||||||
|
|
||||||
if let Some(stream) = task.into_stream() {
|
if let Some(stream) = task.into_stream() {
|
||||||
runtime.run(stream);
|
runtime.run(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtime.track(program.subscription().map(Action::Output).into_recipes());
|
||||||
|
|
||||||
let (boot_sender, boot_receiver) = oneshot::channel();
|
let (boot_sender, boot_receiver) = oneshot::channel();
|
||||||
let (event_sender, event_receiver) = mpsc::unbounded();
|
let (event_sender, event_receiver) = mpsc::unbounded();
|
||||||
let (control_sender, control_receiver) = mpsc::unbounded();
|
let (control_sender, control_receiver) = mpsc::unbounded();
|
||||||
|
|
||||||
let instance = Box::pin(run_instance::<P, C>(
|
let instance = Box::pin(run_instance::<P, C>(
|
||||||
application,
|
program,
|
||||||
runtime,
|
runtime,
|
||||||
proxy,
|
proxy.clone(),
|
||||||
debug,
|
debug,
|
||||||
boot_receiver,
|
boot_receiver,
|
||||||
event_receiver,
|
event_receiver,
|
||||||
|
|
@ -226,18 +228,19 @@ where
|
||||||
instance: std::pin::Pin<Box<F>>,
|
instance: std::pin::Pin<Box<F>>,
|
||||||
context: task::Context<'static>,
|
context: task::Context<'static>,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
boot: Option<BootConfig<C>>,
|
boot: Option<BootConfig<Message, C>>,
|
||||||
sender: mpsc::UnboundedSender<Event<Message>>,
|
sender: mpsc::UnboundedSender<Event<Action<Message>>>,
|
||||||
receiver: mpsc::UnboundedReceiver<Control>,
|
receiver: mpsc::UnboundedReceiver<Control>,
|
||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
|
is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
queued_events: Vec<Event<Message>>,
|
queued_events: Vec<Event<Action<Message>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BootConfig<C> {
|
struct BootConfig<Message: 'static, C> {
|
||||||
|
proxy: Proxy<Message>,
|
||||||
sender: oneshot::Sender<Boot<C>>,
|
sender: oneshot::Sender<Boot<C>>,
|
||||||
window_settings: Option<window::Settings>,
|
window_settings: Option<window::Settings>,
|
||||||
graphics_settings: graphics::Settings,
|
graphics_settings: graphics::Settings,
|
||||||
|
|
@ -248,6 +251,7 @@ where
|
||||||
context,
|
context,
|
||||||
id: settings.id,
|
id: settings.id,
|
||||||
boot: Some(BootConfig {
|
boot: Some(BootConfig {
|
||||||
|
proxy,
|
||||||
sender: boot_sender,
|
sender: boot_sender,
|
||||||
window_settings,
|
window_settings,
|
||||||
graphics_settings,
|
graphics_settings,
|
||||||
|
|
@ -262,14 +266,16 @@ where
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<Message, F, C> winit::application::ApplicationHandler<Message>
|
impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>
|
||||||
for Runner<Message, F, C>
|
for Runner<Message, F, C>
|
||||||
where
|
where
|
||||||
|
Message: std::fmt::Debug,
|
||||||
F: Future<Output = ()>,
|
F: Future<Output = ()>,
|
||||||
C: Compositor + 'static,
|
C: Compositor + 'static,
|
||||||
{
|
{
|
||||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
let Some(BootConfig {
|
let Some(BootConfig {
|
||||||
|
mut proxy,
|
||||||
sender,
|
sender,
|
||||||
window_settings,
|
window_settings,
|
||||||
graphics_settings,
|
graphics_settings,
|
||||||
|
|
@ -299,11 +305,23 @@ where
|
||||||
.send(Boot {
|
.send(Boot {
|
||||||
compositor,
|
compositor,
|
||||||
clipboard,
|
clipboard,
|
||||||
window_settings,
|
is_daemon: window_settings.is_none(),
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
.expect("Send boot event");
|
.expect("Send boot event");
|
||||||
|
|
||||||
|
if let Some(window_settings) = window_settings {
|
||||||
|
let (sender, _receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
proxy.send_action(Action::Window(
|
||||||
|
runtime::window::Action::Open(
|
||||||
|
window::Id::unique(),
|
||||||
|
window_settings,
|
||||||
|
sender,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok::<_, graphics::Error>(())
|
Ok::<_, graphics::Error>(())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -383,12 +401,12 @@ where
|
||||||
fn user_event(
|
fn user_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||||
message: Message,
|
action: Action<Message>,
|
||||||
) {
|
) {
|
||||||
self.process_event(
|
self.process_event(
|
||||||
event_loop,
|
event_loop,
|
||||||
Event::EventLoopAwakened(winit::event::Event::UserEvent(
|
Event::EventLoopAwakened(winit::event::Event::UserEvent(
|
||||||
message,
|
action,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -412,7 +430,7 @@ where
|
||||||
fn process_event(
|
fn process_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||||
event: Event<Message>,
|
event: Event<Action<Message>>,
|
||||||
) {
|
) {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
if !*self.is_booted.borrow() {
|
if !*self.is_booted.borrow() {
|
||||||
|
|
@ -574,7 +592,7 @@ where
|
||||||
struct Boot<C> {
|
struct Boot<C> {
|
||||||
compositor: C,
|
compositor: C,
|
||||||
clipboard: Clipboard,
|
clipboard: Clipboard,
|
||||||
window_settings: Option<window::Settings>,
|
is_daemon: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Event<Message: 'static> {
|
enum Event<Message: 'static> {
|
||||||
|
|
@ -616,7 +634,7 @@ async fn run_instance<P, C>(
|
||||||
let Boot {
|
let Boot {
|
||||||
mut compositor,
|
mut compositor,
|
||||||
mut clipboard,
|
mut clipboard,
|
||||||
window_settings,
|
is_daemon,
|
||||||
} = boot.try_recv().ok().flatten().expect("Receive boot");
|
} = boot.try_recv().ok().flatten().expect("Receive boot");
|
||||||
|
|
||||||
let mut window_manager = WindowManager::new();
|
let mut window_manager = WindowManager::new();
|
||||||
|
|
@ -626,29 +644,7 @@ async fn run_instance<P, C>(
|
||||||
let mut actions = 0;
|
let mut actions = 0;
|
||||||
|
|
||||||
let mut ui_caches = FxHashMap::default();
|
let mut ui_caches = FxHashMap::default();
|
||||||
let mut user_interfaces = ManuallyDrop::new(build_user_interfaces(
|
let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
|
||||||
&program,
|
|
||||||
&mut debug,
|
|
||||||
&mut window_manager,
|
|
||||||
FxHashMap::from_iter([(
|
|
||||||
window::Id::MAIN,
|
|
||||||
user_interface::Cache::default(),
|
|
||||||
)]),
|
|
||||||
));
|
|
||||||
|
|
||||||
runtime.track(program.subscription().map(Action::Output).into_recipes());
|
|
||||||
|
|
||||||
let is_daemon = window_settings.is_none();
|
|
||||||
|
|
||||||
if let Some(window_settings) = window_settings {
|
|
||||||
let (sender, _receiver) = oneshot::channel();
|
|
||||||
|
|
||||||
proxy.send_action(Action::Window(runtime::window::Action::Open(
|
|
||||||
window::Id::unique(),
|
|
||||||
window_settings,
|
|
||||||
sender,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.startup_finished();
|
debug.startup_finished();
|
||||||
|
|
||||||
|
|
@ -697,8 +693,6 @@ async fn run_instance<P, C>(
|
||||||
| event::StartCause::ResumeTimeReached { .. },
|
| event::StartCause::ResumeTimeReached { .. },
|
||||||
) => {
|
) => {
|
||||||
for (_id, window) in window_manager.iter_mut() {
|
for (_id, window) in window_manager.iter_mut() {
|
||||||
// TODO once widgets can request to be redrawn, we can avoid always requesting a
|
|
||||||
// redraw
|
|
||||||
window.raw.request_redraw();
|
window.raw.request_redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -878,9 +872,6 @@ async fn run_instance<P, C>(
|
||||||
) {
|
) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
debug.render_finished();
|
debug.render_finished();
|
||||||
|
|
||||||
// TODO: Handle animations!
|
|
||||||
// Maybe we can use `ControlFlow::WaitUntil` for this.
|
|
||||||
}
|
}
|
||||||
Err(error) => match error {
|
Err(error) => match error {
|
||||||
// This is an unrecoverable error.
|
// This is an unrecoverable error.
|
||||||
|
|
@ -1024,7 +1015,6 @@ async fn run_instance<P, C>(
|
||||||
|
|
||||||
debug.event_processing_finished();
|
debug.event_processing_finished();
|
||||||
|
|
||||||
// TODO mw application update returns which window IDs to update
|
|
||||||
if !messages.is_empty() || uis_stale {
|
if !messages.is_empty() || uis_stale {
|
||||||
let cached_interfaces: FxHashMap<
|
let cached_interfaces: FxHashMap<
|
||||||
window::Id,
|
window::Id,
|
||||||
|
|
@ -1034,7 +1024,6 @@ async fn run_instance<P, C>(
|
||||||
.map(|(id, ui)| (id, ui.into_cache()))
|
.map(|(id, ui)| (id, ui.into_cache()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Update application
|
|
||||||
update(
|
update(
|
||||||
&mut program,
|
&mut program,
|
||||||
&mut runtime,
|
&mut runtime,
|
||||||
|
|
@ -1042,8 +1031,6 @@ async fn run_instance<P, C>(
|
||||||
&mut messages,
|
&mut messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
// we must synchronize all window states with application state after an
|
|
||||||
// application update since we don't know what changed
|
|
||||||
for (id, window) in window_manager.iter_mut() {
|
for (id, window) in window_manager.iter_mut() {
|
||||||
window.state.synchronize(
|
window.state.synchronize(
|
||||||
&program,
|
&program,
|
||||||
|
|
@ -1051,12 +1038,9 @@ async fn run_instance<P, C>(
|
||||||
&window.raw,
|
&window.raw,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO once widgets can request to be redrawn, we can avoid always requesting a
|
|
||||||
// redraw
|
|
||||||
window.raw.request_redraw();
|
window.raw.request_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// rebuild UIs with the synchronized states
|
|
||||||
user_interfaces =
|
user_interfaces =
|
||||||
ManuallyDrop::new(build_user_interfaces(
|
ManuallyDrop::new(build_user_interfaces(
|
||||||
&program,
|
&program,
|
||||||
|
|
@ -1082,7 +1066,7 @@ async fn run_instance<P, C>(
|
||||||
|
|
||||||
/// Builds a window's [`UserInterface`] for the [`Program`].
|
/// Builds a window's [`UserInterface`] for the [`Program`].
|
||||||
fn build_user_interface<'a, P: Program>(
|
fn build_user_interface<'a, P: Program>(
|
||||||
application: &'a P,
|
program: &'a P,
|
||||||
cache: user_interface::Cache,
|
cache: user_interface::Cache,
|
||||||
renderer: &mut P::Renderer,
|
renderer: &mut P::Renderer,
|
||||||
size: Size,
|
size: Size,
|
||||||
|
|
@ -1093,7 +1077,7 @@ where
|
||||||
P::Theme: DefaultStyle,
|
P::Theme: DefaultStyle,
|
||||||
{
|
{
|
||||||
debug.view_started();
|
debug.view_started();
|
||||||
let view = application.view(id);
|
let view = program.view(id);
|
||||||
debug.view_finished();
|
debug.view_finished();
|
||||||
|
|
||||||
debug.layout_started();
|
debug.layout_started();
|
||||||
|
|
@ -1104,7 +1088,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update<P: Program, E: Executor>(
|
fn update<P: Program, E: Executor>(
|
||||||
application: &mut P,
|
program: &mut P,
|
||||||
runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
|
runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
|
||||||
debug: &mut Debug,
|
debug: &mut Debug,
|
||||||
messages: &mut Vec<P::Message>,
|
messages: &mut Vec<P::Message>,
|
||||||
|
|
@ -1115,7 +1099,7 @@ fn update<P: Program, E: Executor>(
|
||||||
debug.log_message(&message);
|
debug.log_message(&message);
|
||||||
debug.update_started();
|
debug.update_started();
|
||||||
|
|
||||||
let task = runtime.enter(|| application.update(message));
|
let task = runtime.enter(|| program.update(message));
|
||||||
debug.update_finished();
|
debug.update_finished();
|
||||||
|
|
||||||
if let Some(stream) = task.into_stream() {
|
if let Some(stream) = task.into_stream() {
|
||||||
|
|
@ -1123,13 +1107,13 @@ fn update<P: Program, E: Executor>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let subscription = application.subscription();
|
let subscription = program.subscription();
|
||||||
runtime.track(subscription.map(Action::Output).into_recipes());
|
runtime.track(subscription.map(Action::Output).into_recipes());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_action<P, C>(
|
fn run_action<P, C>(
|
||||||
action: Action<P::Message>,
|
action: Action<P::Message>,
|
||||||
application: &P,
|
program: &P,
|
||||||
compositor: &mut C,
|
compositor: &mut C,
|
||||||
messages: &mut Vec<P::Message>,
|
messages: &mut Vec<P::Message>,
|
||||||
clipboard: &mut Clipboard,
|
clipboard: &mut Clipboard,
|
||||||
|
|
@ -1170,7 +1154,7 @@ fn run_action<P, C>(
|
||||||
.start_send(Control::CreateWindow {
|
.start_send(Control::CreateWindow {
|
||||||
id,
|
id,
|
||||||
settings,
|
settings,
|
||||||
title: application.title(id),
|
title: program.title(id),
|
||||||
monitor,
|
monitor,
|
||||||
})
|
})
|
||||||
.expect("Send control action");
|
.expect("Send control action");
|
||||||
|
|
@ -1403,7 +1387,7 @@ fn run_action<P, C>(
|
||||||
|
|
||||||
/// Build the user interface for every window.
|
/// Build the user interface for every window.
|
||||||
pub fn build_user_interfaces<'a, P: Program, C>(
|
pub fn build_user_interfaces<'a, P: Program, C>(
|
||||||
application: &'a P,
|
program: &'a P,
|
||||||
debug: &mut Debug,
|
debug: &mut Debug,
|
||||||
window_manager: &mut WindowManager<P, C>,
|
window_manager: &mut WindowManager<P, C>,
|
||||||
mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
|
mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
|
||||||
|
|
@ -1420,7 +1404,7 @@ where
|
||||||
Some((
|
Some((
|
||||||
id,
|
id,
|
||||||
build_user_interface(
|
build_user_interface(
|
||||||
application,
|
program,
|
||||||
cache,
|
cache,
|
||||||
&mut window.renderer,
|
&mut window.renderer,
|
||||||
window.state.logical_size(),
|
window.state.logical_size(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue