1/*
2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/common/clock.h>
17
18#include <time.h>
19
20static const uint64_t NS_PER_SEC = 1000000000;
21
22#if defined(CLOCK_MONOTONIC_RAW)
23# define HIGH_RES_CLOCK CLOCK_MONOTONIC_RAW
24#else
25# define HIGH_RES_CLOCK CLOCK_MONOTONIC
26#endif
27
28/* This entire compilation branch has two goals. First, prior to OSX Sierra, clock_gettime does not exist on OSX, so we
29 * already need to branch on that. Second, even if we compile on a newer OSX, which we will always do for bindings (e.g.
30 * python, dotnet, java etc...), we have to worry about the same lib being loaded on an older version, and thus, we'd
31 * get linker errors at runtime. To avoid this, we do a dynamic load
32 * to keep the function out of linker tables and only use the symbol if the current running process has access to the
33 * function. */
34#if defined(__MACH__)
35# include <AvailabilityMacros.h>
36# include <aws/common/thread.h>
37# include <dlfcn.h>
38# include <sys/time.h>
39
40static int s_legacy_get_time(uint64_t *timestamp) {
41 struct timeval tv;
42 int ret_val = gettimeofday(&tv, NULL);
43
44 if (ret_val) {
45 return aws_raise_error(AWS_ERROR_CLOCK_FAILURE);
46 }
47
48 uint64_t secs = (uint64_t)tv.tv_sec;
49 uint64_t u_secs = (uint64_t)tv.tv_usec;
50 *timestamp = (secs * NS_PER_SEC) + (u_secs * 1000);
51 return AWS_OP_SUCCESS;
52}
53
54# if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
55static aws_thread_once s_thread_once_flag = AWS_THREAD_ONCE_STATIC_INIT;
56static int (*s_gettime_fn)(clockid_t __clock_id, struct timespec *__tp) = NULL;
57
58static void s_do_osx_loads(void *user_data) {
59 (void)user_data;
60 s_gettime_fn = (int (*)(clockid_t __clock_id, struct timespec * __tp)) dlsym(RTLD_DEFAULT, "clock_gettime");
61}
62
63int aws_high_res_clock_get_ticks(uint64_t *timestamp) {
64 aws_thread_call_once(&s_thread_once_flag, s_do_osx_loads, NULL);
65 int ret_val = 0;
66
67 if (s_gettime_fn) {
68 struct timespec ts;
69 ret_val = s_gettime_fn(HIGH_RES_CLOCK, &ts);
70
71 if (ret_val) {
72 return aws_raise_error(AWS_ERROR_CLOCK_FAILURE);
73 }
74
75 uint64_t secs = (uint64_t)ts.tv_sec;
76 uint64_t n_secs = (uint64_t)ts.tv_nsec;
77 *timestamp = (secs * NS_PER_SEC) + n_secs;
78 return AWS_OP_SUCCESS;
79 }
80
81 return s_legacy_get_time(timestamp);
82}
83
84int aws_sys_clock_get_ticks(uint64_t *timestamp) {
85 aws_thread_call_once(&s_thread_once_flag, s_do_osx_loads, NULL);
86 int ret_val = 0;
87
88 if (s_gettime_fn) {
89 struct timespec ts;
90 ret_val = s_gettime_fn(CLOCK_REALTIME, &ts);
91 if (ret_val) {
92 return aws_raise_error(AWS_ERROR_CLOCK_FAILURE);
93 }
94
95 uint64_t secs = (uint64_t)ts.tv_sec;
96 uint64_t n_secs = (uint64_t)ts.tv_nsec;
97 *timestamp = (secs * NS_PER_SEC) + n_secs;
98 return AWS_OP_SUCCESS;
99 }
100 return s_legacy_get_time(timestamp);
101}
102# else
103int aws_high_res_clock_get_ticks(uint64_t *timestamp) {
104 return s_legacy_get_time(timestamp);
105}
106
107int aws_sys_clock_get_ticks(uint64_t *timestamp) {
108 return s_legacy_get_time(timestamp);
109}
110
111# endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 */
112/* Everywhere else, just link clock_gettime in directly */
113#else
114int aws_high_res_clock_get_ticks(uint64_t *timestamp) {
115 int ret_val = 0;
116
117 struct timespec ts;
118
119 ret_val = clock_gettime(HIGH_RES_CLOCK, &ts);
120
121 if (ret_val) {
122 return aws_raise_error(AWS_ERROR_CLOCK_FAILURE);
123 }
124
125 uint64_t secs = (uint64_t)ts.tv_sec;
126 uint64_t n_secs = (uint64_t)ts.tv_nsec;
127 *timestamp = (secs * NS_PER_SEC) + n_secs;
128 return AWS_OP_SUCCESS;
129}
130
131int aws_sys_clock_get_ticks(uint64_t *timestamp) {
132 int ret_val = 0;
133
134 struct timespec ts;
135 ret_val = clock_gettime(CLOCK_REALTIME, &ts);
136 if (ret_val) {
137 return aws_raise_error(AWS_ERROR_CLOCK_FAILURE);
138 }
139
140 uint64_t secs = (uint64_t)ts.tv_sec;
141 uint64_t n_secs = (uint64_t)ts.tv_nsec;
142 *timestamp = (secs * NS_PER_SEC) + n_secs;
143
144 return AWS_OP_SUCCESS;
145}
146#endif /* defined(__MACH__) */
147