/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include "log.h" #include "async_alarm.h" #include "async_internal.h" #include "timing.h" typedef struct { TimeValue time; int interval; AsyncAlarmCallback *callback; void *data; unsigned active:1; unsigned cancel:1; unsigned reschedule:1; } AlarmEntry; struct AsyncAlarmDataStruct { Queue *alarmQueue; }; void asyncDeallocateAlarmData (AsyncAlarmData *ad) { if (ad) { if (ad->alarmQueue) deallocateQueue(ad->alarmQueue); free(ad); } } static AsyncAlarmData * getAlarmData (void) { AsyncThreadSpecificData *tsd = asyncGetThreadSpecificData(); if (!tsd) return NULL; if (!tsd->alarmData) { AsyncAlarmData *ad; if (!(ad = malloc(sizeof(*ad)))) { logMallocError(); return NULL; } memset(ad, 0, sizeof(*ad)); ad->alarmQueue = NULL; tsd->alarmData = ad; } return tsd->alarmData; } static void cancelAlarm (Element *element) { AlarmEntry *alarm = getElementItem(element); if (alarm->active) { alarm->cancel = 1; } else { deleteElement(element); } } static void deallocateAlarmEntry (void *item, void *data) { AlarmEntry *alarm = item; free(alarm); } static int compareAlarmEntries (const void *newItem, const void *existingItem, void *queueData) { const AlarmEntry *newAlarm = newItem; const AlarmEntry *existingAlarm = existingItem; return compareTimeValues(&newAlarm->time, &existingAlarm->time) < 0; } static Queue * getAlarmQueue (int create) { AsyncAlarmData *ad = getAlarmData(); if (!ad) return NULL; if (!ad->alarmQueue && create) { if ((ad->alarmQueue = newQueue(deallocateAlarmEntry, compareAlarmEntries))) { static AsyncQueueMethods methods = { .cancelRequest = cancelAlarm }; setQueueData(ad->alarmQueue, &methods); } } return ad->alarmQueue; } typedef struct { const TimeValue *time; AsyncAlarmCallback *callback; void *data; } AlarmElementParameters; static Element * newAlarmElement (const void *parameters) { const AlarmElementParameters *aep = parameters; Queue *alarms = getAlarmQueue(1); if (alarms) { AlarmEntry *alarm; if ((alarm = malloc(sizeof(*alarm)))) { memset(alarm, 0, sizeof(*alarm)); alarm->time = *aep->time; alarm->callback = aep->callback; alarm->data = aep->data; alarm->active = 0; alarm->cancel = 0; alarm->reschedule = 0; { Element *element = enqueueItem(alarms, alarm); if (element) { logSymbol(LOG_CATEGORY(ASYNC_EVENTS), aep->callback, "alarm added"); return element; } } free(alarm); } else { logMallocError(); } } return NULL; } int asyncNewAbsoluteAlarm ( AsyncHandle *handle, const TimeValue *time, AsyncAlarmCallback *callback, void *data ) { const AlarmElementParameters aep = { .time = time, .callback = callback, .data = data }; return asyncMakeHandle(handle, newAlarmElement, &aep); } int asyncNewRelativeAlarm ( AsyncHandle *handle, int milliseconds, AsyncAlarmCallback *callback, void *data ) { TimeValue time; getMonotonicTime(&time); adjustTimeValue(&time, milliseconds); return asyncNewAbsoluteAlarm(handle, &time, callback, data); } static Element * getAlarmElement (AsyncHandle handle) { return asyncGetHandleElement(handle, getAlarmQueue(0)); } int asyncResetAlarmTo (AsyncHandle handle, const TimeValue *time) { Element *element = getAlarmElement(handle); if (element) { AlarmEntry *alarm = getElementItem(element); alarm->time = *time; requeueElement(element); return 1; } return 0; } int asyncResetAlarmIn (AsyncHandle handle, int milliseconds) { TimeValue time; getMonotonicTime(&time); adjustTimeValue(&time, milliseconds); return asyncResetAlarmTo(handle, &time); } int asyncResetAlarmInterval (AsyncHandle handle, int milliseconds) { Element *element = getAlarmElement(handle); if (element) { AlarmEntry *alarm = getElementItem(element); alarm->interval = milliseconds; alarm->reschedule = milliseconds > 0; return 1; } return 0; } static int testInactiveAlarm (void *item, void *data) { const AlarmEntry *alarm = item; return !alarm->active; } int asyncExecuteAlarmCallback (AsyncAlarmData *ad, long int *timeout) { if (ad) { Queue *alarms = ad->alarmQueue; if (alarms) { Element *element = processQueue(alarms, testInactiveAlarm, NULL); if (element) { AlarmEntry *alarm = getElementItem(element); TimeValue now; long int milliseconds; getMonotonicTime(&now); milliseconds = millisecondsBetween(&now, &alarm->time); if (milliseconds <= 0) { AsyncAlarmCallback *callback = alarm->callback; const AsyncAlarmCallbackParameters parameters = { .now = &now, .data = alarm->data }; logSymbol(LOG_CATEGORY(ASYNC_EVENTS), callback, "alarm starting"); alarm->active = 1; if (callback) callback(¶meters); alarm->active = 0; if (alarm->reschedule) { adjustTimeValue(&alarm->time, alarm->interval); getMonotonicTime(&now); if (compareTimeValues(&alarm->time, &now) < 0) alarm->time = now; requeueElement(element); } else { alarm->cancel = 1; } if (alarm->cancel) deleteElement(element); return 1; } if (milliseconds < *timeout) { *timeout = milliseconds; logSymbol(LOG_CATEGORY(ASYNC_EVENTS), alarm->callback, "next alarm: %ld", *timeout); } } } } return 0; }