LibCore: Port EventLoop to Windows

This commit is contained in:
stasoid 2024-10-31 12:44:19 +05:00 committed by Andrew Kaster
parent d4f8b598cb
commit 1c77135948
Notes: github-actions[bot] 2024-11-14 18:19:34 +00:00
7 changed files with 320 additions and 6 deletions

View file

@ -36,7 +36,6 @@ set(SOURCES
Event.cpp Event.cpp
EventLoop.cpp EventLoop.cpp
EventLoopImplementation.cpp EventLoopImplementation.cpp
EventLoopImplementationUnix.cpp
EventReceiver.cpp EventReceiver.cpp
MappedFile.cpp MappedFile.cpp
MimeData.cpp MimeData.cpp
@ -55,9 +54,13 @@ set(SOURCES
) )
if (WIN32) if (WIN32)
list(APPEND SOURCES AnonymousBufferWindows.cpp) list(APPEND SOURCES
AnonymousBufferWindows.cpp
EventLoopImplementationWindows.cpp)
else() else()
list(APPEND SOURCES AnonymousBuffer.cpp) list(APPEND SOURCES
AnonymousBuffer.cpp
EventLoopImplementationUnix.cpp)
endif() endif()
if (NOT WIN32 AND NOT EMSCRIPTEN) if (NOT WIN32 AND NOT EMSCRIPTEN)

View file

@ -9,7 +9,7 @@
#include <AK/Badge.h> #include <AK/Badge.h>
#include <LibCore/Event.h> #include <LibCore/Event.h>
#include <LibCore/EventLoop.h> #include <LibCore/EventLoop.h>
#include <LibCore/EventLoopImplementationUnix.h> #include <LibCore/EventLoopImplementation.h>
#include <LibCore/EventReceiver.h> #include <LibCore/EventReceiver.h>
#include <LibCore/Promise.h> #include <LibCore/Promise.h>
#include <LibCore/ThreadEventQueue.h> #include <LibCore/ThreadEventQueue.h>

View file

@ -7,8 +7,12 @@
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
#include <LibCore/Event.h> #include <LibCore/Event.h>
#include <LibCore/EventLoopImplementation.h> #include <LibCore/EventLoopImplementation.h>
#include <LibCore/EventLoopImplementationUnix.h>
#include <LibCore/ThreadEventQueue.h> #include <LibCore/ThreadEventQueue.h>
#ifdef AK_OS_WINDOWS
# include <LibCore/EventLoopImplementationWindows.h>
#else
# include <LibCore/EventLoopImplementationUnix.h>
#endif
namespace Core { namespace Core {
@ -23,7 +27,7 @@ static EventLoopManager* s_event_loop_manager;
EventLoopManager& EventLoopManager::the() EventLoopManager& EventLoopManager::the()
{ {
if (!s_event_loop_manager) if (!s_event_loop_manager)
s_event_loop_manager = new EventLoopManagerUnix; s_event_loop_manager = new EventLoopManagerPlatform;
return *s_event_loop_manager; return *s_event_loop_manager;
} }

View file

@ -62,4 +62,6 @@ private:
Array<int, 2>& m_wake_pipe_fds; Array<int, 2>& m_wake_pipe_fds;
}; };
using EventLoopManagerPlatform = EventLoopManagerUnix;
} }

View file

@ -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), &notifier);
}
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 == &notifier; });
}
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>();
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <LibCore/EventLoopImplementation.h>
namespace Core {
class EventLoopManagerWindows final : public EventLoopManager {
public:
virtual ~EventLoopManagerWindows() override = default;
virtual NonnullOwnPtr<EventLoopImplementation> make_implementation() override;
virtual intptr_t register_timer(EventReceiver&, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible) override;
virtual void unregister_timer(intptr_t timer_id) override;
virtual void register_notifier(Notifier&) override;
virtual void unregister_notifier(Notifier&) override;
virtual void did_post_event() override;
virtual int register_signal(int signal_number, Function<void(int)> handler) override;
virtual void unregister_signal(int handler_id) override;
};
class EventLoopImplementationWindows final : public EventLoopImplementation {
public:
static NonnullOwnPtr<EventLoopImplementationWindows> create() { return make<EventLoopImplementationWindows>(); }
EventLoopImplementationWindows();
virtual ~EventLoopImplementationWindows() override = default;
virtual int exec() override;
virtual size_t pump(PumpMode) override;
virtual void quit(int) override;
virtual void wake() override;
virtual void unquit() override;
virtual bool was_exit_requested() const override;
virtual void notify_forked_and_in_child() override;
virtual void post_event(EventReceiver& receiver, NonnullOwnPtr<Event>&&) override;
private:
bool m_exit_requested { false };
int m_exit_code { 0 };
// The wake event handle of this event loop needs to be accessible from other threads.
void*& m_wake_event;
};
using EventLoopManagerPlatform = EventLoopManagerWindows;
}

View file

@ -9,6 +9,7 @@
#include <AK/Function.h> #include <AK/Function.h>
#include <LibCore/Event.h> #include <LibCore/Event.h>
#include <LibCore/EventReceiver.h> #include <LibCore/EventReceiver.h>
#include <pthread.h>
namespace Core { namespace Core {