blob: b9ad24514c6fa50538e0c48c26f4c5c9b5c8dee2 [file]
// Copyright 2019 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/base/time.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "iree/base/target_platform.h"
#include "iree/base/tracing.h"
IREE_API_EXPORT iree_time_t iree_time_now(void) {
#if defined(IREE_TIME_NOW_FN)
IREE_TIME_NOW_FN
#elif defined(IREE_PLATFORM_WINDOWS)
// GetSystemTimePreciseAsFileTime requires Windows 8, add a fallback
// (such as using std::chrono) if older support is needed.
FILETIME system_time;
GetSystemTimePreciseAsFileTime(&system_time);
const int64_t kUnixEpochStartTicks = 116444736000000000i64;
const int64_t kFtToNanoSec = 100;
LARGE_INTEGER li;
li.LowPart = system_time.dwLowDateTime;
li.HighPart = system_time.dwHighDateTime;
li.QuadPart -= kUnixEpochStartTicks;
li.QuadPart *= kFtToNanoSec;
return li.QuadPart;
#elif defined(IREE_PLATFORM_ANDROID) || defined(IREE_PLATFORM_APPLE) || \
defined(IREE_PLATFORM_LINUX) || defined(IREE_PLATFORM_EMSCRIPTEN)
struct timespec clock_time;
clock_gettime(CLOCK_REALTIME, &clock_time);
return clock_time.tv_sec * 1000000000ull + clock_time.tv_nsec;
#else
#error "IREE system clock needs to be set up for your platform"
#endif // IREE_PLATFORM_*
}
IREE_API_EXPORT iree_time_t
iree_relative_timeout_to_deadline_ns(iree_duration_t timeout_ns) {
if (timeout_ns == IREE_DURATION_ZERO) {
return IREE_TIME_INFINITE_PAST;
} else if (timeout_ns == IREE_DURATION_INFINITE) {
return IREE_TIME_INFINITE_FUTURE;
}
return iree_time_now() + timeout_ns;
}
IREE_API_EXPORT iree_duration_t
iree_absolute_deadline_to_timeout_ns(iree_time_t deadline_ns) {
if (deadline_ns == IREE_TIME_INFINITE_PAST) {
return IREE_DURATION_ZERO;
} else if (deadline_ns == IREE_TIME_INFINITE_FUTURE) {
return IREE_DURATION_INFINITE;
} else {
iree_time_t now_ns = iree_time_now();
return deadline_ns < now_ns ? IREE_DURATION_ZERO : deadline_ns - now_ns;
}
}
IREE_API_EXPORT uint32_t
iree_absolute_deadline_to_timeout_ms(iree_time_t deadline_ns) {
if (deadline_ns == IREE_TIME_INFINITE_PAST) {
return IREE_DURATION_ZERO;
} else if (deadline_ns == IREE_TIME_INFINITE_FUTURE) {
return UINT32_MAX;
} else {
// We have either already passed the deadline (and can turn this into a
// poll) or want to do nanos->millis. We round up so that a deadline of 1ns
// results in 1ms as it should still wait, vs. if it was actually 0ns
// indicating the user intended a poll.
iree_time_t now_ns = iree_time_now();
return deadline_ns < now_ns
? IREE_DURATION_ZERO
: (deadline_ns - now_ns + 1000000 - 1) / 1000000ull;
}
}
#if defined(IREE_WAIT_UNTIL_FN)
// Define IREE_WAIT_UNTIL_FN to call out to a user-configured function.
static bool iree_wait_until_impl(iree_time_t deadline_ns) {
return IREE_WAIT_UNTIL_FN(deadline_ns);
}
#elif defined(IREE_PLATFORM_WINDOWS)
// No good sleep APIs on Windows; we need to accumulate low-precision relative
// waits to reach the absolute time. Lots of slop here, but we primarily use
// nanoseconds as a uniform time API and don't guarantee that precision. Note
// that we try to round up to ensure we wait until at least the requested time.
static bool iree_wait_until_impl(iree_time_t deadline_ns) {
iree_time_t now_ns = iree_time_now();
while (now_ns < deadline_ns) {
iree_time_t delta_ns = deadline_ns - now_ns;
uint32_t delta_ms = (uint32_t)((delta_ns + 1000000 - 1) / 1000000ull);
if (delta_ms == 0) {
// Sleep(0) doesn't actually sleep and instead acts as a yield; instead of
// potentially spilling in a tight loop when we get down near the end of
// the wait we bail a bit early. We don't guarantee the precision of the
// waits so this is fine.
break;
}
Sleep(delta_ms);
now_ns = iree_time_now();
}
return true;
}
#elif (_POSIX_C_SOURCE >= 200112L) && defined(TIMER_ABSTIME)
// This is widely available on *nix-like systems (linux/bsd/etc) and in
// most libc implementations (glibc/musl/etc). It's the best as we get to
// tell the system the exact time we want to sleep until.
//
// https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html
//
// NOTE: we could save a syscall in many cases if we returned the time upon wake
// from the API.
static bool iree_wait_until_impl(iree_time_t deadline_ns) {
struct timespec ts = {
.tv_sec = (time_t)(deadline_ns / 1000000000ull),
.tv_nsec = (long)(deadline_ns % 1000000000ull),
};
int ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL);
return ret == 0;
}
#elif (_POSIX_C_SOURCE >= 199309L) || defined(IREE_PLATFORM_APPLE)
// Apple doesn't have clock_nanosleep. We could use the Mach APIs on darwin to
// do this but they require initialization and potential updates during
// execution as clock frequencies change. Instead we use the relative nanosleep
// and accumulate until the deadline, which is a good fallback for some other
// platforms as well.
//
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/nanosleep.2.html
static bool iree_wait_until_impl(iree_time_t deadline_ns) {
iree_time_t now_ns = iree_time_now();
while (now_ns < deadline_ns) {
iree_time_t delta_ns = deadline_ns - now_ns;
struct timespec abs_ts = {
.tv_sec = (time_t)(delta_ns / 1000000000ull),
.tv_nsec = (long)(delta_ns % 1000000000ull),
};
int ret = nanosleep(&abs_ts, NULL);
if (ret != 0) return false;
now_ns = iree_time_now();
}
return true;
}
#else
// No waiting available; just pretend like we did. This will cause programs
// using timers to run as fast as possible but without having a way to delay
// time there's not much else they could do.
static bool iree_wait_until_impl(iree_time_t deadline_ns) { return true; }
#endif // (platforms)
bool iree_wait_until(iree_time_t deadline_ns) {
// Can't wait forever - or for the past.
if (deadline_ns == IREE_TIME_INFINITE_FUTURE) return false;
if (deadline_ns == IREE_TIME_INFINITE_PAST) return true;
IREE_TRACE_ZONE_BEGIN(z0);
IREE_TRACE_ZONE_APPEND_VALUE(
z0, (uint64_t)iree_absolute_deadline_to_timeout_ns(deadline_ns));
// NOTE: we want to use sleep APIs with absolute times as that makes retrying
// on spurious wakes easier; if we using relative timeouts we need to ensure
// we don't drift.
bool did_wait = iree_wait_until_impl(deadline_ns);
IREE_TRACE_ZONE_END(z0);
return did_wait;
}