From 6a565ea71745aaf4c91a7698bbf56f7d906fcaaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= jaakko.keranen@iki.fi
Date: Sun, 6 Dec 2020 11:52:26 +0200
Subject: [PATCH 1/1] Added build option for sleeping while idle
It appears at least on macOS, SDL is doing a while lot of stuff while waiting for new events. Perhaps because it has some sort of high-frequency input/sensor processing? Not sure.
Now Lagrange will idle by polling events every 15 ms and sleeping in between. This reduces CPU time by an order of magnitude on macOS. Need to still test on other platforms.
CMakeLists.txt | 4 +++
res/about/version.gmi | 1 +
src/app.c | 79 ++++++++++++++++++++++++++++++++++++++-----
src/app.h | 1 +
4 files changed, 76 insertions(+), 9 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 82d55590..0edfbed2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,7 @@ option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF)
option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON)
option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF)
option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF)
+option (ENABLE_IDLE_SLEEP "While idle, sleep in the main thread instead of waiting for events" ON)
include (BuildType.cmake)
include (res/Embed.cmake)
@@ -219,6 +220,9 @@ if (ENABLE_MPG123 AND MPG123_FOUND)
target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_MPG123=1)
target_link_libraries (app PUBLIC PkgConfig::MPG123)
endif ()
+if (ENABLE_IDLE_SLEEP)
+endif ()
target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
if (APPLE)
diff --git a/res/about/version.gmi b/res/about/version.gmi
index af758486..5944932f 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -10,6 +10,7 @@
+* macOS: Fixed excessive CPU usage while idling.
diff --git a/src/app.c b/src/app.c
index 1e0a847d..ba7d41d2 100644
--- a/src/app.c
+++ b/src/app.c
@@ -89,6 +89,8 @@ static const char *prefsFileName_App_ = "prefs.cfg";
static const char *stateFileName_App_ = "state.binary";
static const char *downloadDir_App_ = "~/Downloads";
+static const int idleThreshold_App_ = 1000; /* ms */
struct Impl_App {
iCommandLine args;
iString * execPath;
@@ -100,7 +102,12 @@ struct Impl_App {
iSortedArray tickers;
uint32_t lastTickerTime;
uint32_t elapsedSinceLastTicker;
+#if defined (LAGRANGE_IDLE_SLEEP)
+#endif
iAtomicInt pendingRefresh;
int tabEnum;
iStringList *launchCommands;
@@ -320,6 +327,16 @@ static void saveState_App_(const iApp *d) {
iRelease(f);
}
+#if defined (LAGRANGE_IDLE_SLEEP)
+static uint32_t checkAsleep_App_(uint32_t interval, void *param) {
+}
+#endif
static void init_App_(iApp *d, int argc, char **argv) {
const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_));
d->isFinishedLaunching = iFalse;
@@ -349,7 +366,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
#endif
init_Prefs(&d->prefs);
setCStr_String(&d->prefs.downloadDir, downloadDir_App_);
d->window = NULL;
set_Atomic(&d->pendingRefresh, iFalse);
d->mimehooks = new_MimeHooks();
@@ -358,6 +375,11 @@ static void init_App_(iApp *d, int argc, char **argv) {
d->bookmarks = new_Bookmarks();
d->tabEnum = 0; /* generates unique IDs for tab pages */
setThemePalette_Color(d->prefs.theme);
+#if defined (LAGRANGE_IDLE_SLEEP)
+#endif
#if defined (iPlatformApple)
setupApplication_MacOS();
#endif
@@ -484,19 +506,25 @@ const iString *debugInfo_App(void) {
}
iLocalDef iBool isWaitingAllowed_App_(iApp *d) {
+#if defined (LAGRANGE_IDLE_SLEEP)
return iFalse;
+#endif
return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers);
}
void processEvents_App(enum iAppEventMode eventMode) {
iApp *d = &app_;
SDL_Event ev;
while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode &&
SDL_WaitEvent(&ev)) ||
((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) &&
SDL_PollEvent(&ev))) {
switch (ev.type) {
case SDL_QUIT:
d->running = iFalse;
d->isRunning = iFalse;
goto backToMainLoop;
case SDL_DROPFILE: {
iBool newTab = iFalse;
@@ -516,6 +544,25 @@ void processEvents_App(enum iAppEventMode eventMode) {
break;
}
default: {
+#if defined (LAGRANGE_IDLE_SLEEP)
if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) {
if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_) {
if (!d->isIdling) {
printf("[App] idling...\n");
fflush(stdout);
}
d->isIdling = iTrue;
}
continue;
}
d->lastEventTime = SDL_GetTicks();
if (d->isIdling) {
printf("[App] ...woke up\n");
fflush(stdout);
}
d->isIdling = iFalse;
+#endif
gotEvents = iTrue;
iBool wasUsed = processEvent_Window(d->window, &ev);
if (!wasUsed) {
/* There may be a key bindings for this. */
@@ -539,6 +586,14 @@ void processEvents_App(enum iAppEventMode eventMode) {
}
}
}
+#if defined (LAGRANGE_IDLE_SLEEP)
/* This is where we spend most of our time when idle. 60 Hz still quite a lot but we
can't wait too long after the user tries to interact again with the app. In any
case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */
SDL_Delay(1000 / 60);
+#endif
backToMainLoop:;
}
@@ -586,10 +641,10 @@ static int resizeWatcher_(void *user, SDL_Event *event) {
static int run_App_(iApp *d) {
arrange_Widget(findWidget_App("root"));
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */
SDL_AddEventWatch(resizeWatcher_, d);
processEvents_App(waitForNewEvents_AppEventMode);
runTickers_App_(d);
refresh_App();
@@ -600,6 +655,9 @@ static int run_App_(iApp *d) {
void refresh_App(void) {
iApp *d = &app_;
+#if defined (LAGRANGE_IDLE_SLEEP)
+#endif
destroyPending_Widget();
draw_Window(d->window);
set_Atomic(&d->pendingRefresh, iFalse);
@@ -657,6 +715,9 @@ int run_App(int argc, char **argv) {
void postRefresh_App(void) {
iApp *d = &app_;
+#if defined (LAGRANGE_IDLE_SLEEP)
+#endif
const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue);
if (!wasPending) {
SDL_Event ev;
@@ -1085,12 +1146,12 @@ iBool handleCommand_App(const char *cmd) {
}
else if (equal_Command(cmd, "prefs.sideicon.changed")) {
d->prefs.sideIcon = arg_Command(cmd) != 0;
refresh_App();
postRefresh_App();
return iTrue;
}
else if (equal_Command(cmd, "prefs.hoveroutline.changed")) {
d->prefs.hoverOutline = arg_Command(cmd) != 0;
refresh_App();
postRefresh_App();
return iTrue;
}
else if (equal_Command(cmd, "saturation.set")) {
@@ -1373,13 +1434,13 @@ iBool handleCommand_App(const char *cmd) {
}
else if (equal_Command(cmd, "feeds.update.started")) {
setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iFalse);
refresh_App();
postRefresh_App();
return iFalse;
}
else if (equal_Command(cmd, "feeds.update.finished")) {
setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iTrue);
refreshFinished_Feeds();
refresh_App();
postRefresh_App();
return iFalse;
}
else if (equal_Command(cmd, "visited.changed")) {
diff --git a/src/app.h b/src/app.h
index bc086dfe..743484a5 100644
--- a/src/app.h
+++ b/src/app.h
@@ -46,6 +46,7 @@ enum iAppEventMode {
enum iUserEventCode {
command_UserEventCode = 1,
refresh_UserEventCode = 2,
};
const iString *execPath_App (void);
--
2.25.1
text/plain
This content has been proxied by September (ba2dc).