X-Git-Url: http://nitlanguage.org diff --git a/lib/realtime.nit b/lib/realtime.nit index cb62251..31040ba 100644 --- a/lib/realtime.nit +++ b/lib/realtime.nit @@ -10,8 +10,8 @@ # You are allowed to redistribute it and sell it, alone or is a part of # another product. -# Provides the Clock utility class to keep time of real time flow -module realtime +# Services to keep time of the wall clock time +module realtime is ldflags "-lrt" in "C header" `{ #ifdef _POSIX_C_SOURCE @@ -21,17 +21,58 @@ in "C header" `{ #include `} -extern Timespec `{struct timespec*`} +in "C" `{ + +#ifdef __APPLE__ + #include + #if defined(TARGET_OS_IPHONE) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000 + // Preserve compatibility with pre-iOS 10 devices where there is no clock_get_time. + #undef CLOCK_REALTIME + #endif +#endif + +#if (defined(__MACH__) || defined(TARGET_OS_IPHONE)) && !defined(CLOCK_REALTIME) +/* OS X does not have clock_gettime, mascarade it and use clock_get_time + * cf http://stackoverflow.com/questions/11680461/monotonic-clock-on-osx +*/ +#include +#include +#undef CLOCK_REALTIME +#undef CLOCK_MONOTONIC +#define CLOCK_REALTIME CALENDAR_CLOCK +#define CLOCK_MONOTONIC SYSTEM_CLOCK +void nit_clock_gettime(clock_t clock_name, struct timespec *ts) { + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), clock_name, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +} +#else + #define nit_clock_gettime clock_gettime +#endif +`} + +# Elapsed time representation. +private extern class Timespec `{struct timespec*`} + + # Init a new Timespec from `s` seconds and `ns` nanoseconds. new ( s, ns : Int ) `{ struct timespec* tv = malloc( sizeof(struct timespec) ); tv->tv_sec = s; tv->tv_nsec = ns; return tv; `} + + # Init a new Timespec from now. new monotonic_now `{ struct timespec* tv = malloc( sizeof(struct timespec) ); - clock_gettime( CLOCK_MONOTONIC, tv ); + nit_clock_gettime( CLOCK_MONOTONIC, tv ); return tv; `} + + # Init a new Timespec copied from another. new copy_of( other : Timespec ) `{ struct timespec* tv = malloc( sizeof(struct timespec) ); tv->tv_sec = other->tv_sec; @@ -39,62 +80,155 @@ extern Timespec `{struct timespec*`} return tv; `} + # Update `self` clock. fun update `{ - clock_gettime( CLOCK_MONOTONIC, recv ); + nit_clock_gettime(CLOCK_MONOTONIC, self); + `} + + # Subtract `other` from `self` + fun -(other: Timespec): Timespec `{ + time_t s = self->tv_sec - other->tv_sec; + long ns = self->tv_nsec - other->tv_nsec; + if (ns < 0) { + s -= 1; + ns += 1000000000l; + } + struct timespec* tv = malloc(sizeof(struct timespec)); + tv->tv_sec = s; tv->tv_nsec = ns; + return tv; `} - fun - ( o : Timespec ) : Timespec - do - var s = sec - o.sec - var ns = nanosec - o.nanosec - if ns > nanosec then s += 1 - return new Timespec( s, ns ) - end + # Set `self` to `a` - `b` + fun minus(a, b: Timespec) `{ + time_t s = a->tv_sec - b->tv_sec; + long ns = a->tv_nsec - b->tv_nsec; + if (ns < 0) { + s -= 1; + ns += 1000000000l; + } + self->tv_sec = s; + self->tv_nsec = ns; + `} + + # Number of whole seconds of elapsed time. fun sec : Int `{ - return recv->tv_sec; + return self->tv_sec; `} + + # Rest of the elapsed time (a fraction of a second). + # + # Number of nanoseconds. fun nanosec : Int `{ - return recv->tv_nsec; + return self->tv_nsec; + `} + + # Elapsed time in microseconds, with both whole seconds and the rest + # + # May cause an `Int` overflow, use only with a low number of seconds. + fun microsec: Int `{ + return self->tv_sec*1000000 + self->tv_nsec/1000; `} - fun destroy `{ - free( recv ); + # Elapsed time in milliseconds, with both whole seconds and the rest + # + # May cause an `Int` overflow, use only with a low number of seconds. + fun millisec: Int `{ + return self->tv_sec*1000 + self->tv_nsec/1000000; `} + + # Number of seconds as a `Float` + # + # Incurs a loss of precision, but the result is pretty to print. + fun to_f: Float `{ + return (double)self->tv_sec + 0.000000001 * self->tv_nsec; + `} + + redef fun to_s do return "{to_f}s" end # Keeps track of real time +# +# ~~~ +# var clock = new Clock +# +# # sleeping at least 1s +# 1.0.sleep +# assert clock.total >= 1.0 +# assert clock.lapse >= 1.0 +# +# # sleeping at least 5ms +# 0.005.sleep +# assert clock.total >= 1.005 +# assert clock.lapse >= 0.005 +# ~~~ class Clock - # Time at instanciation - protected var time_at_beginning : Timespec + super FinalizableOnce + + # TODO use less mallocs + + # Time at creation + private var time_at_beginning = new Timespec.monotonic_now # Time at last time a lapse method was called - protected var time_at_last_lapse : Timespec + private var time_at_last_lapse = new Timespec.monotonic_now - init - do - time_at_beginning = new Timespec.monotonic_now - time_at_last_lapse = new Timespec.monotonic_now - end + private var temp = new Timespec.monotonic_now # Smallest time frame reported by clock - fun resolution : Timespec `{ + private fun resolution: Timespec `{ struct timespec* tv = malloc( sizeof(struct timespec) ); +#if (defined(__MACH__) || defined(TARGET_OS_IPHONE)) && !defined(CLOCK_REALTIME) + clock_serv_t cclock; + int nsecs; + mach_msg_type_number_t count; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count); + mach_port_deallocate(mach_task_self(), cclock); + tv->tv_sec = 0; + tv->tv_nsec = nsecs; +#else clock_getres( CLOCK_MONOTONIC, tv ); +#endif return tv; `} - # Return timelapse since instanciation of this instance - fun total : Timespec + # Seconds since the creation of this instance + fun total: Float + do + var now = temp + now.update + now.minus(now, time_at_beginning) + return now.to_f + end + + # Seconds since the last call to `lapse` + fun lapse: Float + do + var time_at_last_lapse = time_at_last_lapse + var now = temp + now.update + time_at_last_lapse.minus(now, time_at_last_lapse) + var r = time_at_last_lapse.to_f + + self.temp = time_at_last_lapse + self.time_at_last_lapse = now + + return r + end + + # Seconds since the last call to `lapse`, without resetting the lapse counter + fun peek_lapse: Float do - return new Timespec.monotonic_now - time_at_beginning + var now = temp + now.update + now.minus(now, time_at_last_lapse) + return now.to_f end - # Return timelapse since last call to lapse - fun lapse : Timespec + redef fun finalize_once do - var nt = new Timespec.monotonic_now - var dt = nt - time_at_last_lapse - time_at_last_lapse = nt - return dt + time_at_beginning.free + time_at_last_lapse.free + temp.free end end