Add a dispatcher for the alarm event
authorKevin Klues <klueska@cs.berkeley.edu>
Fri, 30 May 2014 23:23:42 +0000 (16:23 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Fri, 30 May 2014 23:23:42 +0000 (16:23 -0700)
Every event has only a single event handler registered for it in the
ev_handlers array.  The alarm dispatcher sets ev_handler[EV_ALARM] to a
dispatcher function, which dispatches alarm events to registered
handlers keyed by alarmid.

This component ws built mostly in preparation for the addition of a
per-vcore alarm service that will need this dispatching functionality.

user/benchutil/alarm.c
user/benchutil/alarm_dispatch.c [new file with mode: 0644]
user/benchutil/include/alarm_dispatch.h [new file with mode: 0644]

index d83a574..eb6737e 100644 (file)
@@ -37,6 +37,7 @@
 #include <uthread.h>
 #include <spinlock.h>
 #include <timing.h>
+#include <alarm_dispatch.h>
 
 /* Helpers, basically renamed kernel interfaces, with the *tchain. */
 static void __tc_locked_set_alarm(struct timer_chain *tchain,
@@ -85,7 +86,7 @@ static void reset_tchain_times(struct timer_chain *tchain)
 
 static void init_alarm_service(void)
 {
-       int ctlfd, timerfd, alarm_nr, ret;
+       int ctlfd, timerfd, alarmid, ret;
        char buf[20];
        char path[32];
        struct event_queue *ev_q;
@@ -112,7 +113,7 @@ static void init_alarm_service(void)
                return;
        }
        buf[ret] = 0;
-       global_tchain.alarmid = atoi(buf);
+       alarmid = atoi(buf);
        snprintf(path, sizeof(path), "#A/a%s/timer", buf);
        timerfd = open(path, O_RDWR | O_CLOEXEC);
        if (timerfd < 0) {
@@ -121,7 +122,7 @@ static void init_alarm_service(void)
        }
        /* Since we're doing SPAM_PUBLIC later, we actually don't need a big ev_q.
         * But someone might copy/paste this and change a flag. */
-       ev_handlers[EV_ALARM] = handle_user_alarm;
+       alarm_dispatch_register(alarmid, handle_user_alarm);
        if (!(ev_q = get_big_event_q())) {
                perror("Useralarm: Failed ev_q");
                return;
@@ -139,6 +140,7 @@ static void init_alarm_service(void)
        }
        /* now the alarm is all set, just need to write the timer whenever we want
         * it to go off. */
+       global_tchain.alarmid = alarmid;
        global_tchain.ctlfd = ctlfd;
        global_tchain.timerfd = timerfd;
        global_tchain.ev_q = ev_q;      /* mostly for debugging */
diff --git a/user/benchutil/alarm_dispatch.c b/user/benchutil/alarm_dispatch.c
new file mode 100644 (file)
index 0000000..ed70097
--- /dev/null
@@ -0,0 +1,77 @@
+/* Copyright (c) 2013 The Regents of the University of California
+ * Kevin Klues <klueska@cs.berkeley.edu>
+ * See LICENSE for details. */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <ros/common.h>
+#include <arch/atomic.h>
+#include <spinlock.h>
+#include <alarm_dispatch.h>
+
+#define GROWTH_INC 10
+
+/* The dispatch data structure.  Holds an array of handlers indexed by an
+ * alarmid.  The size of the array is initally 0 and grows in increments of
+ * GROWTH_INC on demand when a larger alarmid is registering its handler. */
+struct {
+       struct spin_pdr_lock lock;
+       handle_event_t *handlers;
+       int length;
+} dispatch;
+
+/* Dispatch the alarm event to its proper handler */
+static void dispatch_alarm(struct event_msg *ev_msg, unsigned int ev_type)
+{
+       assert(ev_type == EV_ALARM);
+       if (ev_msg) {
+               // There is a slight race here if you don't disable the alarm before
+               // deregistering its handler.  Make sure you do this properly.
+               dispatch.handlers[ev_msg->ev_arg2](ev_msg, ev_type);
+       }
+}
+
+/* Initalize the alarm_dispatcher. This should only be called once. */
+static void init_alarm_dispatch()
+{
+       spin_pdr_init(&dispatch.lock);
+       dispatch.handlers = NULL;
+       dispatch.length = 0;
+       ev_handlers[EV_ALARM] = dispatch_alarm;
+}
+
+/* Grow the handler array if necessary.  The array lock must be held when
+ * calling this function. */
+static void __maybe_grow_handler_array(int index)
+{
+       if (dispatch.length <= index) {
+               int new_size = dispatch.length + GROWTH_INC*(index/GROWTH_INC);
+               dispatch.handlers = realloc(dispatch.handlers, new_size);
+               for (int i=dispatch.length; i<new_size; i++)
+                       dispatch.handlers[i] = NULL;
+               dispatch.length = new_size;
+       }
+}
+
+/* Register an alarm handler for alarmid. Make sure the alarm is inactive
+ * before calling this function. */
+void alarm_dispatch_register(int alarmid, handle_event_t handler)
+{
+       run_once(init_alarm_dispatch());
+
+       spin_pdr_lock(&dispatch.lock);
+       __maybe_grow_handler_array(alarmid);
+       dispatch.handlers[alarmid] = handler;
+       spin_pdr_unlock(&dispatch.lock);
+}
+
+/* Deregister an alarm handler for alarmid. Make sure the alarm is inactive
+ * before calling this function. */
+void alarm_dispatch_deregister(int alarmid)
+{
+       spin_pdr_lock(&dispatch.lock);
+       if (alarmid < dispatch.length)
+               dispatch.handlers[alarmid] = NULL;
+       spin_pdr_unlock(&dispatch.lock);
+}
+
diff --git a/user/benchutil/include/alarm_dispatch.h b/user/benchutil/include/alarm_dispatch.h
new file mode 100644 (file)
index 0000000..dcc7797
--- /dev/null
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013 The Regents of the University of California
+ * Kevin Klues <klueska@cs.berkeley.edu>
+ * See LICENSE for details. */
+
+#ifndef _ALARM_DISPATCH_H
+#define _ALARM_DISPATCH_H
+
+#include <event.h>
+
+/* Register an alarm handler for alarmid. Make sure the alarm is inactive
+ * before calling this function. */
+void alarm_dispatch_register(int alarmid, handle_event_t handler);
+/* Deregister an alarm handler for alarmid. Make sure the alarm is inactive
+ * before calling this function. */
+void alarm_dispatch_deregister(int alarmid);
+
+#endif // _ALARM_DISPATCH_H