Ruby  3.1.4p223 (2023-03-30 revision HEAD)
hrtime.h
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
45 typedef 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  */
58 static inline rb_hrtime_t
59 rb_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  */
78 static inline rb_hrtime_t
79 rb_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  */
97 static inline rb_hrtime_t
98 rb_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  */
109 static inline rb_hrtime_t
110 rb_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  */
120 static inline rb_hrtime_t
121 rb_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  */
130 static inline rb_hrtime_t
131 rb_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  */
142 static inline struct timespec *
143 rb_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  */
157 static inline struct timeval *
158 rb_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 */