Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Source/WTF/wtf/RunLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,10 @@ class WTF_CAPABILITY("is current") RunLoop final : public GuaranteedSerialFuncti
#if USE(WINDOWS_EVENT_LOOP)
static LRESULT CALLBACK RunLoopWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
DWORD msTillNextTimer();
void fireTimers();
HWND m_runLoopMessageWindow;
UncheckedKeyHashSet<UINT_PTR> m_liveTimers;
Deque<TimerBase*> m_timers;

Lock m_loopLock;
#elif USE(COCOA_EVENT_LOOP)
Expand Down
100 changes: 77 additions & 23 deletions Source/WTF/wtf/win/RunLoopWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace WTF {

static const UINT PerformWorkMessage = WM_USER + 1;
static const UINT SetTimerMessage = WM_USER + 2;
static const UINT KillTimerMessage = WM_USER + 3;
static const UINT FireTimerMessage = WM_USER + 3;
static const LPCWSTR kRunLoopMessageWindowClassName = L"RunLoopMessageWindow";

LRESULT CALLBACK RunLoop::RunLoopWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Expand All @@ -58,18 +58,10 @@ LRESULT RunLoop::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
performWork();
return 0;
case SetTimerMessage:
::SetTimer(hWnd, wParam, lParam, nullptr);
return 0;
case KillTimerMessage:
::KillTimer(hWnd, wParam);
return 0;
case WM_TIMER:
case FireTimerMessage:
RunLoop::TimerBase* timer = nullptr;
{
Locker locker { m_loopLock };
if (m_liveTimers.contains(wParam))
timer = std::bit_cast<RunLoop::TimerBase*>(wParam);
}
timer = std::bit_cast<RunLoop::TimerBase*>(wParam);
Copy link
Contributor

Choose a reason for hiding this comment

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

timer can be already destroyed here. I fixed it in #43244

if (timer != nullptr)
timer->timerFired();
return 0;
Expand All @@ -80,12 +72,55 @@ LRESULT RunLoop::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

void RunLoop::run()
{
MSG message;
while (BOOL result = ::GetMessage(&message, nullptr, 0, 0)) {
if (result == -1)
break;
::TranslateMessage(&message);
::DispatchMessage(&message);
while (true) {
RunLoop::currentSingleton().fireTimers();
MSG message;
while (BOOL result = ::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
if (result == -1)
break;
if (message.message == WM_QUIT)
return;
::TranslateMessage(&message);
::DispatchMessage(&message);
}

DWORD timeout = RunLoop::currentSingleton().msTillNextTimer();
if (timeout > 0)
::MsgWaitForMultipleObjectsEx(0, nullptr, timeout, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
}
}

DWORD RunLoop::msTillNextTimer()
{
Locker locker { m_loopLock };
Seconds timeout = Seconds(3600);

if (!m_timers.isEmpty()) {
auto now = MonotonicTime::now();
auto firstTimer = m_timers.last();

timeout = std::max<Seconds>(firstTimer->m_nextFireDate - now, 0_s);
}
return timeout.milliseconds();
}

void RunLoop::fireTimers()
{
Locker locker { m_loopLock };

// Can bail here if there's no timers before we check the time
if (m_timers.isEmpty())
return;

// Fire any timers ready from the front of the queue
auto now = MonotonicTime::now();

while (!m_timers.isEmpty()) {
auto timer = m_timers.last();
if (timer->m_nextFireDate > now)
return;
::PostMessage(m_runLoopMessageWindow, FireTimerMessage, std::bit_cast<uintptr_t>(timer), 0LL);
m_timers.removeLast();
}
}

Expand Down Expand Up @@ -137,6 +172,7 @@ void RunLoop::wakeUp()

RunLoop::CycleResult RunLoop::cycle(RunLoopMode)
{
RunLoop::currentSingleton().fireTimers();
MSG message;
while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
if (message.message == WM_QUIT)
Expand All @@ -160,9 +196,13 @@ void RunLoop::TimerBase::timerFired()

if (!m_isRepeating) {
m_isActive = false;
::KillTimer(m_runLoop->m_runLoopMessageWindow, std::bit_cast<uintptr_t>(this));
} else
m_nextFireDate = MonotonicTime::infinity();
} else {
m_nextFireDate = MonotonicTime::timePointFromNow(m_interval);
m_runLoop->m_timers.appendAndBubble(this, [&] (TimerBase* otherTimer) -> bool {
return m_nextFireDate > otherTimer->m_nextFireDate;
});
}
}

fired();
Expand All @@ -181,12 +221,23 @@ RunLoop::TimerBase::~TimerBase()
void RunLoop::TimerBase::start(Seconds interval, bool repeat)
{
Locker locker { m_runLoop->m_loopLock };
if (isActiveWithLock()) {
// Rescheduling timer that's already started
m_runLoop->m_timers.removeFirstMatching([&] (TimerBase* t) -> bool {
return this == t;
});
}

m_isRepeating = repeat;
m_isActive = true;
m_interval = interval;
m_nextFireDate = MonotonicTime::timePointFromNow(m_interval);
m_runLoop->m_liveTimers.add(std::bit_cast<uintptr_t>(this));
::PostMessage(m_runLoop->m_runLoopMessageWindow, SetTimerMessage, std::bit_cast<uintptr_t>(this), interval.millisecondsAs<UINT>());
m_runLoop->m_timers.appendAndBubble(this, [&] (TimerBase* otherTimer) -> bool {
return m_nextFireDate > otherTimer->m_nextFireDate;
});
// If this is the first timer now, we need to cycle the run loop so we don't sleep through it
if (m_runLoop->m_timers.last() == this)
::PostMessage(m_runLoop->m_runLoopMessageWindow, SetTimerMessage, std::bit_cast<uintptr_t>(this), interval.millisecondsAs<UINT>());
}

void RunLoop::TimerBase::stop()
Expand All @@ -196,8 +247,11 @@ void RunLoop::TimerBase::stop()
return;

m_isActive = false;
m_runLoop->m_liveTimers.remove(std::bit_cast<uintptr_t>(this));
::PostMessage(m_runLoop->m_runLoopMessageWindow, KillTimerMessage, std::bit_cast<uintptr_t>(this), 0LL);
m_nextFireDate = MonotonicTime::infinity();

m_runLoop->m_timers.removeFirstMatching([&] (TimerBase* t) -> bool {
return this == t;
});
}

bool RunLoop::TimerBase::isActiveWithLock() const
Expand Down