Moves some timing func from benchutil to parlib
[akaros.git] / user / benchutil / alarm.c
index 811e998..d83a574 100644 (file)
@@ -22,6 +22,7 @@
  * - printks, and other minor stuff. */
 
 #include <sys/queue.h>
+#include <sys/time.h>
 #include <alarm.h>
 #include <stdio.h>
 #include <assert.h>
@@ -35,6 +36,7 @@
 #include <measure.h>
 #include <uthread.h>
 #include <spinlock.h>
+#include <timing.h>
 
 /* Helpers, basically renamed kernel interfaces, with the *tchain. */
 static void __tc_locked_set_alarm(struct timer_chain *tchain,
@@ -51,6 +53,21 @@ static void handle_user_alarm(struct event_msg *ev_msg, unsigned int ev_type);
 /* One chain to rule them all. */
 struct timer_chain global_tchain;
 
+/* Unix time offsets so we can allow people to specify an absolute unix time to
+ * an alarm, rather than an absolute time in terms of raw tsc ticks.  This
+ * value is initialized when the timer service is started. */
+static struct {
+       uint64_t tod; // The initial time of day in microseconds
+       uint64_t tsc; // The initial value of the tsc counter
+} unixtime_offsets;
+static inline void init_unixtime_offsets()
+{
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       unixtime_offsets.tsc = read_tsc();
+       unixtime_offsets.tod = tv.tv_sec*1000000 + tv.tv_usec;
+}
+
 /* Helper, resets the earliest/latest times, based on the elements of the list.
  * If the list is empty, we set the times to be the 12345 poison time.  Since
  * the list is empty, the alarm shouldn't be going off. */
@@ -73,6 +90,9 @@ static void init_alarm_service(void)
        char path[32];
        struct event_queue *ev_q;
 
+       /* Initialize the unixtime_offsets */
+       init_unixtime_offsets();
+
        /* Sets up timer chain (only one chain per process) */
        spin_pdr_init(&global_tchain.lock);
        TAILQ_INIT(&global_tchain.waiters);
@@ -92,6 +112,7 @@ static void init_alarm_service(void)
                return;
        }
        buf[ret] = 0;
+       global_tchain.alarmid = atoi(buf);
        snprintf(path, sizeof(path), "#A/a%s/timer", buf);
        timerfd = open(path, O_RDWR | O_CLOEXEC);
        if (timerfd < 0) {
@@ -106,7 +127,10 @@ static void init_alarm_service(void)
                return;
        }
        ev_q->ev_vcore = 0;
-       ev_q->ev_flags = EVENT_IPI | EVENT_NOMSG | EVENT_SPAM_PUBLIC;
+       /* We could get multiple events for a single alarm.  It's okay, since
+        * __trigger can handle spurious upcalls.  If it ever is not okay, then use
+        * an INDIR/FALLBACK instead of SPAM_PUBLIC. */
+       ev_q->ev_flags = EVENT_IPI | EVENT_SPAM_PUBLIC;
        ret = snprintf(path, sizeof(path), "evq %llx", ev_q);
        ret = write(ctlfd, path, ret);
        if (ret <= 0) {
@@ -139,6 +163,14 @@ void set_awaiter_abs(struct alarm_waiter *waiter, uint64_t abs_time)
        waiter->wake_up_time = abs_time;
 }
 
+/* Give this the absolute unix time (in microseconds) that you want the alarm
+ * to go off. */
+void set_awaiter_abs_unix(struct alarm_waiter *waiter, uint64_t abs_time)
+{
+       abs_time = usec2tsc(abs_time - unixtime_offsets.tod) + unixtime_offsets.tsc;
+       set_awaiter_abs(waiter, abs_time);
+}
+
 /* Give this a relative time from now, in microseconds.  This might be easier to
  * use than dealing with the TSC. */
 void set_awaiter_rel(struct alarm_waiter *waiter, uint64_t usleep)
@@ -251,7 +283,8 @@ static void __trigger_tchain(struct timer_chain *tchain)
 static void handle_user_alarm(struct event_msg *ev_msg, unsigned int ev_type)
 {
        assert(ev_type == EV_ALARM);
-       __trigger_tchain(&global_tchain);
+       if (ev_msg && (ev_msg->ev_arg2 == global_tchain.alarmid))
+               __trigger_tchain(&global_tchain);
 }
 
 /* Helper, inserts the waiter into the tchain, returning TRUE if we still need
@@ -393,3 +426,20 @@ void print_chain(struct timer_chain *tchain)
               tchain->latest_time);
        spin_pdr_unlock(&tchain->lock);
 }
+
+/* "parlib" alarm handlers */
+void alarm_abort_sysc(struct alarm_waiter *awaiter)
+{
+       struct uthread *uth = awaiter->data;
+       assert(uth);
+       if (!uth->sysc) {
+               /* It's possible the sysc hasn't blocked yet or is in the process of
+                * unblocking, or even has returned, but hasn't cancelled the alarm.
+                * regardless, we request a new alarm (the uthread will cancel us one
+                * way or another). */
+               set_awaiter_inc(awaiter, 1000000);
+               __set_alarm(awaiter);
+               return;
+       }
+       sys_abort_sysc(uth->sysc);
+}