Skip to content

feat(core): add wakeup callback for external event loop integration#5430

Open
rlch wants to merge 1 commit intoDioxusLabs:mainfrom
rlch:feat/wakeup-callback
Open

feat(core): add wakeup callback for external event loop integration#5430
rlch wants to merge 1 commit intoDioxusLabs:mainfrom
rlch:feat/wakeup-callback

Conversation

@rlch
Copy link
Copy Markdown

@rlch rlch commented Mar 31, 2026

Summary

Adds VirtualDom::set_wakeup_callback() — a hook that fires when async tasks complete, allowing external event loops to schedule a repaint without polling.

The problem: Frameworks that drive the VirtualDom synchronously (calling render_immediate() in response to input events) have no way to know when async work needs processing. A use_resource fetch that completes while the user isn't interacting sits in the VirtualDom's queue until the next input event. This causes visible delays — e.g., images only appearing when the user hovers over the UI.

The solution: set_wakeup_callback registers a Send + Sync callback on the runtime that gets invoked from ArcWake::wake_by_ref alongside the existing channel send. External event loops use this to schedule a render_immediate call:

// winit example
let proxy = event_loop.create_proxy();
dom.set_wakeup_callback(move || {
    let _ = proxy.send_event(UserEvent::NewWork);
});

This follows the universal pattern used by every major GUI framework (Flutter's scheduleFrame, egui's request_repaint, Iced's EventLoopProxy, Qt's postEvent, GTK's g_idle_add).

Changes:

  • Runtime: new wakeup_callback: RefCell<Option<Arc<dyn Fn() + Send + Sync>>> field
  • LocalTaskHandle: carries the callback, invokes it in wake_by_ref
  • VirtualDom::set_wakeup_callback(): public API to set the callback
  • 2 new tests in packages/core/tests/wakeup_callback.rs

Test plan

  • All existing dioxus-core tests pass (71 passed, 7 ignored)
  • New test: callback fires when spawned async task completes
  • New test: callback does NOT fire when no async work is spawned
  • No breaking changes — fully backwards compatible (callback is optional, defaults to None)

Add `VirtualDom::set_wakeup_callback()` so external event loops (winit,
GTK, etc.) can be notified when async tasks complete or scopes are marked
dirty. This avoids the need for polling — the callback fires directly
from the task waker when a future resolves.

Without this, frameworks that drive the VirtualDom synchronously
(calling `render_immediate()` in response to input events) have no way
to know when async work like `use_resource` completions need processing.
The UI only updates on the next user interaction, causing visual delays
(e.g., images only appearing on hover).

The callback is `Send + Sync` because Rust's `Waker` trait requires it —
task wakers may fire from any thread.

Closes #XXXX
@rlch rlch requested a review from a team as a code owner March 31, 2026 09:02
@ealmloff
Copy link
Copy Markdown
Member

The wait_for_work method on the virtual dom will poll any futures that have woken up and return when a scope needs to be rerun. We use this with a custom waker to integrate with the window event loop here in desktop:

pub fn poll_vdom(&mut self) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants