tests: add some runtime error in nitin.input
[nit.git] / lib / realtime.nit
index fd0fe86..a400619 100644 (file)
@@ -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 is c_linker_option("-lrt")
+# Services to keep time of the wall clock time
+module realtime is ldflags "-lrt"
 
 in "C header" `{
 #ifdef _POSIX_C_SOURCE
@@ -21,17 +21,46 @@ in "C header" `{
 #include <time.h>
 `}
 
+in "C" `{
+
+#if defined(__MACH__) && !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 <mach/clock.h>
+#include <mach/mach.h>
+#define CLOCK_REALTIME CALENDAR_CLOCK
+#define CLOCK_MONOTONIC SYSTEM_CLOCK
+void 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;
+}
+#endif
+`}
+
+# Elapsed time representation.
 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 );
                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 +68,141 @@ extern class Timespec `{struct timespec*`}
                return tv;
        `}
 
+       # Update `self` clock.
        fun update `{
-               clock_gettime( CLOCK_MONOTONIC, recv );
+               clock_gettime(CLOCK_MONOTONIC, self);
        `}
-       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
 
+       # 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;
+       `}
+
+       # 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;
+       `}
+
+       # 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;
        `}
 
-       fun destroy `{
-               free( recv );
+       # 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
 
-       # Time at last time a lapse method was called
-       protected var time_at_last_lapse : Timespec
+       # TODO use less mallocs
 
-       init
-       do
-               time_at_beginning = new Timespec.monotonic_now
-               time_at_last_lapse = new Timespec.monotonic_now
-       end
+       # Time at creation
+       protected var time_at_beginning = new Timespec.monotonic_now
+
+       # Time at last time a lapse method was called
+       protected var time_at_last_lapse = new Timespec.monotonic_now
 
        # Smallest time frame reported by clock
-       fun resolution : Timespec `{
+       fun resolution: Timespec `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
+#if defined(__MACH__) && !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
-               return new Timespec.monotonic_now - time_at_beginning
+               var now = new Timespec.monotonic_now
+               var diff = now - time_at_beginning
+               var r = diff.to_f
+               diff.free
+               now.free
+               return r
        end
 
-       # Return timelapse since last call to lapse
-       fun lapse : Timespec
+       # Seconds since the last call to `lapse`
+       fun lapse: Float
        do
                var nt = new Timespec.monotonic_now
                var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               dt.free
+               time_at_last_lapse.free
                time_at_last_lapse = nt
-               return dt
+               return r
+       end
+
+       # Seconds since the last call to `lapse`, without resetting the lapse counter
+       fun peek_lapse: Float
+       do
+               var nt = new Timespec.monotonic_now
+               var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               nt.free
+               dt.free
+               return r
+       end
+
+       redef fun finalize_once
+       do
+               time_at_beginning.free
+               time_at_last_lapse.free
        end
 end