1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
#ifndef RB_HRTIME_H
#define RB_HRTIME_H
#include "ruby/ruby.h"
#include <time.h>
#if defined(HAVE_SYS_TIME_H)
# include <sys/time.h>
#endif
/*
* Hi-res monotonic clock. It is currently nsec resolution, which has over
* 500 years of range (with an unsigned 64-bit integer). Developers
* targeting small systems may try 32-bit and low-resolution (milliseconds).
*
* TBD: Is nsec even necessary? usec resolution seems enough for userspace
* and it'll be suitable for use with devices lasting over 500,000 years
* (maybe some devices designed for long-term space travel)
*
* Current API:
*
* * rb_hrtime_now - current clock value (monotonic if available)
* * rb_hrtime_mul - multiply with overflow check
* * rb_hrtime_add - add with overflow check
* * rb_timeval2hrtime - convert from timeval
* * rb_timespec2hrtime - convert from timespec
* * rb_msec2hrtime - convert from millisecond
* * rb_sec2hrtime - convert from time_t (seconds)
* * rb_hrtime2timeval - convert to timeval
* * rb_hrtime2timespec - convert to timespec
*
* Note: no conversion to milliseconds is provided here because different
* functions have different limits (e.g. epoll_wait vs w32_wait_events).
* So we provide RB_HRTIME_PER_MSEC and similar macros for implementing
* this for each use case.
*/
#define RB_HRTIME_PER_USEC ((rb_hrtime_t)1000)
#define RB_HRTIME_PER_MSEC (RB_HRTIME_PER_USEC * (rb_hrtime_t)1000)
#define RB_HRTIME_PER_SEC (RB_HRTIME_PER_MSEC * (rb_hrtime_t)1000)
#define RB_HRTIME_MAX UINT64_MAX
#define RB_HRTIME_MIN ((rb_hrtime_t)0)
/*
* Lets try to support time travelers. Lets assume anybody with a time machine
* also has access to a modern gcc or clang with 128-bit int support
*/
#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
typedef int128_t rb_hrtime_t;
#else
typedef uint64_t rb_hrtime_t;
#endif
/* thread.c */
/* returns the value of the monotonic clock (if available) */
rb_hrtime_t rb_hrtime_now(void);
/*
* multiply @a and @b with overflow check and return the
* (clamped to RB_HRTIME_MAX) result.
*/
static inline rb_hrtime_t
rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b)
{
rb_hrtime_t c;
#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
if (__builtin_mul_overflow(a, b, &c))
return RB_HRTIME_MAX;
#else
if (b != 0 && a > RB_HRTIME_MAX / b) /* overflow */
return RB_HRTIME_MAX;
c = a * b;
#endif
return c;
}
/*
* add @a and @b with overflow check and return the
* (clamped to RB_HRTIME_MAX) result.
*/
static inline rb_hrtime_t
rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b)
{
rb_hrtime_t c;
#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
if (__builtin_add_overflow(a, b, &c))
return RB_HRTIME_MAX;
#else
c = a + b;
if (c < a) /* overflow */
return RB_HRTIME_MAX;
#endif
return c;
}
static inline rb_hrtime_t
rb_hrtime_sub(rb_hrtime_t a, rb_hrtime_t b)
{
if (a < b) {
return RB_HRTIME_MIN;
}
return a - b;
}
/*
* convert a timeval struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
*/
static inline rb_hrtime_t
rb_timeval2hrtime(const struct timeval *tv)
{
rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)tv->tv_sec, RB_HRTIME_PER_SEC);
rb_hrtime_t u = rb_hrtime_mul((rb_hrtime_t)tv->tv_usec, RB_HRTIME_PER_USEC);
return rb_hrtime_add(s, u);
}
/*
* convert a timespec struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
*/
static inline rb_hrtime_t
rb_timespec2hrtime(const struct timespec *ts)
{
rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)ts->tv_sec, RB_HRTIME_PER_SEC);
return rb_hrtime_add(s, (rb_hrtime_t)ts->tv_nsec);
}
/*
* convert a millisecond value to rb_hrtime_t, clamping at RB_HRTIME_MAX
*/
static inline rb_hrtime_t
rb_msec2hrtime(unsigned long msec)
{
return rb_hrtime_mul((rb_hrtime_t)msec, RB_HRTIME_PER_MSEC);
}
/*
* convert a time_t value to rb_hrtime_t, clamping at RB_HRTIME_MAX
* Negative values will be clamped at 0.
*/
static inline rb_hrtime_t
rb_sec2hrtime(time_t sec)
{
if (sec <= 0) return 0;
return rb_hrtime_mul((rb_hrtime_t)sec, RB_HRTIME_PER_SEC);
}
/*
* convert a rb_hrtime_t value to a timespec, suitable for calling
* functions like ppoll(2) or kevent(2)
*/
static inline struct timespec *
rb_hrtime2timespec(struct timespec *ts, const rb_hrtime_t *hrt)
{
if (hrt) {
ts->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
ts->tv_nsec = (int32_t)(*hrt % RB_HRTIME_PER_SEC);
return ts;
}
return 0;
}
/*
* convert a rb_hrtime_t value to a timeval, suitable for calling
* functions like select(2)
*/
static inline struct timeval *
rb_hrtime2timeval(struct timeval *tv, const rb_hrtime_t *hrt)
{
if (hrt) {
tv->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
tv->tv_usec = (int32_t)((*hrt % RB_HRTIME_PER_SEC)/RB_HRTIME_PER_USEC);
return tv;
}
return 0;
}
#include "internal/warnings.h"
#include "internal/time.h"
/*
* Back when we used "struct timeval", not all platforms implemented
* tv_sec as time_t. Nowadays we use "struct timespec" and tv_sec
* seems to be implemented more consistently across platforms.
* At least other parts of our code hasn't had to deal with non-time_t
* tv_sec in timespec...
*/
#define TIMESPEC_SEC_MAX TIMET_MAX
#define TIMESPEC_SEC_MIN TIMET_MIN
COMPILER_WARNING_PUSH
#if __has_warning("-Wimplicit-int-float-conversion")
COMPILER_WARNING_IGNORED(-Wimplicit-int-float-conversion)
#elif defined(_MSC_VER)
/* C4305: 'initializing': truncation from '__int64' to 'const double' */
COMPILER_WARNING_IGNORED(4305)
#endif
static const double TIMESPEC_SEC_MAX_as_double = TIMESPEC_SEC_MAX;
COMPILER_WARNING_POP
static inline rb_hrtime_t *
double2hrtime(rb_hrtime_t *hrt, double d)
{
/* assume timespec.tv_sec has same signedness as time_t */
const double TIMESPEC_SEC_MAX_PLUS_ONE = 2.0 * (TIMESPEC_SEC_MAX_as_double / 2.0 + 1.0);
if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
*hrt = RB_HRTIME_MAX;
return NULL;
}
else if (d <= 0) {
*hrt = 0;
}
else {
*hrt = (rb_hrtime_t)(d * (double)RB_HRTIME_PER_SEC);
}
return hrt;
}
static inline double
hrtime2double(rb_hrtime_t hrt)
{
return (double)hrt / (double)RB_HRTIME_PER_SEC;
}
#endif /* RB_HRTIME_H */
|