5 #if defined(HAVE_SYS_TIME_H)
9 #include "internal/compilers.h"
12 * Hi-res monotonic clock. It is currently nsec resolution, which has over
13 * 500 years of range (with an unsigned 64-bit integer). Developers
14 * targeting small systems may try 32-bit and low-resolution (milliseconds).
16 * TBD: Is nsec even necessary? usec resolution seems enough for userspace
17 * and it'll be suitable for use with devices lasting over 500,000 years
18 * (maybe some devices designed for long-term space travel)
22 * * rb_hrtime_now - current clock value (monotonic if available)
23 * * rb_hrtime_mul - multiply with overflow check
24 * * rb_hrtime_add - add with overflow check
25 * * rb_timeval2hrtime - convert from timeval
26 * * rb_timespec2hrtime - convert from timespec
27 * * rb_msec2hrtime - convert from millisecond
28 * * rb_sec2hrtime - convert from time_t (seconds)
29 * * rb_hrtime2timeval - convert to timeval
30 * * rb_hrtime2timespec - convert to timespec
32 * Note: no conversion to milliseconds is provided here because different
33 * functions have different limits (e.g. epoll_wait vs w32_wait_events).
34 * So we provide RB_HRTIME_PER_MSEC and similar macros for implementing
35 * this for each use case.
37 #define RB_HRTIME_PER_USEC ((rb_hrtime_t)1000)
38 #define RB_HRTIME_PER_MSEC (RB_HRTIME_PER_USEC * (rb_hrtime_t)1000)
39 #define RB_HRTIME_PER_SEC (RB_HRTIME_PER_MSEC * (rb_hrtime_t)1000)
40 #define RB_HRTIME_MAX UINT64_MAX
41 #define RB_HRTIME_MIN ((rb_hrtime_t)0)
44 * Lets try to support time travelers. Lets assume anybody with a time machine
45 * also has access to a modern gcc or clang with 128-bit int support
47 #ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
48 typedef int128_t rb_hrtime_t
;
50 typedef uint64_t rb_hrtime_t
;
54 /* returns the value of the monotonic clock (if available) */
55 rb_hrtime_t
rb_hrtime_now(void);
58 * multiply @a and @b with overflow check and return the
59 * (clamped to RB_HRTIME_MAX) result.
61 static inline rb_hrtime_t
62 rb_hrtime_mul(rb_hrtime_t a
, rb_hrtime_t b
)
67 if (ckd_mul(&c
, a
, b
))
70 #elif __has_builtin(__builtin_mul_overflow)
71 if (__builtin_mul_overflow(a
, b
, &c
))
74 if (b
!= 0 && a
> RB_HRTIME_MAX
/ b
) /* overflow */
82 * add @a and @b with overflow check and return the
83 * (clamped to RB_HRTIME_MAX) result.
85 static inline rb_hrtime_t
86 rb_hrtime_add(rb_hrtime_t a
, rb_hrtime_t b
)
91 if (ckd_add(&c
, a
, b
))
94 #elif __has_builtin(__builtin_add_overflow)
95 if (__builtin_add_overflow(a
, b
, &c
))
99 if (c
< a
) /* overflow */
100 return RB_HRTIME_MAX
;
105 static inline rb_hrtime_t
106 rb_hrtime_sub(rb_hrtime_t a
, rb_hrtime_t b
)
109 return RB_HRTIME_MIN
;
115 * convert a timeval struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
117 static inline rb_hrtime_t
118 rb_timeval2hrtime(const struct timeval
*tv
)
120 rb_hrtime_t s
= rb_hrtime_mul((rb_hrtime_t
)tv
->tv_sec
, RB_HRTIME_PER_SEC
);
121 rb_hrtime_t u
= rb_hrtime_mul((rb_hrtime_t
)tv
->tv_usec
, RB_HRTIME_PER_USEC
);
123 return rb_hrtime_add(s
, u
);
127 * convert a timespec struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
129 static inline rb_hrtime_t
130 rb_timespec2hrtime(const struct timespec
*ts
)
132 rb_hrtime_t s
= rb_hrtime_mul((rb_hrtime_t
)ts
->tv_sec
, RB_HRTIME_PER_SEC
);
134 return rb_hrtime_add(s
, (rb_hrtime_t
)ts
->tv_nsec
);
138 * convert a millisecond value to rb_hrtime_t, clamping at RB_HRTIME_MAX
140 static inline rb_hrtime_t
141 rb_msec2hrtime(unsigned long msec
)
143 return rb_hrtime_mul((rb_hrtime_t
)msec
, RB_HRTIME_PER_MSEC
);
147 * convert a time_t value to rb_hrtime_t, clamping at RB_HRTIME_MAX
148 * Negative values will be clamped at 0.
150 static inline rb_hrtime_t
151 rb_sec2hrtime(time_t sec
)
153 if (sec
<= 0) return 0;
155 return rb_hrtime_mul((rb_hrtime_t
)sec
, RB_HRTIME_PER_SEC
);
159 * convert a rb_hrtime_t value to a timespec, suitable for calling
160 * functions like ppoll(2) or kevent(2)
162 static inline struct timespec
*
163 rb_hrtime2timespec(struct timespec
*ts
, const rb_hrtime_t
*hrt
)
166 ts
->tv_sec
= (time_t)(*hrt
/ RB_HRTIME_PER_SEC
);
167 ts
->tv_nsec
= (int32_t)(*hrt
% RB_HRTIME_PER_SEC
);
174 * convert a rb_hrtime_t value to a timeval, suitable for calling
175 * functions like select(2)
177 static inline struct timeval
*
178 rb_hrtime2timeval(struct timeval
*tv
, const rb_hrtime_t
*hrt
)
181 tv
->tv_sec
= (time_t)(*hrt
/ RB_HRTIME_PER_SEC
);
182 tv
->tv_usec
= (int32_t)((*hrt
% RB_HRTIME_PER_SEC
)/RB_HRTIME_PER_USEC
);
189 #include "internal/warnings.h"
190 #include "internal/time.h"
193 * Back when we used "struct timeval", not all platforms implemented
194 * tv_sec as time_t. Nowadays we use "struct timespec" and tv_sec
195 * seems to be implemented more consistently across platforms.
196 * At least other parts of our code hasn't had to deal with non-time_t
197 * tv_sec in timespec...
199 #define TIMESPEC_SEC_MAX TIMET_MAX
200 #define TIMESPEC_SEC_MIN TIMET_MIN
202 COMPILER_WARNING_PUSH
203 #if __has_warning("-Wimplicit-int-float-conversion")
204 COMPILER_WARNING_IGNORED(-Wimplicit
-int-float-conversion
)
205 #elif defined(_MSC_VER)
206 /* C4305: 'initializing': truncation from '__int64' to 'const double' */
207 COMPILER_WARNING_IGNORED(4305)
209 static const double TIMESPEC_SEC_MAX_as_double
= TIMESPEC_SEC_MAX
;
212 static inline rb_hrtime_t
*
213 double2hrtime(rb_hrtime_t
*hrt
, double d
)
215 /* assume timespec.tv_sec has same signedness as time_t */
216 const double TIMESPEC_SEC_MAX_PLUS_ONE
= 2.0 * (TIMESPEC_SEC_MAX_as_double
/ 2.0 + 1.0);
218 if (TIMESPEC_SEC_MAX_PLUS_ONE
<= d
) {
219 *hrt
= RB_HRTIME_MAX
;
226 *hrt
= (rb_hrtime_t
)(d
* (double)RB_HRTIME_PER_SEC
);
232 hrtime2double(rb_hrtime_t hrt
)
234 return (double)hrt
/ (double)RB_HRTIME_PER_SEC
;
237 #endif /* RB_HRTIME_H */