1/* Copyright 2016 Google Inc.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License. */
14
15#ifndef NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_
16#define NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_
17
18#include "nsync_cpp.h"
19
20/* Internal details of the implementation of the type nsync_time.
21
22 The type nsync_time can have different implementations on different
23 platforms, because the world has many different representations of time.
24 Further, the "epoch" of absolute times can vary from address space to
25 address space.
26
27 On monotonic clocks: In our testing, we found that the monotonic clock on
28 various popular systems (such as Linux, and some BSD variants) was no better
29 behaved than the realtime clock, and routinely took large steps backwards,
30 especially on multiprocessors. Given that "monotonic" doesn't seem to mean
31 what it says, implementers of nsync_time might consider retaining the
32 simplicity of a single epoch within an address space, by configuring any
33 time synchronization mechanism (like ntp) to adjust for leap seconds by
34 adjusting the rate, rather than with a backwards step. */
35
36#if NSYNC_USE_GPR_TIMESPEC
37#include "grpc/support/time.h"
38NSYNC_CPP_START_
39typedef gpr_timespec nsync_time;
40#define NSYNC_TIME_SEC(t) ((t).tv_sec)
41#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
42NSYNC_CPP_END_
43
44#elif defined(NSYNC_USE_INT_TIME)
45#include <time.h>
46NSYNC_CPP_START_
47typedef NSYNC_USE_INT_TIME nsync_time;
48#define NSYNC_TIME_SEC(t) (sizeof (nsync_time) >= 8? \
49 (t) / (1000 * 1000 * 1000): \
50 ((t) / 1000))
51#define NSYNC_TIME_NSEC(t) (sizeof (nsync_time) >= 8? \
52 (t) % (1000 * 1000 * 1000): \
53 (((t) % 1000) * 1000 * 1000))
54#define NSYNC_TIME_MAX_ MAX_INT_TYPE (nsync_time)
55NSYNC_CPP_END_
56
57#elif defined(NSYNC_USE_FLOATING_TIME)
58#include <math.h>
59#include <time.h>
60NSYNC_CPP_START_
61typedef NSYNC_USE_FLOATING_TIME nsync_time;
62#define NSYNC_TIME_SEC(t) (trunc ((t) / (nsync_time) (1000 * 1000 * 1000)))
63#define NSYNC_TIME_NSEC(t) ((t) - ((1000 * 1000 * 1000) * NSYNC_TIME_SEC (t)))
64#define NSYNC_TIME_MAX_ DBL_MAX
65NSYNC_CPP_END_
66
67#elif NSYNC_USE_DEBUG_TIME
68/* Check that the library can be built with a different time struct. */
69#include <time.h>
70NSYNC_CPP_START_
71typedef struct {
72 time_t seconds;
73 unsigned nanoseconds;
74} nsync_time;
75#define NSYNC_TIME_SEC(t) ((t).seconds)
76#define NSYNC_TIME_NSEC(t) ((t).nanoseconds)
77NSYNC_CPP_END_
78
79#elif defined(__cplusplus) && \
80 (NSYNC_USE_CPP11_TIMEPOINT || (__cplusplus >= 201103L) || (_MSC_VER >= 1700))
81/* The inline functions below provide function overloads that accept the most
82 likely C++11 time type(s).
83
84 C++11 time types have many variations and subtleties:
85 - There are multiple clocks with potentially differing epochs; these clocks
86 are not necessarily phase-locked to the same rate, making conversion and
87 comparison between clocks tricky.
88 - Relative and absolute times are distinguished in the type system.
89 - Either integral or floating point counters may be used to represent time
90 intervals, and code valid with one may not be valid with the other
91 (see std::chrono::treat_as_floating_point).
92 - A counter increment of one can represent any rational number of seconds
93 (for whatever "seconds" means for this clock).
94 - Conversions between duration types may round or truncate at the
95 implementation's discretion.
96 - As mentioned above, common implementations of the default monotonic clock
97 ("steady_clock") illegally allow a thread to observe time going backwards,
98 especially in the face of scheduling on a different CPU, making its use
99 misleading, at best.
100 I've chosen to handle this complexity by doing a conversion to absolute
101 timespec at the interface layer, so all the C++ complication is here, rather
102 than spread throughout the library. */
103
104#include <chrono>
105#include <time.h>
106NSYNC_CPP_START_
107typedef struct timespec nsync_time;
108#define NSYNC_TIME_SEC(t) ((t).tv_sec)
109#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
110
111typedef std::chrono::system_clock::time_point nsync_cpp_time_point_;
112nsync_time nsync_from_time_point_ (nsync_cpp_time_point_);
113nsync_cpp_time_point_ nsync_to_time_point_ (nsync_time);
114#define NSYNC_COUNTER_CPP_OVERLOAD_ \
115 static inline uint32_t nsync_counter_wait (nsync_counter c, \
116 nsync_cpp_time_point_ abs_deadline) { \
117 return (nsync_counter_wait (c, nsync_from_time_point_ (abs_deadline))); \
118 }
119#define NSYNC_CV_CPP_OVERLOAD_ \
120 static inline int nsync_cv_wait_with_deadline (nsync_cv *cv, nsync_mu *mu, \
121 nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
122 return (nsync_cv_wait_with_deadline (cv, mu, \
123 nsync_from_time_point_ (abs_deadline), \
124 cancel_note)); \
125 } \
126 static inline int nsync_cv_wait_with_deadline_generic (nsync_cv *cv, \
127 void *mu, void (*lock) (void *), void (*unlock) (void *), \
128 nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
129 return (nsync_cv_wait_with_deadline_generic (cv, mu, lock, unlock, \
130 nsync_from_time_point_ (abs_deadline), \
131 cancel_note)); \
132 }
133#define NSYNC_MU_WAIT_CPP_OVERLOAD_ \
134 static inline int nsync_mu_wait_with_deadline (nsync_mu *mu, \
135 int (*condition) (const void *condition_arg), const void *condition_arg, \
136 int (*condition_arg_eq) (const void *a, const void *b), \
137 nsync_cpp_time_point_ abs_deadline, struct nsync_note_s_ *cancel_note) { \
138 return (nsync_mu_wait_with_deadline (mu, condition, condition_arg, \
139 condition_arg_eq, \
140 nsync_from_time_point_ (abs_deadline), \
141 cancel_note)); \
142 }
143#define NSYNC_NOTE_CPP_OVERLOAD_ \
144 static inline nsync_note nsync_note_new (nsync_note parent, \
145 nsync_cpp_time_point_ abs_deadline) { \
146 return (nsync_note_new (parent, nsync_from_time_point_ (abs_deadline))); \
147 } \
148 static inline int nsync_note_wait (nsync_note n, nsync_cpp_time_point_ abs_deadline) { \
149 return (nsync_note_wait (n, nsync_from_time_point_ (abs_deadline))); \
150 } \
151 static inline nsync_cpp_time_point_ nsync_note_expiry_timepoint (nsync_note n) { \
152 return (nsync_to_time_point_ (nsync_note_expiry (n))); \
153 }
154#define NSYNC_WAITER_CPP_OVERLOAD_ \
155 static inline int nsync_wait_n (void *mu, void (*lock) (void *), \
156 void (*unlock) (void *), \
157 nsync_cpp_time_point_ abs_deadline, \
158 int count, struct nsync_waitable_s *waitable[]) { \
159 return (nsync_wait_n (mu, lock, unlock, \
160 nsync_from_time_point_ (abs_deadline), count, waitable)); \
161 }
162
163NSYNC_CPP_END_
164
165#else
166/* Default is to use timespec. */
167#include <time.h>
168NSYNC_CPP_START_
169typedef struct timespec nsync_time;
170#define NSYNC_TIME_SEC(t) ((t).tv_sec)
171#define NSYNC_TIME_NSEC(t) ((t).tv_nsec)
172NSYNC_CPP_END_
173
174#endif
175
176#if !defined(NSYNC_COUNTER_CPP_OVERLOAD_)
177#define NSYNC_COUNTER_CPP_OVERLOAD_
178#define NSYNC_CV_CPP_OVERLOAD_
179#define NSYNC_MU_WAIT_CPP_OVERLOAD_
180#define NSYNC_NOTE_CPP_OVERLOAD_
181#define NSYNC_WAITER_CPP_OVERLOAD_
182#endif
183
184#endif /*NSYNC_PUBLIC_NSYNC_TIME_INTERNAL_H_*/
185