Merge: curl: basic Unix domain socket support
[nit.git] / lib / realtime.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # This file is free software, which comes along with NIT. This software is
6 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 # is kept unaltered, and a notification of the changes is added.
10 # You are allowed to redistribute it and sell it, alone or is a part of
11 # another product.
12
13 # Services to keep time of the wall clock time
14 module realtime is ldflags "-lrt"
15
16 in "C header" `{
17 #ifdef _POSIX_C_SOURCE
18 #undef _POSIX_C_SOURCE
19 #endif
20 #define _POSIX_C_SOURCE 199309L
21 #include <time.h>
22 `}
23
24 in "C" `{
25
26 #ifdef __APPLE__
27 #include <TargetConditionals.h>
28 #if defined(TARGET_OS_IPHONE) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
29 // Preserve compatibility with pre-iOS 10 devices where there is no clock_get_time.
30 #undef CLOCK_REALTIME
31 #endif
32 #endif
33
34 #if (defined(__MACH__) || defined(TARGET_OS_IPHONE)) && !defined(CLOCK_REALTIME)
35 /* OS X does not have clock_gettime, mascarade it and use clock_get_time
36 * cf http://stackoverflow.com/questions/11680461/monotonic-clock-on-osx
37 */
38 #include <mach/clock.h>
39 #include <mach/mach.h>
40 #undef CLOCK_REALTIME
41 #undef CLOCK_MONOTONIC
42 #define CLOCK_REALTIME CALENDAR_CLOCK
43 #define CLOCK_MONOTONIC SYSTEM_CLOCK
44 void nit_clock_gettime(clock_t clock_name, struct timespec *ts) {
45 clock_serv_t cclock;
46 mach_timespec_t mts;
47 host_get_clock_service(mach_host_self(), clock_name, &cclock);
48 clock_get_time(cclock, &mts);
49 mach_port_deallocate(mach_task_self(), cclock);
50 ts->tv_sec = mts.tv_sec;
51 ts->tv_nsec = mts.tv_nsec;
52 }
53 #else
54 #define nit_clock_gettime clock_gettime
55 #endif
56 `}
57
58 # Elapsed time representation.
59 private extern class Timespec `{struct timespec*`}
60
61 # Init a new Timespec from `s` seconds and `ns` nanoseconds.
62 new ( s, ns : Int ) `{
63 struct timespec* tv = malloc( sizeof(struct timespec) );
64 tv->tv_sec = s; tv->tv_nsec = ns;
65 return tv;
66 `}
67
68 # Init a new Timespec from now.
69 new monotonic_now `{
70 struct timespec* tv = malloc( sizeof(struct timespec) );
71 nit_clock_gettime( CLOCK_MONOTONIC, tv );
72 return tv;
73 `}
74
75 # Init a new Timespec copied from another.
76 new copy_of( other : Timespec ) `{
77 struct timespec* tv = malloc( sizeof(struct timespec) );
78 tv->tv_sec = other->tv_sec;
79 tv->tv_nsec = other->tv_nsec;
80 return tv;
81 `}
82
83 # Update `self` clock.
84 fun update `{
85 nit_clock_gettime(CLOCK_MONOTONIC, self);
86 `}
87
88 # Subtract `other` from `self`
89 fun -(other: Timespec): Timespec `{
90 time_t s = self->tv_sec - other->tv_sec;
91 long ns = self->tv_nsec - other->tv_nsec;
92 if (ns < 0) {
93 s -= 1;
94 ns += 1000000000l;
95 }
96 struct timespec* tv = malloc(sizeof(struct timespec));
97 tv->tv_sec = s; tv->tv_nsec = ns;
98 return tv;
99 `}
100
101 # Set `self` to `a` - `b`
102 fun minus(a, b: Timespec) `{
103 time_t s = a->tv_sec - b->tv_sec;
104 long ns = a->tv_nsec - b->tv_nsec;
105 if (ns < 0) {
106 s -= 1;
107 ns += 1000000000l;
108 }
109 self->tv_sec = s;
110 self->tv_nsec = ns;
111 `}
112
113 # Number of whole seconds of elapsed time.
114 fun sec : Int `{
115 return self->tv_sec;
116 `}
117
118 # Rest of the elapsed time (a fraction of a second).
119 #
120 # Number of nanoseconds.
121 fun nanosec : Int `{
122 return self->tv_nsec;
123 `}
124
125 # Elapsed time in microseconds, with both whole seconds and the rest
126 #
127 # May cause an `Int` overflow, use only with a low number of seconds.
128 fun microsec: Int `{
129 return self->tv_sec*1000000 + self->tv_nsec/1000;
130 `}
131
132 # Elapsed time in milliseconds, with both whole seconds and the rest
133 #
134 # May cause an `Int` overflow, use only with a low number of seconds.
135 fun millisec: Int `{
136 return self->tv_sec*1000 + self->tv_nsec/1000000;
137 `}
138
139 # Number of seconds as a `Float`
140 #
141 # Incurs a loss of precision, but the result is pretty to print.
142 fun to_f: Float `{
143 return (double)self->tv_sec + 0.000000001 * self->tv_nsec;
144 `}
145
146 redef fun to_s do return "{to_f}s"
147 end
148
149 # Keeps track of real time
150 #
151 # ~~~
152 # var clock = new Clock
153 #
154 # # sleeping at least 1s
155 # 1.0.sleep
156 # assert clock.total >= 1.0
157 # assert clock.lapse >= 1.0
158 #
159 # # sleeping at least 5ms
160 # 0.005.sleep
161 # assert clock.total >= 1.005
162 # assert clock.lapse >= 0.005
163 # ~~~
164 class Clock
165 super FinalizableOnce
166
167 # TODO use less mallocs
168
169 # Time at creation
170 private var time_at_beginning = new Timespec.monotonic_now
171
172 # Time at last time a lapse method was called
173 private var time_at_last_lapse = new Timespec.monotonic_now
174
175 private var temp = new Timespec.monotonic_now
176
177 # Smallest time frame reported by clock
178 private fun resolution: Timespec `{
179 struct timespec* tv = malloc( sizeof(struct timespec) );
180 #if (defined(__MACH__) || defined(TARGET_OS_IPHONE)) && !defined(CLOCK_REALTIME)
181 clock_serv_t cclock;
182 int nsecs;
183 mach_msg_type_number_t count;
184 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
185 clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
186 mach_port_deallocate(mach_task_self(), cclock);
187 tv->tv_sec = 0;
188 tv->tv_nsec = nsecs;
189 #else
190 clock_getres( CLOCK_MONOTONIC, tv );
191 #endif
192 return tv;
193 `}
194
195 # Seconds since the creation of this instance
196 fun total: Float
197 do
198 var now = temp
199 now.update
200 now.minus(now, time_at_beginning)
201 return now.to_f
202 end
203
204 # Seconds since the last call to `lapse`
205 fun lapse: Float
206 do
207 var time_at_last_lapse = time_at_last_lapse
208 var now = temp
209 now.update
210 time_at_last_lapse.minus(now, time_at_last_lapse)
211 var r = time_at_last_lapse.to_f
212
213 self.temp = time_at_last_lapse
214 self.time_at_last_lapse = now
215
216 return r
217 end
218
219 # Seconds since the last call to `lapse`, without resetting the lapse counter
220 fun peek_lapse: Float
221 do
222 var now = temp
223 now.update
224 now.minus(now, time_at_last_lapse)
225 return now.to_f
226 end
227
228 redef fun finalize_once
229 do
230 time_at_beginning.free
231 time_at_last_lapse.free
232 temp.free
233 end
234 end