From 5dfb890cf841579edaeb2633025db218e86f45c2 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 20 Mar 2021 18:51:03 +0200 Subject: App: Periodic commands without timers/threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the periodic commands get posted on the main thread at intervals, and the event loop cooperates by not sleeping if there are periodic commands pending. The macOS animation hangup seems to be unrelated, though — perhaps some internal SDL/Metal machinery related to app refresh stopping for a while? --- src/app.c | 32 ++++++++++++++++++++++++++------ src/periodic.c | 44 ++++++++++++++------------------------------ src/periodic.h | 16 ++++++++++------ src/ui/scrollwidget.c | 2 +- 4 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/app.c b/src/app.c index 31bd33ed..0611b6dd 100644 --- a/src/app.c +++ b/src/app.c @@ -837,6 +837,9 @@ void trimCache_App(void) { } iLocalDef iBool isWaitingAllowed_App_(iApp *d) { + if (!isEmpty_Periodic(&d->periodic)) { + return iFalse; + } if (d->warmupFrames > 0) { return iFalse; } @@ -853,14 +856,26 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) { return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); } +static iBool nextEvent_App_(iApp *d, enum iAppEventMode eventMode, SDL_Event *event) { + if (eventMode == waitForNewEvents_AppEventMode && isWaitingAllowed_App_(d)) { + /* If there are periodic commands pending, wait only for a short while. */ + if (!isEmpty_Periodic(&d->periodic)) { + return SDL_WaitEventTimeout(event, 500); + } + /* We may be allowed to block here until an event comes in. */ + if (isWaitingAllowed_App_(d)) { + return SDL_WaitEvent(event); + } + } + return SDL_PollEvent(event); +} + void processEvents_App(enum iAppEventMode eventMode) { iApp *d = &app_; SDL_Event ev; iBool gotEvents = iFalse; - while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode && - SDL_WaitEvent(&ev)) || - ((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) && - SDL_PollEvent(&ev))) { + postCommands_Periodic(&d->periodic); + while (nextEvent_App_(d, eventMode, &ev)) { #if defined (iPlatformAppleMobile) if (processEvent_iOS(&ev)) { continue; @@ -1064,13 +1079,18 @@ static int run_App_(iApp *d) { void refresh_App(void) { iApp *d = &app_; + destroyPending_Widget(); #if defined (LAGRANGE_IDLE_SLEEP) if (d->warmupFrames == 0 && d->isIdling) { return; } #endif - set_Atomic(&d->pendingRefresh, iFalse); - destroyPending_Widget(); + if (!exchange_Atomic(&d->pendingRefresh, iFalse)) { + /* Refreshing wasn't pending. */ + if (isFinished_Anim(&d->window->rootOffset)) { + return; + } + } // iTime draw; // initCurrent_Time(&draw); draw_Window(d->window); diff --git a/src/periodic.c b/src/periodic.c index c039097d..29c26de9 100644 --- a/src/periodic.c +++ b/src/periodic.c @@ -52,50 +52,35 @@ iDefineTypeConstructionArgs(PeriodicCommand, (iAny *ctx, const char *cmd), ctx, /*----------------------------------------------------------------------------------------------*/ -//static uint32_t postCommands_Periodic_(uint32_t interval, void *param) { -static iThreadResult poster_Periodic_(iThread *thread) { - iPeriodic *d = userData_Thread(thread); +static const uint32_t postingInterval_Periodic_ = 500; + +iBool postCommands_Periodic(iPeriodic *d) { + const uint32_t now = SDL_GetTicks(); + if (now - d->lastPostTime < postingInterval_Periodic_) { + return iFalse; + } + d->lastPostTime = now; + iBool wasPosted = iFalse; lock_Mutex(d->mutex); - while (!value_Atomic(&d->isStopping)) { - if (isEmpty_SortedArray(&d->commands)) { - /* Sleep until we have something to post. */ - wait_Condition(&d->haveCommands, d->mutex); - continue; - } - iConstForEach(Array, i, &d->commands.values) { - postCommandString_App(&((const iPeriodicCommand *) i.value)->command); - } - /* Sleep for a while. */ - iTime until; - initTimeout_Time(&until, 0.5f); - waitTimeout_Condition(&d->haveCommands, d->mutex, &until); + iConstForEach(Array, i, &d->commands.values) { + postCommandString_App(&((const iPeriodicCommand *) i.value)->command); + wasPosted = iTrue; } unlock_Mutex(d->mutex); - return 0; + return wasPosted; } void init_Periodic(iPeriodic *d) { d->mutex = new_Mutex(); init_SortedArray(&d->commands, sizeof(iPeriodicCommand), cmp_PeriodicCommand_); -// d->timer = SDL_AddTimer(500, postCommands_Periodic_, d); - set_Atomic(&d->isStopping, iFalse); - init_Condition(&d->haveCommands); - d->thread = new_Thread(poster_Periodic_); - setUserData_Thread(d->thread, d); - start_Thread(d->thread); + d->lastPostTime = 0; } void deinit_Periodic(iPeriodic *d) { -// SDL_RemoveTimer(d->timer); - set_Atomic(&d->isStopping, iTrue); - signal_Condition(&d->haveCommands); - join_Thread(d->thread); - iRelease(d->thread); iForEach(Array, i, &d->commands.values) { deinit_PeriodicCommand(i.value); } deinit_SortedArray(&d->commands); - deinit_Condition(&d->haveCommands); delete_Mutex(d->mutex); } @@ -112,7 +97,6 @@ void add_Periodic(iPeriodic *d, iAny *context, const char *command) { init_PeriodicCommand(&pc, context, command); insert_SortedArray(&d->commands, &pc); } - signal_Condition(&d->haveCommands); unlock_Mutex(d->mutex); } diff --git a/src/periodic.h b/src/periodic.h index c643a2fe..db90b848 100644 --- a/src/periodic.h +++ b/src/periodic.h @@ -26,17 +26,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ iDeclareType(Periodic) iDeclareType(Thread) -/* Animation utility. Not per frame but several times per second. */ +/* Animation utility. Not per frame but several times per second. Thread safe. */ struct Impl_Periodic { iMutex * mutex; iSortedArray commands; - iCondition haveCommands; - iThread * thread; - iAtomicInt isStopping; + uint32_t lastPostTime; }; void init_Periodic (iPeriodic *); void deinit_Periodic (iPeriodic *); -void add_Periodic (iPeriodic *, iAny *context, const char *command); -void remove_Periodic (iPeriodic *, iAny *context); +iLocalDef iBool isEmpty_Periodic(const iPeriodic *d) { + return isEmpty_SortedArray(&d->commands); +} + +void add_Periodic (iPeriodic *, iAny *context, const char *command); +void remove_Periodic (iPeriodic *, iAny *context); + +iBool postCommands_Periodic (iPeriodic *); diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c index b5b204a1..55ede426 100644 --- a/src/ui/scrollwidget.c +++ b/src/ui/scrollwidget.c @@ -112,7 +112,7 @@ static void unfade_ScrollWidget_(iScrollWidget *d, float opacity) { d->willCheckFade = iTrue; /* TODO: This causes an inexplicable refresh issue on macOS: the drawing of one frame takes 100ms for some reason (not the current frame but some time after). */ -// add_Periodic(periodic_App(), d, "scrollbar.fade"); + add_Periodic(periodic_App(), d, "scrollbar.fade"); } refresh_Widget(d); } -- cgit v1.2.3