#include "internal.h"
#if DISPATCH_USE_HOST_TIME
typedef struct _dispatch_host_time_data_s {
long double frac;
bool ratio_1_to_1;
} _dispatch_host_time_data_s;
DISPATCH_CACHELINE_ALIGN
static _dispatch_host_time_data_s _dispatch_host_time_data;
uint64_t (*_dispatch_host_time_mach2nano)(uint64_t machtime);
uint64_t (*_dispatch_host_time_nano2mach)(uint64_t nsec);
static uint64_t
_dispatch_mach_host_time_mach2nano(uint64_t machtime)
{
_dispatch_host_time_data_s *const data = &_dispatch_host_time_data;
if (unlikely(!machtime || data->ratio_1_to_1)) {
return machtime;
}
if (machtime >= INT64_MAX) {
return INT64_MAX;
}
long double big_tmp = ((long double)machtime * data->frac) + .5L;
if (unlikely(big_tmp >= INT64_MAX)) {
return INT64_MAX;
}
return (uint64_t)big_tmp;
}
static uint64_t
_dispatch_mach_host_time_nano2mach(uint64_t nsec)
{
_dispatch_host_time_data_s *const data = &_dispatch_host_time_data;
if (unlikely(!nsec || data->ratio_1_to_1)) {
return nsec;
}
if (nsec >= INT64_MAX) {
return INT64_MAX;
}
long double big_tmp = ((long double)nsec / data->frac) + .5L;
if (unlikely(big_tmp >= INT64_MAX)) {
return INT64_MAX;
}
return (uint64_t)big_tmp;
}
static void
_dispatch_host_time_init(mach_timebase_info_data_t *tbi)
{
_dispatch_host_time_data.frac = tbi->numer;
_dispatch_host_time_data.frac /= tbi->denom;
_dispatch_host_time_data.ratio_1_to_1 = (tbi->numer == tbi->denom);
_dispatch_host_time_mach2nano = _dispatch_mach_host_time_mach2nano;
_dispatch_host_time_nano2mach = _dispatch_mach_host_time_nano2mach;
}
#endif // DISPATCH_USE_HOST_TIME
void
_dispatch_time_init(void)
{
#if DISPATCH_USE_HOST_TIME
mach_timebase_info_data_t tbi;
(void)dispatch_assume_zero(mach_timebase_info(&tbi));
_dispatch_host_time_init(&tbi);
#endif // DISPATCH_USE_HOST_TIME
}
dispatch_time_t
dispatch_time(dispatch_time_t inval, int64_t delta)
{
uint64_t offset;
if (inval == DISPATCH_TIME_FOREVER) {
return DISPATCH_TIME_FOREVER;
}
if ((int64_t)inval < 0) {
if (delta >= 0) {
offset = (uint64_t)delta;
if ((int64_t)(inval -= offset) >= 0) {
return DISPATCH_TIME_FOREVER; }
return inval;
} else {
offset = (uint64_t)-delta;
if ((int64_t)(inval += offset) >= -1) {
return (dispatch_time_t)-2ll; }
return inval;
}
}
if (inval == 0) {
inval = _dispatch_absolute_time();
}
if (delta >= 0) {
offset = _dispatch_time_nano2mach((uint64_t)delta);
if ((int64_t)(inval += offset) <= 0) {
return DISPATCH_TIME_FOREVER; }
return inval;
} else {
offset = _dispatch_time_nano2mach((uint64_t)-delta);
if ((int64_t)(inval -= offset) < 1) {
return 1; }
return inval;
}
}
dispatch_time_t
dispatch_walltime(const struct timespec *inval, int64_t delta)
{
int64_t nsec;
if (inval) {
nsec = (int64_t)_dispatch_timespec_to_nano(*inval);
} else {
nsec = (int64_t)_dispatch_get_nanoseconds();
}
nsec += delta;
if (nsec <= 1) {
return delta >= 0 ? DISPATCH_TIME_FOREVER : (dispatch_time_t)-2ll;
}
return (dispatch_time_t)-nsec;
}
uint64_t
_dispatch_timeout(dispatch_time_t when)
{
dispatch_time_t now;
if (when == DISPATCH_TIME_FOREVER) {
return DISPATCH_TIME_FOREVER;
}
if (when == 0) {
return 0;
}
if ((int64_t)when < 0) {
when = (dispatch_time_t)-(int64_t)when;
now = _dispatch_get_nanoseconds();
return now >= when ? 0 : when - now;
}
now = _dispatch_absolute_time();
return now >= when ? 0 : _dispatch_time_mach2nano(when - now);
}
uint64_t
_dispatch_time_nanoseconds_since_epoch(dispatch_time_t when)
{
if (when == DISPATCH_TIME_FOREVER) {
return DISPATCH_TIME_FOREVER;
}
if ((int64_t)when < 0) {
return (uint64_t)-(int64_t)when;
}
return _dispatch_get_nanoseconds() + _dispatch_timeout(when);
}