Event handling can have multiple handlers
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 3 Jun 2014 20:23:12 +0000 (13:23 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 3 Jun 2014 20:23:12 +0000 (13:23 -0700)
Events have a type (integer), like IRQs.  We can now have multiple handlers per
event type, all of which are executed for each event.

tests/alarm.c
tests/lock_test.c
tests/mhello.c
tests/old/syscall.c
user/benchutil/alarm_dispatch.c
user/parlib/event.c
user/parlib/include/event.h
user/parlib/signal.c
user/parlib/uthread.c
user/pthread/pthread.c

index 4ab4447..122d418 100644 (file)
@@ -61,7 +61,7 @@ int main(int argc, char **argv)
        }
        /* 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_alarm;
+       register_ev_handler(EV_ALARM, handle_alarm, 0);
        if (!(ev_q = get_big_event_q())) {
                perror("Failed ev_q");  /* it'll actually PF if malloc fails */
                exit(-1);
index b3d45a4..cbcf333 100644 (file)
@@ -666,8 +666,9 @@ static void os_prep_work(int nr_threads)
        pthread_can_vcore_request(FALSE);       /* 2LS won't manage vcores */
        pthread_need_tls(FALSE);
        pthread_lib_init();                                     /* gives us one vcore */
-       ev_handlers[EV_VCORE_PREEMPT] = handle_preempt;
-       ev_handlers[EV_CHECK_MSGS] = handle_indir;
+       /* TODO: register tracing handlers (old style was replacing) */
+//     register_ev_handler(EV_VCORE_PREEMPT, handle_preempt, 0);
+//     register_ev_handler(EV_CHECK_MSGS, handle_indir, 0);
        if (pargs.fake_vc_ctx) {
                /* need to disable events when faking vc ctx.  since we're looping and
                 * not handling events, we could run OOM */
index 370f47b..431f050 100644 (file)
@@ -59,9 +59,9 @@ int main(int argc, char** argv)
        /* handle events: just want to print out what we get.  This is just a
         * quick set of handlers, not a registration for a kevent. */
        for (int i = 0; i < MAX_NR_EVENT; i++)
-               ev_handlers[i] = handle_generic;
+               register_ev_handler(i, handle_generic, 0);
        /* Want to use the default ev_ev (which we just overwrote) */
-       ev_handlers[EV_EVENT] = handle_ev_ev;
+       register_ev_handler(EV_EVENT, handle_ev_ev, 0);
        /* vcore_init() done in vcore_request() now. */
        /* Set up event reception.  For example, this will allow us to receive an
         * event and IPI for USER_IPIs on vcore 0.  Check event.c for more stuff.
index c64370d..4f8513f 100644 (file)
@@ -26,7 +26,7 @@ int main(int argc, char** argv)
        unsigned int ev_type;
 
        /* register our syscall handler (2LS does this) */
-       ev_handlers[EV_SYSCALL] = handle_syscall;
+       register_ev_handler(EV_SYSCALL, handle_syscall, 0);
 
        printf("Trying to block\n");
        /* Not doing anything else to it: no EVENT_IPI yet, etc. */
index 0ce7e97..56e417c 100644 (file)
@@ -40,7 +40,7 @@ static void init_alarm_dispatch()
        spin_pdr_init(&dispatch.lock);
        dispatch.handlers = NULL;
        dispatch.length = 0;
-       ev_handlers[EV_ALARM] = dispatch_alarm;
+       register_ev_handler(EV_ALARM, dispatch_alarm, 0);
 }
 
 /* Grow the handler array if necessary.  The array lock must be held when
index 52e3ad0..6407591 100644 (file)
@@ -17,6 +17,7 @@
 #include <parlib.h>
 #include <event.h>
 #include <uthread.h>
+#include <spinlock.h>
 
 /* For remote VCPD mbox event handling */
 __thread bool __vc_handle_an_mbox = FALSE;
@@ -164,9 +165,44 @@ unsigned int get_event_type(struct event_mbox *ev_mbox)
 
 /* Actual Event Handling */
 
-/* List of handlers, process-wide, that the 2LS should fill in.  They all must
- * return (don't context switch to a u_thread) */
-handle_event_t ev_handlers[MAX_NR_EVENT];
+/* List of handler lists, process-wide.  They all must return (don't context
+ * switch to a u_thread) */
+struct ev_handler *ev_handlers[MAX_NR_EVENT] = {0};
+spinpdrlock_t ev_h_wlock = SPINPDR_INITIALIZER;
+
+int register_ev_handler(unsigned int ev_type, handle_event_t handler,
+                        void *data)
+{
+       struct ev_handler *new_h = malloc(sizeof(struct ev_handler));
+       if (!new_h)
+               return -1;
+       new_h->func = handler;
+       new_h->data = data;
+       spin_pdr_lock(&ev_h_wlock);
+       new_h->next = ev_handlers[ev_type];
+       wmb();  /* make sure new_h is done before publishing to readers */
+       ev_handlers[ev_type] = new_h;
+       spin_pdr_unlock(&ev_h_wlock);
+       return 0;
+}
+
+int deregister_ev_handler(unsigned int ev_type, handle_event_t handler,
+                          void *data)
+{
+       /* TODO: User-level RCU */
+       printf("Failed to dereg handler, not supported yet!\n");
+}
+
+static void run_ev_handlers(unsigned int ev_type, struct event_msg *ev_msg)
+{
+       struct ev_handler *handler;
+       /* TODO: RCU read lock */
+       handler = ev_handlers[ev_type];
+       while (handler) {
+               handler->func(ev_msg, ev_type, handler->data);
+               handler = handler->next;
+       }
+}
 
 /* Attempts to handle a message.  Returns 1 if we dequeued a msg, 0 o/w. */
 int handle_one_mbox_msg(struct event_mbox *ev_mbox)
