Skip to content

Commit

Permalink
Fix initialization race conditions in WebAssembly
Browse files Browse the repository at this point in the history
WebGL is still broken, but oh well... Time to move on.
  • Loading branch information
hecrj committed Jun 19, 2024
1 parent 5f25943 commit 5b21772
Showing 1 changed file with 41 additions and 57 deletions.
98 changes: 41 additions & 57 deletions winit/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,20 +200,22 @@ where
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() {
runtime.run(stream);
}

runtime.track(program.subscription().map(Action::Output).into_recipes());

let (boot_sender, boot_receiver) = oneshot::channel();
let (event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, control_receiver) = mpsc::unbounded();

let instance = Box::pin(run_instance::<P, C>(
application,
program,
runtime,
proxy,
proxy.clone(),
debug,
boot_receiver,
event_receiver,
Expand All @@ -226,8 +228,8 @@ where
instance: std::pin::Pin<Box<F>>,
context: task::Context<'static>,
id: Option<String>,
boot: Option<BootConfig<C>>,
sender: mpsc::UnboundedSender<Event<Message>>,
boot: Option<BootConfig<Message, C>>,
sender: mpsc::UnboundedSender<Event<Action<Message>>>,
receiver: mpsc::UnboundedReceiver<Control>,
error: Option<Error>,

Expand All @@ -237,7 +239,8 @@ where
queued_events: Vec<Event<Message>>,
}

struct BootConfig<C> {
struct BootConfig<Message: 'static, C> {
proxy: Proxy<Message>,
sender: oneshot::Sender<Boot<C>>,
window_settings: Option<window::Settings>,
graphics_settings: graphics::Settings,
Expand All @@ -248,6 +251,7 @@ where
context,
id: settings.id,
boot: Some(BootConfig {
proxy,
sender: boot_sender,
window_settings,
graphics_settings,
Expand All @@ -262,14 +266,16 @@ where
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>
where
Message: std::fmt::Debug,
F: Future<Output = ()>,
C: Compositor + 'static,
{
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let Some(BootConfig {
mut proxy,
sender,
window_settings,
graphics_settings,
Expand Down Expand Up @@ -299,11 +305,23 @@ where
.send(Boot {
compositor,
clipboard,
window_settings,
is_daemon: window_settings.is_none(),
})
.ok()
.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>(())
};

Expand Down Expand Up @@ -383,12 +401,12 @@ where
fn user_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
message: Message,
action: Action<Message>,
) {
self.process_event(
event_loop,
Event::EventLoopAwakened(winit::event::Event::UserEvent(
message,
action,
)),
);
}
Expand All @@ -412,7 +430,7 @@ where
fn process_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
event: Event<Message>,
event: Event<Action<Message>>,
) {
#[cfg(target_arch = "wasm32")]
if !*self.is_booted.borrow() {
Expand Down Expand Up @@ -574,7 +592,7 @@ where
struct Boot<C> {
compositor: C,
clipboard: Clipboard,
window_settings: Option<window::Settings>,
is_daemon: bool,
}

enum Event<Message: 'static> {
Expand Down Expand Up @@ -616,7 +634,7 @@ async fn run_instance<P, C>(
let Boot {
mut compositor,
mut clipboard,
window_settings,
is_daemon,
} = boot.try_recv().ok().flatten().expect("Receive boot");

let mut window_manager = WindowManager::new();
Expand All @@ -626,29 +644,7 @@ async fn run_instance<P, C>(
let mut actions = 0;

let mut ui_caches = FxHashMap::default();
let mut user_interfaces = ManuallyDrop::new(build_user_interfaces(
&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,
)));
}
let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());

debug.startup_finished();

Expand Down Expand Up @@ -697,8 +693,6 @@ async fn run_instance<P, C>(
| event::StartCause::ResumeTimeReached { .. },
) => {
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();
}
}
Expand Down Expand Up @@ -878,9 +872,6 @@ async fn run_instance<P, C>(
) {
Ok(()) => {
debug.render_finished();

// TODO: Handle animations!
// Maybe we can use `ControlFlow::WaitUntil` for this.
}
Err(error) => match error {
// This is an unrecoverable error.
Expand Down Expand Up @@ -1024,7 +1015,6 @@ async fn run_instance<P, C>(

debug.event_processing_finished();

// TODO mw application update returns which window IDs to update
if !messages.is_empty() || uis_stale {
let cached_interfaces: FxHashMap<
window::Id,
Expand All @@ -1034,29 +1024,23 @@ async fn run_instance<P, C>(
.map(|(id, ui)| (id, ui.into_cache()))
.collect();

// Update application
update(
&mut program,
&mut runtime,
&mut debug,
&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() {
window.state.synchronize(
&program,
id,
&window.raw,
);

// TODO once widgets can request to be redrawn, we can avoid always requesting a
// redraw
window.raw.request_redraw();
}

// rebuild UIs with the synchronized states
user_interfaces =
ManuallyDrop::new(build_user_interfaces(
&program,
Expand All @@ -1082,7 +1066,7 @@ async fn run_instance<P, C>(

/// Builds a window's [`UserInterface`] for the [`Program`].
fn build_user_interface<'a, P: Program>(
application: &'a P,
program: &'a P,
cache: user_interface::Cache,
renderer: &mut P::Renderer,
size: Size,
Expand All @@ -1093,7 +1077,7 @@ where
P::Theme: DefaultStyle,
{
debug.view_started();
let view = application.view(id);
let view = program.view(id);
debug.view_finished();

debug.layout_started();
Expand All @@ -1104,7 +1088,7 @@ where
}

fn update<P: Program, E: Executor>(
application: &mut P,
program: &mut P,
runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
debug: &mut Debug,
messages: &mut Vec<P::Message>,
Expand All @@ -1115,21 +1099,21 @@ fn update<P: Program, E: Executor>(
debug.log_message(&message);
debug.update_started();

let task = runtime.enter(|| application.update(message));
let task = runtime.enter(|| program.update(message));
debug.update_finished();

if let Some(stream) = task.into_stream() {
runtime.run(stream);
}
}

let subscription = application.subscription();
let subscription = program.subscription();
runtime.track(subscription.map(Action::Output).into_recipes());
}

fn run_action<P, C>(
action: Action<P::Message>,
application: &P,
program: &P,
compositor: &mut C,
messages: &mut Vec<P::Message>,
clipboard: &mut Clipboard,
Expand Down Expand Up @@ -1170,7 +1154,7 @@ fn run_action<P, C>(
.start_send(Control::CreateWindow {
id,
settings,
title: application.title(id),
title: program.title(id),
monitor,
})
.expect("Send control action");
Expand Down Expand Up @@ -1403,7 +1387,7 @@ fn run_action<P, C>(

/// Build the user interface for every window.
pub fn build_user_interfaces<'a, P: Program, C>(
application: &'a P,
program: &'a P,
debug: &mut Debug,
window_manager: &mut WindowManager<P, C>,
mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
Expand All @@ -1420,7 +1404,7 @@ where
Some((
id,
build_user_interface(
application,
program,
cache,
&mut window.renderer,
window.state.logical_size(),
Expand Down

0 comments on commit 5b21772

Please sign in to comment.