mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
LibCore: Port EventLoop to Windows
This commit is contained in:
parent
d4f8b598cb
commit
1c77135948
Notes:
github-actions[bot]
2024-11-14 18:19:34 +00:00
Author: https://github.com/stasoid Commit: https://github.com/LadybirdBrowser/ladybird/commit/1c771359486 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2299 Reviewed-by: https://github.com/ADKaster ✅
7 changed files with 320 additions and 6 deletions
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,4 +62,6 @@ private:
|
||||||
Array<int, 2>& m_wake_pipe_fds;
|
Array<int, 2>& m_wake_pipe_fds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using EventLoopManagerPlatform = EventLoopManagerUnix;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
244
Libraries/LibCore/EventLoopImplementationWindows.cpp
Normal file
244
Libraries/LibCore/EventLoopImplementationWindows.cpp
Normal 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), ¬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>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
Libraries/LibCore/EventLoopImplementationWindows.h
Normal file
60
Libraries/LibCore/EventLoopImplementationWindows.h
Normal 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;
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue