additional tests for `do catch` structure
[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 #if defined(__MACH__) && !defined(CLOCK_REALTIME)
27 /* OS X does not have clock_gettime, mascarade it and use clock_get_time
28 * cf http://stackoverflow.com/questions/11680461/monotonic-clock-on-osx
29 */
30 #include <mach/clock.h>
31 #include <mach/mach.h>
32 #define CLOCK_REALTIME CALENDAR_CLOCK
33 #define CLOCK_MONOTONIC SYSTEM_CLOCK
34 void clock_gettime(clock_t clock_name, struct timespec *ts) {
35 clock_serv_t cclock;
36 mach_timespec_t mts;
37 host_get_clock_service(mach_host_self(), clock_name, &cclock);
38 clock_get_time(cclock, &mts);
39 mach_port_deallocate(mach_task_self(), cclock);
40 ts->tv_sec = mts.tv_sec;
41 ts->tv_nsec = mts.tv_nsec;
42 }
43 #endif
44 `}
45
46 # Elapsed time representation.
47 private extern class Timespec `{struct timespec*`}
48
49 # Init a new Timespec from `s` seconds and `ns` nanoseconds.
50 new ( s, ns : Int ) `{
51 struct timespec* tv = malloc( sizeof(struct timespec) );
52 tv->tv_sec = s; tv->tv_nsec = ns;
53 return tv;
54 `}
55
56 # Init a new Timespec from now.
57 new monotonic_now `{
58 struct timespec* tv = malloc( sizeof(struct timespec) );
59 clock_gettime( CLOCK_MONOTONIC, tv );
60 return tv;
61 `}
62
63 # Init a new Timespec copied from another.
64 new copy_of( other : Timespec ) `{
65 struct timespec* tv = malloc( sizeof(struct timespec) );
66 tv->tv_sec = other->tv_sec;
67 tv->tv_nsec = other->tv_nsec;
68 return tv;
69 `}
70
71 # Update `self` clock.
72 fun update `{
73 clock_gettime(CLOCK_MONOTONIC, self);
74 `}
75
76 # Subtract `other` from `self`
77 fun -(other: Timespec): Timespec `{
78 time_t s = self->tv_sec - other->tv_sec;
79 long ns = self->tv_nsec - other->tv_nsec;
80 if (ns < 0) {
81 s -= 1;
82 ns += 1000000000l;
83 }
84 struct timespec* tv = malloc(sizeof(struct timespec));
85 tv->tv_sec = s; tv->tv_nsec = ns;
86 return tv;
87 `}
88
89 # Set `self` to `a` - `b`
90 fun minus(a, b: Timespec) `{
91 time_t s = a->tv_sec - b->tv_sec;
92 long ns = a->tv_nsec - b->tv_nsec;
93 if (ns < 0) {
94 s -= 1;
95 ns += 1000000000l;
96 }
97 self->tv_sec = s;
98 self->tv_nsec = ns;
99 `}
100
101 # Number of whole seconds of elapsed time.
102 fun sec : Int `{
103 return self->tv_sec;
104 `}
105
106 # Rest of the elapsed time (a fraction of a second).
107 #
108 # Number of nanoseconds.
109 fun nanosec : Int `{
110 return self->tv_nsec;
111 `}
112
113 # Elapsed time in microseconds, with both whole seconds and the rest
114 #
115 # May cause an `Int` overflow, use only with a low number of seconds.
116 fun microsec: Int `{
117 return self->tv_sec*1000000 + self->tv_nsec/1000;
118 `}
119
120 # Elapsed time in milliseconds, with both whole seconds and the rest
121 #
122 # May cause an `Int` overflow, use only with a low number of seconds.
123 fun millisec: Int `{
124 return self->tv_sec*1000 + self->tv_nsec/1000000;
125 `}
126
127 # Number of seconds as a `Float`
128 #
129 # Incurs a loss of precision, but the result is pretty to print.
130 fun to_f: Float `{
131 return (double)self->tv_sec + 0.000000001 * self->tv_nsec;
132 `}
133
134 redef fun to_s do return "{to_f}s"
135 end
136
137 # Keeps track of real time
138 #
139 # ~~~
140 # var clock = new Clock
141 #
142 # # sleeping at least 1s
143 # 1.0.sleep
144 # assert clock.total >= 1.0
145 # assert clock.lapse >= 1.0
146 #
147 # # sleeping at least 5ms
148 # 0.005.sleep
149 # assert clock.total >= 1.005
150 # assert clock.lapse >= 0.005
151 # ~~~
152 class Clock
153 super FinalizableOnce
154
155 # TODO use less mallocs
156
157 # Time at creation
158 private var time_at_beginning = new Timespec.monotonic_now
159
160 # Time at last time a lapse method was called
161 private var time_at_last_lapse = new Timespec.monotonic_now
162
163 private var temp = new Timespec.monotonic_now
164
165 # Smallest time frame reported by clock
166 private fun resolution: Timespec `{
167 struct timespec* tv = malloc( sizeof(struct timespec) );
168 #if defined(__MACH__) && !defined(CLOCK_REALTIME)
169 clock_serv_t cclock;
170 int nsecs;
171 mach_msg_type_number_t count;
172 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
173 clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
174 mach_port_deallocate(mach_task_self(), cclock);
175 tv->tv_sec = 0;
176 tv->tv_nsec = nsecs;
177 #else
178 clock_getres( CLOCK_MONOTONIC, tv );
179 #endif
180 return tv;
181 `}
182
183 # Seconds since the creation of this instance
184 fun total: Float
185 do
186 var now = temp
187 now.update
188 now.minus(now, time_at_beginning)
189 return now.to_f
190 end
191
192 # Seconds since the last call to `lapse`
193 fun lapse: Float
194 do
195 var time_at_last_lapse = time_at_last_lapse
196 var now = temp
197 now.update
198 time_at_last_lapse.minus(now, time_at_last_lapse)
199 var r = time_at_last_lapse.to_f
200
201 self.temp = time_at_last_lapse
202 self.time_at_last_lapse = now
203
204 return r
205 end
206
207 # Seconds since the last call to `lapse`, without resetting the lapse counter
208 fun peek_lapse: Float
209 do
210 var now = temp
211 now.update
212 now.minus(now, time_at_last_lapse)
213 return now.to_f
214 end
215
216 redef fun finalize_once
217 do
218 time_at_beginning.free
219 time_at_last_lapse.free
220 temp.free
221 end
222 end