Improve the ui::MessageLoop waiting for events for a specific time

In this way we can remove the busy wait hack, and finally wait the
required time for an event or the next running ui::Timer that will
timeout.
This commit is contained in:
David Capello 2021-06-03 19:50:22 -03:00
parent ad7c319dcd
commit e6ec13cc31
5 changed files with 38 additions and 31 deletions

2
laf

@ -1 +1 @@
Subproject commit b31ebd916c067af953d1d944f85ef5b5be520ebd Subproject commit f443e196b643d714986a42068393267493bbdc58

View File

@ -381,27 +381,29 @@ void Manager::generateMessagesFromOSEvents()
// Events from laf-os // Events from laf-os
os::Event osEvent; os::Event osEvent;
for (;;) { for (;;) {
// TODO Add timers to laf::os library so we can wait for then in // Calculate how much time we can wait for the next message in the
// the OS message loop. // event queue.
bool canWait = (msg_queue.empty() && double timeout = 0.0;
redrawState == RedrawState::Normal && if (msg_queue.empty() && redrawState == RedrawState::Normal) {
!Timer::haveRunningTimers()); if (!Timer::getNextTimeout(timeout))
timeout = os::EventQueue::kWithoutTimeout;
}
if (canWait && used_msg_queue.empty()) if (timeout == os::EventQueue::kWithoutTimeout && used_msg_queue.empty())
collectGarbage(); collectGarbage();
#if _DEBUG #if _DEBUG
else if (!m_garbage.empty()) { else if (!m_garbage.empty()) {
GARBAGE_TRACE("collectGarbage() wasn't called #objects=%d" GARBAGE_TRACE("collectGarbage() wasn't called #objects=%d"
" (msg_queue=%d used_msg_queue=%d redrawState=%d runningTimers=%d)\n", " (msg_queue=%d used_msg_queue=%d redrawState=%d timeout=%.16g)\n",
int(m_garbage.size()), int(m_garbage.size()),
msg_queue.size(), msg_queue.size(),
used_msg_queue.size(), used_msg_queue.size(),
int(redrawState), int(redrawState),
Timer::haveRunningTimers()); timeout);
} }
#endif #endif
m_eventQueue->getEvent(osEvent, canWait); m_eventQueue->getEvent(osEvent, timeout);
if (osEvent.type() == os::Event::None) if (osEvent.type() == os::Event::None)
break; break;

View File

@ -1,4 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2013 David Capello // Copyright (C) 2001-2013 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -10,8 +11,6 @@
#include "ui/message_loop.h" #include "ui/message_loop.h"
#include "base/chrono.h"
#include "base/thread.h"
#include "ui/manager.h" #include "ui/manager.h"
namespace ui { namespace ui {
@ -23,22 +22,8 @@ MessageLoop::MessageLoop(Manager* manager)
void MessageLoop::pumpMessages() void MessageLoop::pumpMessages()
{ {
base::Chrono chrono;
if (m_manager->generateMessages()) if (m_manager->generateMessages())
m_manager->dispatchMessages(); m_manager->dispatchMessages();
// If the dispatching of messages was faster than 10 milliseconds,
// it means that the process is not using a lot of CPU, so we can
// wait the difference to cover those 10 milliseconds
// sleeping. With this code we can avoid 100% CPU usage.
//
// TODO check if we require this code, after adding support of event
// queues that wait for events.
//
double elapsedMSecs = chrono.elapsed() * 1000.0;
if (elapsedMSecs > 0.0 && elapsedMSecs < 10.0)
base::this_thread::sleep_for((10.0 - elapsedMSecs) / 1000.0);
} }
} // namespace ui } // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2018 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -18,6 +18,7 @@
#include "ui/widget.h" #include "ui/widget.h"
#include <algorithm> #include <algorithm>
#include <limits>
#include <vector> #include <vector>
namespace ui { namespace ui {
@ -128,9 +129,28 @@ bool Timer::haveTimers()
return !timers.empty(); return !timers.empty();
} }
bool Timer::haveRunningTimers() bool Timer::getNextTimeout(double& timeout)
{ {
return (running_timers != 0); if (running_timers == 0)
return false;
base::tick_t t = base::current_tick();
bool result = false;
timeout = std::numeric_limits<double>::max();
for (auto timer : timers) {
if (timer && timer->isRunning()) {
int64_t diff = (timer->m_lastTick + timer->m_interval) - t;
if (diff < 0) {
timeout = 0.0; // Right-now
return true;
}
else {
timeout = std::min<double>(timeout, diff / 1000.0);
result = true;
}
}
}
return result;
} }
} // namespace ui } // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -38,7 +38,7 @@ namespace ui {
static void pollTimers(); static void pollTimers();
static bool haveTimers(); static bool haveTimers();
static bool haveRunningTimers(); static bool getNextTimeout(double& timeout);
protected: protected:
virtual void onTick(); virtual void onTick();