| /* |
| * 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-2023 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 <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <errno.h> |
| #include <time.h> |
| |
| #if defined(HAVE_GETTIMEOFDAY) || defined(HAVE_SETTIMEOFDAY) |
| #include <sys/time.h> |
| #endif /* HAVE_(GET|SET)TIMEOFDAY */ |
| |
| #ifdef HAVE_SYS_POLL_H |
| #include <poll.h> |
| #endif /* HAVE_SYS_POLL_H */ |
| |
| #ifdef HAVE_SELECT |
| #ifdef HAVE_SYS_SELECT_H |
| #include <sys/select.h> |
| #else /* HAVE_SYS_SELECT_H */ |
| #include <sys/time.h> |
| #endif /* HAVE_SYS_SELECT_H */ |
| #endif /* HAVE_SELECT */ |
| |
| #include "log.h" |
| #include "timing.h" |
| |
| #ifdef __MSDOS__ |
| #include "system_msdos.h" |
| #endif /* __MSDOS__ */ |
| |
| #if !HAVE_DECL_LOCALTIME_R |
| static inline struct tm * |
| localtime_r (const time_t *timep, struct tm *result) { |
| *result = *localtime(timep); |
| return result; |
| } |
| #endif /* HAVE_DECL_LOCALTIME_R */ |
| |
| void |
| getCurrentTime (TimeValue *now) { |
| now->seconds = 0; |
| now->nanoseconds = 0; |
| |
| #if defined(GRUB_RUNTIME) |
| static time_t baseSeconds = 0; |
| static uint64_t baseMilliseconds; |
| |
| if (!baseSeconds) { |
| baseSeconds = time(NULL); |
| baseMilliseconds = grub_get_time_ms(); |
| } |
| |
| { |
| uint64_t milliseconds = grub_get_time_ms() - baseMilliseconds; |
| |
| now->seconds = baseSeconds + (milliseconds / MSECS_PER_SEC); |
| now->nanoseconds = (milliseconds % MSECS_PER_SEC) * NSECS_PER_MSEC; |
| } |
| |
| #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME) && !defined(__MINGW32__) |
| struct timespec ts; |
| |
| if (clock_gettime(CLOCK_REALTIME, &ts) != -1) { |
| now->seconds = ts.tv_sec; |
| now->nanoseconds = ts.tv_nsec; |
| } else { |
| //logSystemError("clock_gettime"); |
| } |
| |
| #elif defined(HAVE_GETTIMEOFDAY) |
| struct timeval tv; |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
| int result = gettimeofday(&tv, NULL); |
| #pragma GCC diagnostic pop |
| |
| if (result != -1) { |
| now->seconds = tv.tv_sec; |
| now->nanoseconds = tv.tv_usec * NSECS_PER_USEC; |
| } else { |
| //logSystemError("gettimeofday"); |
| } |
| |
| #elif defined(HAVE_TIME) |
| now->seconds = time(NULL); |
| |
| #else /* get current time */ |
| #warning get current time not supported on this platform |
| #endif /* get current time */ |
| } |
| |
| void |
| setCurrentTime (const TimeValue *now) { |
| #if defined(HAVE_CLOCK_SETTIME) && defined(CLOCK_REALTIME) |
| const struct timespec ts = { |
| .tv_sec = now->seconds, |
| .tv_nsec = now->nanoseconds |
| }; |
| |
| if (clock_settime(CLOCK_REALTIME, &ts) == -1) { |
| logSystemError("clock_settime"); |
| } |
| |
| #elif defined(HAVE_SETTIMEOFDAY) |
| struct timeval tv = { |
| .tv_sec = now->seconds, |
| .tv_usec = now->nanoseconds / NSECS_PER_USEC |
| }; |
| |
| if (settimeofday(&tv, NULL) == -1) { |
| logSystemError("settimeofday"); |
| } |
| |
| #elif defined(__MINGW32__) |
| TimeComponents components; |
| expandTimeValue(now, &components); |
| |
| SYSTEMTIME time = { |
| .wYear = components.year, |
| .wMonth = components.month + 1, |
| .wDay = components.day + 1, |
| .wHour = components.hour, |
| .wMinute = components.minute, |
| .wSecond = components.second, |
| .wMilliseconds = now->nanoseconds / NSECS_PER_MSEC |
| }; |
| |
| if (!SetLocalTime(&time)) { |
| logWindowsSystemError("SetLocalTime"); |
| } |
| |
| #elif defined(HAVE_STIME) |
| const time_t seconds = now->seconds; |
| |
| if (stime(&seconds) == -1) { |
| logSystemError("stime"); |
| } |
| |
| #else /* set current time */ |
| #warning set current time not supported on this platform |
| #endif /* get current time */ |
| } |
| |
| void |
| makeTimeValue (TimeValue *value, const TimeComponents *components) { |
| value->nanoseconds = components->nanosecond; |
| |
| #if defined(GRUB_RUNTIME) |
| value->seconds = 0; |
| |
| #else /* make seconds */ |
| struct tm time = { |
| .tm_year = components->year - 1900, |
| .tm_mon = components->month, |
| .tm_mday = components->day + 1, |
| .tm_hour = components->hour, |
| .tm_min = components->minute, |
| .tm_sec = components->second, |
| .tm_isdst = -1 |
| }; |
| |
| value->seconds = mktime(&time); |
| #endif /* make seconds */ |
| } |
| |
| void |
| expandTimeValue (const TimeValue *value, TimeComponents *components) { |
| time_t seconds = value->seconds; |
| components->nanosecond = value->nanoseconds; |
| |
| struct tm *time = &components->time; |
| localtime_r(&seconds, time); |
| |
| #if defined(GRUB_RUNTIME) |
| components->year = time->tm.year; |
| components->month = time->tm.month - 1; |
| components->day = time->tm.day - 1; |
| components->hour = time->tm.hour; |
| components->minute = time->tm.minute; |
| components->second = time->tm.second; |
| |
| #else /* expand seconds */ |
| components->year = time->tm_year + 1900; |
| components->month = time->tm_mon; |
| components->day = time->tm_mday - 1; |
| components->hour = time->tm_hour; |
| components->minute = time->tm_min; |
| components->second = time->tm_sec; |
| #endif /* expand seconds */ |
| } |
| |
| size_t |
| formatSeconds (char *buffer, size_t size, const char *format, int32_t seconds) { |
| time_t time = seconds; |
| struct tm description; |
| |
| localtime_r(&time, &description); |
| return strftime(buffer, size, format, &description); |
| } |
| |
| void |
| normalizeTimeValue (TimeValue *time) { |
| while (time->nanoseconds < 0) { |
| time->seconds -= 1; |
| time->nanoseconds += NSECS_PER_SEC; |
| } |
| |
| while (time->nanoseconds >= NSECS_PER_SEC) { |
| time->seconds += 1; |
| time->nanoseconds -= NSECS_PER_SEC; |
| } |
| } |
| |
| void |
| adjustTimeValue (TimeValue *time, int milliseconds) { |
| TimeValue amount = { |
| .seconds = milliseconds / MSECS_PER_SEC, |
| .nanoseconds = (milliseconds % MSECS_PER_SEC) * NSECS_PER_MSEC |
| }; |
| |
| normalizeTimeValue(time); |
| normalizeTimeValue(&amount); |
| time->seconds += amount.seconds; |
| time->nanoseconds += amount.nanoseconds; |
| normalizeTimeValue(time); |
| } |
| |
| int |
| compareTimeValues (const TimeValue *first, const TimeValue *second) { |
| if (first->seconds < second->seconds) return -1; |
| if (first->seconds > second->seconds) return 1; |
| |
| if (first->nanoseconds < second->nanoseconds) return -1; |
| if (first->nanoseconds > second->nanoseconds) return 1; |
| |
| return 0; |
| } |
| |
| long int |
| millisecondsBetween (const TimeValue *from, const TimeValue *to) { |
| TimeValue elapsed = { |
| .seconds = to->seconds - from->seconds, |
| .nanoseconds = to->nanoseconds - from->nanoseconds |
| }; |
| |
| normalizeTimeValue(&elapsed); |
| return ((long int)elapsed.seconds * MSECS_PER_SEC) |
| + (elapsed.nanoseconds / NSECS_PER_MSEC); |
| } |
| |
| long int |
| millisecondsTillNextSecond (const TimeValue *reference) { |
| TimeValue time = *reference; |
| |
| time.nanoseconds = 0; |
| time.seconds += 1; |
| return millisecondsBetween(reference, &time); |
| } |
| |
| long int |
| millisecondsTillNextMinute (const TimeValue *reference) { |
| TimeValue time = *reference; |
| int32_t *seconds = &time.seconds; |
| |
| time.nanoseconds = 0; |
| *seconds /= SECS_PER_MIN; |
| *seconds += 1; |
| *seconds *= SECS_PER_MIN; |
| return millisecondsBetween(reference, &time); |
| } |
| |
| void |
| getMonotonicTime (TimeValue *now) { |
| #if defined(GRUB_RUNTIME) |
| grub_uint64_t milliseconds = grub_get_time_ms(); |
| now->seconds = milliseconds / MSECS_PER_SEC; |
| now->nanoseconds = (milliseconds % MSECS_PER_SEC) * NSECS_PER_MSEC; |
| |
| #elif defined(CLOCK_REALTIME) |
| static const clockid_t clocks[] = { |
| #ifdef CLOCK_MONOTONIC_RAW |
| CLOCK_MONOTONIC_RAW, |
| #endif /* CLOCK_MONOTONIC_RAW */ |
| |
| #ifdef CLOCK_MONOTONIC_HR |
| CLOCK_MONOTONIC_HR, |
| #endif /* CLOCK_MONOTONIC_HR */ |
| |
| #ifdef CLOCK_MONOTONIC |
| CLOCK_MONOTONIC, |
| #endif /* CLOCK_MONOTONIC */ |
| |
| CLOCK_REALTIME |
| }; |
| |
| static const clockid_t *clock = clocks; |
| |
| while (*clock != CLOCK_REALTIME) { |
| struct timespec ts; |
| |
| if (clock_gettime(*clock, &ts) != -1) { |
| now->seconds = ts.tv_sec; |
| now->nanoseconds = ts.tv_nsec; |
| return; |
| } |
| |
| logMessage(LOG_WARNING, "clock not available: %u", (unsigned int)*clock); |
| clock += 1; |
| } |
| #endif /* get monotonic time */ |
| |
| getCurrentTime(now); |
| } |
| |
| long int |
| getMonotonicElapsed (const TimeValue *start) { |
| TimeValue now; |
| |
| getMonotonicTime(&now); |
| return millisecondsBetween(start, &now); |
| } |
| |
| void |
| restartTimePeriod (TimePeriod *period) { |
| getMonotonicTime(&period->start); |
| } |
| |
| void |
| startTimePeriod (TimePeriod *period, long int length) { |
| period->length = length; |
| restartTimePeriod(period); |
| } |
| |
| int |
| afterTimePeriod (const TimePeriod *period, long int *elapsed) { |
| long int milliseconds = getMonotonicElapsed(&period->start); |
| |
| if (elapsed) *elapsed = milliseconds; |
| return milliseconds >= period->length; |
| } |
| |
| void |
| approximateDelay (int milliseconds) { |
| if (milliseconds > 0) { |
| #if defined(__MINGW32__) |
| Sleep(milliseconds); |
| |
| #elif defined(__MSDOS__) |
| msdosUSleep(milliseconds * USECS_PER_MSEC); |
| |
| #elif defined (GRUB_RUNTIME) |
| grub_millisleep(milliseconds); |
| |
| #elif defined(HAVE_NANOSLEEP) |
| const struct timespec timeout = { |
| .tv_sec = milliseconds / MSECS_PER_SEC, |
| .tv_nsec = (milliseconds % MSECS_PER_SEC) * NSECS_PER_MSEC |
| }; |
| |
| if (nanosleep(&timeout, NULL) == -1) { |
| if (errno != EINTR) logSystemError("nanosleep"); |
| } |
| |
| #elif defined(HAVE_SYS_POLL_H) |
| if (poll(NULL, 0, milliseconds) == -1) { |
| if (errno != EINTR) logSystemError("poll"); |
| } |
| |
| #elif defined(HAVE_SELECT) |
| struct timeval timeout = { |
| .tv_sec = milliseconds / MSECS_PER_SEC, |
| .tv_usec = (milliseconds % MSECS_PER_SEC) * USECS_PER_MSEC |
| }; |
| |
| if (select(0, NULL, NULL, NULL, &timeout) == -1) { |
| if (errno != EINTR) logSystemError("select"); |
| } |
| |
| #endif /* approximate delay */ |
| } |
| } |
| |
| void |
| accurateDelay (const TimeValue *duration) { |
| TimeValue delay = *duration; |
| normalizeTimeValue(&delay); |
| |
| if ((delay.seconds > 0) || ((delay.seconds == 0) && (delay.nanoseconds > 0))) { |
| #if defined(HAVE_NANOSLEEP) |
| const struct timespec timeout = { |
| .tv_sec = delay.seconds, |
| .tv_nsec = delay.nanoseconds |
| }; |
| |
| if (nanosleep(&timeout, NULL) == -1) { |
| if (errno != EINTR) logSystemError("nanosleep"); |
| } |
| |
| #elif defined(HAVE_SELECT) |
| struct timeval timeout = { |
| .tv_sec = delay.seconds, |
| .tv_usec = (delay.nanoseconds + (NSECS_PER_USEC - 1)) / NSECS_PER_USEC |
| }; |
| |
| if (timeout.tv_usec == USECS_PER_SEC) { |
| timeout.tv_sec += 1; |
| timeout.tv_usec = 0; |
| } |
| |
| if (select(0, NULL, NULL, NULL, &timeout) == -1) { |
| if (errno != EINTR) logSystemError("select"); |
| } |
| |
| #else /* accurate delay */ |
| approximateDelay((delay.seconds * MSECS_PER_SEC) + ((delay.nanoseconds + (NSECS_PER_MSEC - 1)) / NSECS_PER_MSEC)); |
| #endif /* accurate delay */ |
| } |
| } |