Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented GLib-based dispatcher #17281

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

kekekeks
Copy link
Member

@kekekeks kekekeks commented Oct 15, 2024

This should allow running Avalonia on the same thread as another UI toolkit that supports running on top of GRunLoop (e. g. GTK).

GLib priorities and Avalonia priorities are a bit different. Avalonia follows the WPF model when there
are "background" and "foreground" priority groups. Foreground jobs are executed before any user input processing,
background jobs are executed strictly after user input processing.

GLib has numeric priorities that are used in the following way:

  • -100 G_PRIORITY_HIGH - "high" priority sources, not really used by GLib/GTK
  • 0 G_PRIORITY_DEFAULT - polling X11 events (GTK) and default value for g_timeout_add
  • 100 G_PRIORITY_HIGH_IDLE without a clear definition, used as an anchor value of sorts
  • 110 Resize/layout operations (GTK)
  • 120 Render operations (GTK)
  • 200 G_PRIORITY_DEFAULT_IDLE - "idle" priority sources

So, unlike Avalonia, GTK puts way higher priority on input processing, then does resize/layout/render

So, to map our model to GLib we do the following:

  • foreground jobs (including grouped user events) are executed with (-1) priority (before any normal GLib jobs)
  • X11 socket is polled with G_PRIORITY_DEFAULT, all X11 events are read until socket is empty,
    we also group input events at that stage (this matches our epoll-based dispatcher)
  • background jobs are executed with G_PRIORITY_DEFAULT_IDLE, so they would have lower priority than GTK
    foreground jobs

Unfortunately we can't detect if there are pending non-idle GLib jobs using g_main_context_pending, since

  • g_main_context_pending doesn't accept max_priority argument
  • even if it did, that would still involve a syscall to the kernel to poll for fds anyway

So we just report that we don't support pending input query and let the dispatcher to
call RequestBackgroundProcessing every time, which results in g_idle_add call for every background job.
Background jobs are expected to be relatively expensive to execute since on Windows
MsgWaitForMultipleObjectsEx results isn't really free too.

For signaling (aka waking up dispatcher for processing high priority jobs we are using
g_idle_add_full with (-1) priority. While the naming suggests that it would enqueue an idle job,
it actually adds an always-triggered source that would be called before other sources with lower priority.

For timers we are using a simple g_timeout_add_full and discard the previous one when dispatcher requests
an update

Since GLib dispatches event sources in batches, we force-check for "signaled" flag to run high-prio jobs
whenever we get control back from GLib. We can still occasionally get GTK code to run before high-prio
Avalonia-jobs, but that should be fine since the point is to keep Avalonia-based jobs ordered properly
and to not have our low-priority jobs to prevent GLib-based code from running its own "foreground" jobs

Another implementation note here is that GLib (just as any other C library) is NOT aware of C# exceptions,
so we are NOT allowed to have exceptions to escape native->managed call boundary. So we have exception handlers
that try to propagate those to the nearest run loop frame that was initiated by Avalonia.

If there is no such frame, we have no choice but to log/swallow those

This should allow running Avalonia on the same thread as another UI toolkit that supports running on top of GRunLoop (e. g. GTK)
@kekekeks kekekeks added feature priority backport-candidate-11.1.x Consider this PR for backporting to 11.1 branch backport-candidate-11.2.x Consider this PR for backporting to 11.2 branch labels Oct 15, 2024
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0052660-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@kekekeks kekekeks marked this pull request as draft October 15, 2024 11:52
@kekekeks kekekeks marked this pull request as ready for review October 15, 2024 12:11
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0052685-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Comment on lines 187 to 188
using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid():N}"))
app = gtk_application_new(utf, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question. assuming, these APIs will be used from the GTK.
Will GTK Application -> Avalonia App -> Dialog specific GTK Application work well? Don't we need to check for existing gtk app? It's probably out of scope of this PR either way.

Copy link
Member Author

@kekekeks kekekeks Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure if it's even required to be called. I suspect that gtk_init_check should be sufficient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-candidate-11.1.x Consider this PR for backporting to 11.1 branch backport-candidate-11.2.x Consider this PR for backporting to 11.2 branch feature priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants