| /* |
| * Copyright (c) 2008-2013 Apple Inc. All rights reserved. |
| * |
| * @APPLE_APACHE_LICENSE_HEADER_START@ |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * @APPLE_APACHE_LICENSE_HEADER_END@ |
| */ |
| |
| #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) { |
| // wall clock |
| if (delta >= 0) { |
| offset = (uint64_t)delta; |
| if ((int64_t)(inval -= offset) >= 0) { |
| return DISPATCH_TIME_FOREVER; // overflow |
| } |
| return inval; |
| } else { |
| offset = (uint64_t)-delta; |
| if ((int64_t)(inval += offset) >= -1) { |
| // -1 is special == DISPATCH_TIME_FOREVER == forever |
| return (dispatch_time_t)-2ll; // underflow |
| } |
| return inval; |
| } |
| } |
| // mach clock |
| 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; // overflow |
| } |
| return inval; |
| } else { |
| offset = _dispatch_time_nano2mach((uint64_t)-delta); |
| if ((int64_t)(inval -= offset) < 1) { |
| return 1; // underflow |
| } |
| 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) { |
| // -1 is special == DISPATCH_TIME_FOREVER == forever |
| 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) { |
| // time in nanoseconds since the POSIX epoch already |
| return (uint64_t)-(int64_t)when; |
| } |
| return _dispatch_get_nanoseconds() + _dispatch_timeout(when); |
| } |