Ruby: hrtime.h Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

hrtime.h
Go to the documentation of this file.
1#ifndef RB_HRTIME_H
2#define RB_HRTIME_H
3#include "ruby/ruby.h"
4#include <time.h>
5#if defined(HAVE_SYS_TIME_H)
6# include <sys/time.h>
7#endif
8
9/*
10 * Hi-res monotonic clock. It is currently nsec resolution, which has over
11 * 500 years of range (with an unsigned 64-bit integer). Developers
12 * targeting small systems may try 32-bit and low-resolution (milliseconds).
13 *
14 * TBD: Is nsec even necessary? usec resolution seems enough for userspace
15 * and it'll be suitable for use with devices lasting over 500,000 years
16 * (maybe some devices designed for long-term space travel)
17 *
18 * Current API:
19 *
20 * * rb_hrtime_now - current clock value (monotonic if available)
21 * * rb_hrtime_mul - multiply with overflow check
22 * * rb_hrtime_add - add with overflow check
23 * * rb_timeval2hrtime - convert from timeval
24 * * rb_timespec2hrtime - convert from timespec
25 * * rb_msec2hrtime - convert from millisecond
26 * * rb_sec2hrtime - convert from time_t (seconds)
27 * * rb_hrtime2timeval - convert to timeval
28 * * rb_hrtime2timespec - convert to timespec
29 *
30 * Note: no conversion to milliseconds is provided here because different
31 * functions have different limits (e.g. epoll_wait vs w32_wait_events).
32 * So we provide RB_HRTIME_PER_MSEC and similar macros for implementing
33 * this for each use case.
34 */
35#define RB_HRTIME_PER_USEC ((rb_hrtime_t)1000)
36#define RB_HRTIME_PER_MSEC (RB_HRTIME_PER_USEC * (rb_hrtime_t)1000)
37#define RB_HRTIME_PER_SEC (RB_HRTIME_PER_MSEC * (rb_hrtime_t)1000)
38#define RB_HRTIME_MAX UINT64_MAX
39
40/*
41 * Lets try to support time travelers. Lets assume anybody with a time machine
42 * also has access to a modern gcc or clang with 128-bit int support
43 */
44#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
45typedef int128_t rb_hrtime_t;
46#else
47 typedef uint64_t rb_hrtime_t;
48#endif
49
50/* thread.c */
51/* returns the value of the monotonic clock (if available) */
52 rb_hrtime_t rb_hrtime_now(void);
53
54/*
55 * multiply @a and @b with overflow check and return the
56 * (clamped to RB_HRTIME_MAX) result.
57 */
58static inline rb_hrtime_t
59rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b)
60{
61 rb_hrtime_t c;
62
63#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
64 if (__builtin_mul_overflow(a, b, &c))
65 return RB_HRTIME_MAX;
66#else
67 if (b != 0 && a > RB_HRTIME_MAX / b) /* overflow */
68 return RB_HRTIME_MAX;
69 c = a * b;
70#endif
71 return c;
72}
73
74/*
75 * add @a and @b with overflow check and return the
76 * (clamped to RB_HRTIME_MAX) result.
77 */
78static inline rb_hrtime_t
79rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b)
80{
81 rb_hrtime_t c;
82
83#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
84 if (__builtin_add_overflow(a, b, &c))
85 return RB_HRTIME_MAX;
86#else
87 c = a + b;
88 if (c < a) /* overflow */
89 return RB_HRTIME_MAX;
90#endif
91 return c;
92}
93
94/*
95 * convert a timeval struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
96 */
97static inline rb_hrtime_t
98rb_timeval2hrtime(const struct timeval *tv)
99{
100 rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)tv->tv_sec, RB_HRTIME_PER_SEC);
101 rb_hrtime_t u = rb_hrtime_mul((rb_hrtime_t)tv->tv_usec, RB_HRTIME_PER_USEC);
102
103 return rb_hrtime_add(s, u);
104}
105
106/*
107 * convert a timespec struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
108 */
109static inline rb_hrtime_t
110rb_timespec2hrtime(const struct timespec *ts)
111{
112 rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)ts->tv_sec, RB_HRTIME_PER_SEC);
113
114 return rb_hrtime_add(s, (rb_hrtime_t)ts->tv_nsec);
115}
116
117/*
118 * convert a millisecond value to rb_hrtime_t, clamping at RB_HRTIME_MAX
119 */
120static inline rb_hrtime_t
121rb_msec2hrtime(unsigned long msec)
122{
123 return rb_hrtime_mul((rb_hrtime_t)msec, RB_HRTIME_PER_MSEC);
124}
125
126/*
127 * convert a time_t value to rb_hrtime_t, clamping at RB_HRTIME_MAX
128 * Negative values will be clamped at 0.
129 */
130static inline rb_hrtime_t
131rb_sec2hrtime(time_t sec)
132{
133 if (sec <= 0) return 0;
134
135 return rb_hrtime_mul((rb_hrtime_t)sec, RB_HRTIME_PER_SEC);
136}
137
138/*
139 * convert a rb_hrtime_t value to a timespec, suitable for calling
140 * functions like ppoll(2) or kevent(2)
141 */
142static inline struct timespec *
143rb_hrtime2timespec(struct timespec *ts, const rb_hrtime_t *hrt)
144{
145 if (hrt) {
146 ts->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
147 ts->tv_nsec = (int32_t)(*hrt % RB_HRTIME_PER_SEC);
148 return ts;
149 }
150 return 0;
151}
152
153/*
154 * convert a rb_hrtime_t value to a timeval, suitable for calling
155 * functions like select(2)
156 */
157static inline struct timeval *
158rb_hrtime2timeval(struct timeval *tv, const rb_hrtime_t *hrt)
159{
160 if (hrt) {
161 tv->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
162 tv->tv_usec = (int32_t)((*hrt % RB_HRTIME_PER_SEC)/RB_HRTIME_PER_USEC);
163
164 return tv;
165 }
166 return 0;
167}
168#endif /* RB_HRTIME_H */
RB_HRTIME_PER_USEC
#define RB_HRTIME_PER_USEC
Definition: hrtime.h:35
RB_HRTIME_MAX
#define RB_HRTIME_MAX
Definition: hrtime.h:38
rb_hrtime_now
rb_hrtime_t rb_hrtime_now(void)
Definition: thread.c:1439
RB_HRTIME_PER_MSEC
#define RB_HRTIME_PER_MSEC
Definition: hrtime.h:36
rb_hrtime_t
uint64_t rb_hrtime_t
Definition: hrtime.h:47
RB_HRTIME_PER_SEC
#define RB_HRTIME_PER_SEC
Definition: hrtime.h:37
ruby.h
uint64_t
unsigned long long uint64_t
Definition: sha2.h:102
timespec
Definition: missing.h:62
timespec::tv_nsec
long tv_nsec
Definition: missing.h:64
timespec::tv_sec
time_t tv_sec
Definition: missing.h:63
timeval
Definition: missing.h:51
timeval::tv_usec
long tv_usec
Definition: missing.h:53
timeval::tv_sec
time_t tv_sec
Definition: missing.h:52