|
@@ -0,0 +1,244 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
|
|
|
+ * Copyright (c) 2024, stasoid <stasoid@yahoo.com>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+#include <LibCore/EventLoopImplementationWindows.h>
|
|
|
+#include <LibCore/Notifier.h>
|
|
|
+#include <LibCore/ThreadEventQueue.h>
|
|
|
+#include <WinSock2.h>
|
|
|
+#include <io.h>
|
|
|
+
|
|
|
+struct Handle {
|
|
|
+ HANDLE handle = NULL;
|
|
|
+
|
|
|
+ explicit Handle(HANDLE h = NULL)
|
|
|
+ : handle(h)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ Handle(Handle&& h)
|
|
|
+ {
|
|
|
+ handle = h.handle;
|
|
|
+ h.handle = NULL;
|
|
|
+ }
|
|
|
+ void operator=(Handle&& h)
|
|
|
+ {
|
|
|
+ VERIFY(!handle);
|
|
|
+ handle = h.handle;
|
|
|
+ h.handle = NULL;
|
|
|
+ }
|
|
|
+ ~Handle()
|
|
|
+ {
|
|
|
+ if (handle)
|
|
|
+ CloseHandle(handle);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool operator==(Handle const& h) const { return handle == h.handle; }
|
|
|
+ bool operator==(HANDLE h) const { return handle == h; }
|
|
|
+};
|
|
|
+
|
|
|
+template<>
|
|
|
+struct Traits<Handle> : DefaultTraits<Handle> {
|
|
|
+ static unsigned hash(Handle const& h) { return Traits<HANDLE>::hash(h.handle); }
|
|
|
+};
|
|
|
+template<>
|
|
|
+constexpr bool IsHashCompatible<HANDLE, Handle> = true;
|
|
|
+
|
|
|
+namespace Core {
|
|
|
+
|
|
|
+struct EventLoopTimer {
|
|
|
+ WeakPtr<EventReceiver> owner;
|
|
|
+ TimerShouldFireWhenNotVisible fire_when_not_visible = TimerShouldFireWhenNotVisible::No;
|
|
|
+};
|
|
|
+
|
|
|
+struct ThreadData {
|
|
|
+ static ThreadData& the()
|
|
|
+ {
|
|
|
+ thread_local OwnPtr<ThreadData> thread_data = make<ThreadData>();
|
|
|
+ return *thread_data;
|
|
|
+ }
|
|
|
+
|
|
|
+ ThreadData()
|
|
|
+ {
|
|
|
+ wake_event.handle = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
+ VERIFY(wake_event.handle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Each thread has its own timers, notifiers and a wake event.
|
|
|
+ HashMap<Handle, EventLoopTimer> timers;
|
|
|
+ HashMap<Handle, Notifier*> notifiers;
|
|
|
+
|
|
|
+ // The wake event is used to notify another event loop that someone has called wake().
|
|
|
+ Handle wake_event;
|
|
|
+};
|
|
|
+
|
|
|
+EventLoopImplementationWindows::EventLoopImplementationWindows()
|
|
|
+ : m_wake_event(ThreadData::the().wake_event.handle)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+int EventLoopImplementationWindows::exec()
|
|
|
+{
|
|
|
+ for (;;) {
|
|
|
+ if (m_exit_requested)
|
|
|
+ return m_exit_code;
|
|
|
+ pump(PumpMode::WaitForEvents);
|
|
|
+ }
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+}
|
|
|
+
|
|
|
+size_t EventLoopImplementationWindows::pump(PumpMode)
|
|
|
+{
|
|
|
+ auto& thread_data = ThreadData::the();
|
|
|
+ auto& notifiers = thread_data.notifiers;
|
|
|
+ auto& timers = thread_data.timers;
|
|
|
+
|
|
|
+ size_t event_count = 1 + notifiers.size() + timers.size();
|
|
|
+ // If 64 events limit proves to be insufficient RegisterWaitForSingleObject or other methods
|
|
|
+ // can be used instead as mentioned in https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
|
|
|
+ // TODO: investigate if event_count can realistically exceed 64
|
|
|
+ VERIFY(event_count <= MAXIMUM_WAIT_OBJECTS);
|
|
|
+
|
|
|
+ HANDLE event_handles[event_count];
|
|
|
+ event_handles[0] = thread_data.wake_event.handle;
|
|
|
+
|
|
|
+ size_t index = 1;
|
|
|
+ for (auto& entry : notifiers)
|
|
|
+ event_handles[index++] = entry.key.handle;
|
|
|
+ for (auto& entry : timers)
|
|
|
+ event_handles[index++] = entry.key.handle;
|
|
|
+
|
|
|
+ DWORD result = WaitForMultipleObjects(event_count, event_handles, FALSE, INFINITE);
|
|
|
+ index = result - WAIT_OBJECT_0;
|
|
|
+ VERIFY(index < event_count);
|
|
|
+
|
|
|
+ if (index != 0) {
|
|
|
+ if (index <= notifiers.size()) {
|
|
|
+ Notifier* notifier = *notifiers.get(event_handles[index]);
|
|
|
+ ThreadEventQueue::current().post_event(*notifier, make<NotifierActivationEvent>(notifier->fd(), notifier->type()));
|
|
|
+ } else {
|
|
|
+ auto& timer = *timers.get(event_handles[index]);
|
|
|
+ if (auto strong_owner = timer.owner.strong_ref())
|
|
|
+ if (timer.fire_when_not_visible == TimerShouldFireWhenNotVisible::Yes || strong_owner->is_visible_for_timer_purposes())
|
|
|
+ ThreadEventQueue::current().post_event(*strong_owner, make<TimerEvent>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ThreadEventQueue::current().process();
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopImplementationWindows::quit(int code)
|
|
|
+{
|
|
|
+ m_exit_requested = true;
|
|
|
+ m_exit_code = code;
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopImplementationWindows::unquit()
|
|
|
+{
|
|
|
+ m_exit_requested = false;
|
|
|
+ m_exit_code = 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool EventLoopImplementationWindows::was_exit_requested() const
|
|
|
+{
|
|
|
+ return m_exit_requested;
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopImplementationWindows::post_event(EventReceiver& receiver, NonnullOwnPtr<Event>&& event)
|
|
|
+{
|
|
|
+ m_thread_event_queue.post_event(receiver, move(event));
|
|
|
+ if (&m_thread_event_queue != &ThreadEventQueue::current())
|
|
|
+ wake();
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopImplementationWindows::wake()
|
|
|
+{
|
|
|
+ SetEvent(m_wake_event);
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopImplementationWindows::notify_forked_and_in_child()
|
|
|
+{
|
|
|
+ dbgln("Core::EventLoopManagerWindows::notify_forked_and_in_child() is not implemented");
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+}
|
|
|
+
|
|
|
+static int notifier_type_to_network_event(NotificationType type)
|
|
|
+{
|
|
|
+ switch (type) {
|
|
|
+ case NotificationType::Read:
|
|
|
+ return FD_READ;
|
|
|
+ case NotificationType::Write:
|
|
|
+ return FD_WRITE;
|
|
|
+ default:
|
|
|
+ dbgln("This notification type is not implemented: {}", (int)type);
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopManagerWindows::register_notifier(Notifier& notifier)
|
|
|
+{
|
|
|
+ HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
+ VERIFY(event);
|
|
|
+ SOCKET socket = _get_osfhandle(notifier.fd());
|
|
|
+ VERIFY(socket != INVALID_SOCKET);
|
|
|
+ int rc = WSAEventSelect(socket, event, notifier_type_to_network_event(notifier.type()));
|
|
|
+ VERIFY(rc == 0);
|
|
|
+
|
|
|
+ auto& notifiers = ThreadData::the().notifiers;
|
|
|
+ VERIFY(!notifiers.get(event).has_value());
|
|
|
+ notifiers.set(Handle(event), ¬ifier);
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopManagerWindows::unregister_notifier(Notifier& notifier)
|
|
|
+{
|
|
|
+ // remove_first_matching would be clearer, but currently there is no such method in HashMap
|
|
|
+ ThreadData::the().notifiers.remove_all_matching([&](auto&, auto value) { return value == ¬ifier; });
|
|
|
+}
|
|
|
+
|
|
|
+intptr_t EventLoopManagerWindows::register_timer(EventReceiver& object, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible fire_when_not_visible)
|
|
|
+{
|
|
|
+ VERIFY(milliseconds >= 0);
|
|
|
+ HANDLE timer = CreateWaitableTimer(NULL, FALSE, NULL);
|
|
|
+ VERIFY(timer);
|
|
|
+
|
|
|
+ LARGE_INTEGER first_time = {};
|
|
|
+ // Measured in 0.1μs intervals, negative means starting from now
|
|
|
+ first_time.QuadPart = -10'000 * milliseconds;
|
|
|
+ BOOL rc = SetWaitableTimer(timer, &first_time, should_reload ? milliseconds : 0, NULL, NULL, FALSE);
|
|
|
+ VERIFY(rc);
|
|
|
+
|
|
|
+ auto& timers = ThreadData::the().timers;
|
|
|
+ VERIFY(!timers.get(timer).has_value());
|
|
|
+ timers.set(Handle(timer), { object, fire_when_not_visible });
|
|
|
+ return (intptr_t)timer;
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopManagerWindows::unregister_timer(intptr_t timer_id)
|
|
|
+{
|
|
|
+ ThreadData::the().timers.remove((HANDLE)timer_id);
|
|
|
+}
|
|
|
+
|
|
|
+int EventLoopManagerWindows::register_signal(int signal_number, Function<void(int)> handler)
|
|
|
+{
|
|
|
+ dbgln("Core::EventLoopManagerWindows::register_signal() is not implemented");
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopManagerWindows::unregister_signal(int handler_id)
|
|
|
+{
|
|
|
+ dbgln("Core::EventLoopManagerWindows::unregister_signal() is not implemented");
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+}
|
|
|
+
|
|
|
+void EventLoopManagerWindows::did_post_event()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+NonnullOwnPtr<EventLoopImplementation> EventLoopManagerWindows::make_implementation()
|
|
|
+{
|
|
|
+ return make<EventLoopImplementationWindows>();
|
|
|
+}
|
|
|
+
|
|
|
+}
|