mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-05-24 13:39:11 +00:00

Fixes https://github.com/libsdl-org/SDL/issues/5512 Fixes https://github.com/libsdl-org/SDL/issues/6731
192 lines
5 KiB
C
192 lines
5 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
#include "SDL_internal.h"
|
|
|
|
#ifdef SDL_TIMER_UNIX
|
|
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "../SDL_timer_c.h"
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten.h>
|
|
#endif
|
|
|
|
/* The clock_gettime provides monotonous time, so we should use it if
|
|
it's available. The clock_gettime function is behind ifdef
|
|
for __USE_POSIX199309
|
|
Tommi Kyntola (tommi.kyntola@ray.fi) 27/09/2005
|
|
*/
|
|
/* Reworked monotonic clock to not assume the current system has one
|
|
as not all linux kernels provide a monotonic clock (yeah recent ones
|
|
probably do)
|
|
Also added macOS Monotonic clock support
|
|
Based on work in https://github.com/ThomasHabets/monotonic_clock
|
|
*/
|
|
#if HAVE_NANOSLEEP || HAVE_CLOCK_GETTIME
|
|
#include <time.h>
|
|
#endif
|
|
#ifdef __APPLE__
|
|
#include <mach/mach_time.h>
|
|
#endif
|
|
|
|
/* Use CLOCK_MONOTONIC_RAW, if available, which is not subject to adjustment by NTP */
|
|
#if HAVE_CLOCK_GETTIME
|
|
#ifdef CLOCK_MONOTONIC_RAW
|
|
#define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW
|
|
#else
|
|
#define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC
|
|
#endif
|
|
#endif
|
|
|
|
/* The first ticks value of the application */
|
|
#if !defined(HAVE_CLOCK_GETTIME) && defined(__APPLE__)
|
|
mach_timebase_info_data_t mach_base_info;
|
|
#endif
|
|
static SDL_bool checked_monotonic_time = SDL_FALSE;
|
|
static SDL_bool has_monotonic_time = SDL_FALSE;
|
|
|
|
static void CheckMonotonicTime(void)
|
|
{
|
|
#if HAVE_CLOCK_GETTIME
|
|
struct timespec value;
|
|
if (clock_gettime(SDL_MONOTONIC_CLOCK, &value) == 0) {
|
|
has_monotonic_time = SDL_TRUE;
|
|
} else
|
|
#elif defined(__APPLE__)
|
|
if (mach_timebase_info(&mach_base_info) == 0) {
|
|
has_monotonic_time = SDL_TRUE;
|
|
}
|
|
#endif
|
|
checked_monotonic_time = SDL_TRUE;
|
|
}
|
|
|
|
Uint64
|
|
SDL_GetPerformanceCounter(void)
|
|
{
|
|
Uint64 ticks;
|
|
|
|
if (!checked_monotonic_time) {
|
|
CheckMonotonicTime();
|
|
}
|
|
|
|
if (has_monotonic_time) {
|
|
#if HAVE_CLOCK_GETTIME
|
|
struct timespec now;
|
|
|
|
clock_gettime(SDL_MONOTONIC_CLOCK, &now);
|
|
ticks = now.tv_sec;
|
|
ticks *= SDL_NS_PER_SECOND;
|
|
ticks += now.tv_nsec;
|
|
#elif defined(__APPLE__)
|
|
ticks = mach_absolute_time();
|
|
#else
|
|
SDL_assert(SDL_FALSE);
|
|
ticks = 0;
|
|
#endif
|
|
} else {
|
|
struct timeval now;
|
|
|
|
gettimeofday(&now, NULL);
|
|
ticks = now.tv_sec;
|
|
ticks *= SDL_US_PER_SECOND;
|
|
ticks += now.tv_usec;
|
|
}
|
|
return ticks;
|
|
}
|
|
|
|
Uint64
|
|
SDL_GetPerformanceFrequency(void)
|
|
{
|
|
if (!checked_monotonic_time) {
|
|
CheckMonotonicTime();
|
|
}
|
|
|
|
if (has_monotonic_time) {
|
|
#if HAVE_CLOCK_GETTIME
|
|
return SDL_NS_PER_SECOND;
|
|
#elif defined(__APPLE__)
|
|
Uint64 freq = mach_base_info.denom;
|
|
freq *= SDL_NS_PER_SECOND;
|
|
freq /= mach_base_info.numer;
|
|
return freq;
|
|
#endif
|
|
}
|
|
|
|
return SDL_US_PER_SECOND;
|
|
}
|
|
|
|
void SDL_DelayNS(Uint64 ns)
|
|
{
|
|
int was_error;
|
|
|
|
#if HAVE_NANOSLEEP
|
|
struct timespec tv, remaining;
|
|
#else
|
|
struct timeval tv;
|
|
Uint64 then, now, elapsed;
|
|
#endif
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, SDL_TRUE)) {
|
|
/* pseudo-synchronous pause, used directly or through e.g. SDL_WaitEvent */
|
|
emscripten_sleep(ns / SDL_NS_PER_MS);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* Set the timeout interval */
|
|
#if HAVE_NANOSLEEP
|
|
remaining.tv_sec = (ns / SDL_NS_PER_SECOND);
|
|
remaining.tv_nsec = (ns % SDL_NS_PER_SECOND);
|
|
#else
|
|
then = SDL_GetTicksNS();
|
|
#endif
|
|
do {
|
|
errno = 0;
|
|
|
|
#if HAVE_NANOSLEEP
|
|
tv.tv_sec = remaining.tv_sec;
|
|
tv.tv_nsec = remaining.tv_nsec;
|
|
was_error = nanosleep(&tv, &remaining);
|
|
#else
|
|
/* Calculate the time interval left (in case of interrupt) */
|
|
now = SDL_GetTicksNS();
|
|
elapsed = (now - then);
|
|
then = now;
|
|
if (elapsed >= ns) {
|
|
break;
|
|
}
|
|
ns -= elapsed;
|
|
tv.tv_sec = (ns / SDL_NS_PER_SECOND)
|
|
tv.tv_usec = SDL_NS_TO_US(ns % SDL_NS_PER_SECOND);
|
|
|
|
was_error = select(0, NULL, NULL, NULL, &tv);
|
|
#endif /* HAVE_NANOSLEEP */
|
|
} while (was_error && (errno == EINTR));
|
|
}
|
|
|
|
#endif /* SDL_TIMER_UNIX */
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|