This is the closest Windows equivalent to integrating process exit
handlers into the event loop.
Linux could also integrate register_process() into the poll() based
Unix event loop via the SYS_pidfd_open syscall; however, macOS requires
a kqueue. So for now register_process will only be used by Windows to
implement WebView::ProcessMonitor.
Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
We were holding a reference to ThreadData, which could be destroyed
earlier than the EventLoop itself, causing other threads to UAF trying
to signal a wake.
Add a thread-safe deferred_invoke() API on WeakEventLoopReference that
queues work onto the owning thread's event queue and wakes that thread
via EventLoopManager hooks. This avoids calling wake() from foreign
threads during teardown.
Implement current_thread_handle()/wake_thread() in each backend and
track per-thread data so handles are validated before waking:
- Unix: wake via per-thread wake pipe
- Windows: wake via thread wake event
- macOS: wake via stored CFRunLoopRef
- Qt: wake via event target or QEventLoop::wakeUp()
- Android: wake via stored ALooper
The DeferredInvocationContext only existed to satisfy the requirement
in ThreadEventQueue that each event has an EventReceiver. However,
deferred_invoke() was not even using the EventReceiver to call its
callback. Therefore, we don't need to allocate one for every deferred
invocation.
This also prevents WeakPtr::strong_ref() from racing and leaking the
context object when invoking a function across threads.
The initial IOCP event loop implementation adjusted wake() to manually
queue a completion packet onto the current threads IOCP. This caused
us to now be dependent on the current threads IOCP, when the previous
behaviour did not depend on any data from the thread that was waking
the event loop.
Restoring that old behaviour allows https://hardwaretester.com/gamepad
to be loaded again.
This commit changes the event loop to use IOCPs instead of
WaitForMultipleObjects to wait on events. This is done through the Nt
kernel api NtAssociateWaitCompletionPacket which associates an event
with a completion packet. Each completion packet notifies only once, as
they are normally used to signal completion of an operation so to use
them for notifiers they are associated again after each time they are
triggered.
There are more optimizations that can be done, such as reusing the
EventLoopNotifier and EventLoopTimer structures to reduce the number of
allocations and context switches for timer and notifier registration.
This method was removed in e015a43b51
However, it was not exactly *unused* as the commit message would say.
This method was the only thing that allowed spin_until to exit when
the event loop was cancelled. This happens normally when IPC connections
are closed, but also when the process is killed.
The logic to properly handle process exit from event loop spins needs to
actually notify the caller that their goal condition was not met though.
That will be handled in a later commit.