@@ -179,8 +215,7 @@ int handle_one_mbox_msg(struct event_mbox *ev_mbox)
        ev_type = local_msg.ev_type;
        assert(ev_type < MAX_NR_EVENT);
        printd("[event] UCQ (mbox %08p), ev_type: %d\n", ev_mbox, ev_type);
-       if (ev_handlers[ev_type])
-               ev_handlers[ev_type](&local_msg, ev_type, 0);
+       run_ev_handlers(ev_type, &local_msg);
        return 1;
 }
 
@@ -193,8 +228,7 @@ int handle_mbox(struct event_mbox *ev_mbox)
        uint32_t vcoreid = vcore_id();
        void bit_handler(unsigned int bit) {
                printd("[event] Bit: ev_type: %d\n", bit);
-               if (ev_handlers[bit])
-                       ev_handlers[bit](0, bit, 0);
+               run_ev_handlers(bit, 0);
                retval = 1;
                /* Consider checking the queue for incoming messages while we're here */
        }
index a785fe2..934f15a 100644 (file)
@@ -27,11 +27,18 @@ struct event_queue *disable_kevent(unsigned int ev_type);
 /********* Event Handling / Reception ***********/
 unsigned int get_event_type(struct event_mbox *ev_mbox);
 
-/* List of handlers, process-wide, that the 2LS should fill in.  They all must
- * return (don't context switch to a u_thread) */
 typedef void (*handle_event_t)(struct event_msg *ev_msg, unsigned int ev_type,
                                void *data);
-extern handle_event_t ev_handlers[];
+struct ev_handler {
+       struct ev_handler                       *next;
+       handle_event_t                          func;
+       void                                            *data;
+};
+int register_ev_handler(unsigned int ev_type, handle_event_t handler,
+                        void *data);
+int deregister_ev_handler(unsigned int ev_type, handle_event_t handler,
+                          void *data);
+
 /* Default event handlers */
 void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type, void *data);
 
index bda3a5e..473d82d 100644 (file)
@@ -156,7 +156,7 @@ static void handle_event(struct event_msg *ev_msg, unsigned int ev_type,
 void init_posix_signals(void)
 {
        struct event_queue *posix_sig_ev_q;
-       ev_handlers[EV_POSIX_SIGNAL] = handle_event;
+       register_ev_handler(EV_POSIX_SIGNAL, handle_event, 0);
        posix_sig_ev_q = get_big_event_q();
        assert(posix_sig_ev_q);
        posix_sig_ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
index cb2cf23..a588765 100644 (file)
@@ -73,7 +73,7 @@ void uthread_lib_init(struct uthread *uthread)
        init_once_racy(return);
        vcore_init();
        uthread_manage_thread0(uthread);
-       ev_handlers[EV_EVENT] = handle_ev_ev;
+       register_ev_handler(EV_EVENT, handle_ev_ev, 0);
        /* Receive preemption events.  Note that this merely tells the kernel how to
         * send the messages, and does not necessarily provide storage space for the
         * messages.  What we're doing is saying that all PREEMPT and CHECK_MSGS
@@ -82,8 +82,8 @@ void uthread_lib_init(struct uthread *uthread)
         *
         * It is critical that these are either SPAM_PUB or INDIR|FALLBACK, so that
         * yielding vcores do not miss the preemption messages. */
-       ev_handlers[EV_VCORE_PREEMPT] = handle_vc_preempt;
-       ev_handlers[EV_CHECK_MSGS] = handle_vc_indir;
+       register_ev_handler(EV_VCORE_PREEMPT, handle_vc_preempt, 0);
+       register_ev_handler(EV_CHECK_MSGS, handle_vc_indir, 0);
        preempt_ev_q = get_event_q();   /* small ev_q, mostly a vehicle for flags */
        preempt_ev_q->ev_flags = EVENT_IPI | EVENT_SPAM_PUBLIC | EVENT_VCORE_APPRO |
                                                         EVENT_VCORE_MUST_RUN;
index 56cc8ea..52400c3 100644 (file)
@@ -445,7 +445,7 @@ void pthread_lib_init(void)
        enable_kevent(EV_USER_IPI, 0, EVENT_IPI | EVENT_VCORE_PRIVATE);
 
        /* Handle syscall events. */
-       ev_handlers[EV_SYSCALL] = pth_handle_syscall;
+       register_ev_handler(EV_SYSCALL, pth_handle_syscall, 0);
        /* Set up the per-vcore structs to track outstanding syscalls */
        sysc_mgmt = malloc(sizeof(struct sysc_mgmt) * max_vcores());
        assert(sysc_mgmt);