Cleans up preempt_pending helper functions
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 15 Dec 2011 00:41:32 +0000 (16:41 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 15 Dec 2011 22:48:43 +0000 (14:48 -0800)
Don't call __check_preempt_pending() from places that can't handle a
noreturn.  Examples include inside event handlers, while processing an
ev_q (you'll miss messages), while holding locks, etc.  If a preempt is
pending, the vcore will yield and may never come back.  Next time it
runs, it'll be starting fresh from vcore_entry().

If needed, we can make versions of event handlers that can handle
responding to a preempt_pending, but those will need to be careful to
not ignore message queues and stuff like that.  One of the side effects
of not returning is that someone might be expecting you to process all
of your messages, etc.

user/parlib/event.c
user/parlib/include/uthread.h
user/parlib/include/vcore.h
user/parlib/uthread.c
user/pthread/pthread.c

index 2f1ec92..8e4acf6 100644 (file)
@@ -184,7 +184,6 @@ static int handle_mbox_msgs(struct event_mbox *ev_mbox)
                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);
-               check_preempt_pending(vcoreid);
                retval++;
        }
        return retval;
@@ -202,7 +201,6 @@ int handle_mbox(struct event_mbox *ev_mbox)
                if (ev_handlers[bit])
                        ev_handlers[bit](0, bit);
                retval++;
-               check_preempt_pending(vcoreid);
                /* Consider checking the queue for incoming messages while we're here */
        }
        printd("[event] handling ev_mbox %08p on vcore %d\n", ev_mbox, vcore_id());
index c5c12cd..3f4ed9a 100644 (file)
@@ -61,7 +61,7 @@ void uthread_yield(bool save_state);
 void ros_syscall_blockon(struct syscall *sysc);
 
 /* Utility functions */
-bool check_preempt_pending(uint32_t vcoreid);
+bool __check_preempt_pending(uint32_t vcoreid);        /* careful: check the code */
 void uth_disable_notifs(void);
 void uth_enable_notifs(void);
 void copyout_uthread(struct preempt_data *vcpd, struct uthread *uthread);
index 23cf5a3..7729097 100644 (file)
@@ -49,6 +49,8 @@ static inline bool notif_is_enabled(uint32_t vcoreid);
 static inline bool vcore_is_mapped(uint32_t vcoreid);
 static inline bool vcore_is_preempted(uint32_t vcoreid);
 static inline struct preempt_data *vcpd_of(uint32_t vcoreid);
+static inline bool preempt_is_pending(uint32_t vcoreid);
+static inline bool __preempt_is_pending(uint32_t vcoreid);
 int vcore_init(void);
 int vcore_request(long nr_new_vcores);
 void vcore_yield(bool preempt_pending);
@@ -116,6 +118,26 @@ static inline struct preempt_data *vcpd_of(uint32_t vcoreid)
        return &__procdata.vcore_preempt_data[vcoreid];
 }
 
+/* Uthread's can call this in case they care if a preemption is coming.  If a
+ * preempt is incoming, this will return TRUE, if you are in uthread context.  A
+ * reasonable response for a uthread is to yield, and vcore_entry will deal with
+ * the preempt pending.
+ *
+ * If you call this from vcore context, it will do nothing.  In general, it's
+ * not safe to just yield (or do whatever you plan on doing) from arbitrary
+ * places in vcore context.  So we just lie about PP. */
+static inline bool preempt_is_pending(uint32_t vcoreid)
+{
+       if (in_vcore_context())
+               return FALSE;
+       return __preempt_is_pending(vcoreid);
+}
+
+static inline bool __preempt_is_pending(uint32_t vcoreid)
+{
+       return __procinfo.vcoremap[vcoreid].preempt_pending;
+}
+
 #ifdef __cplusplus
 }
 #endif
index a798061..c5817bd 100644 (file)
@@ -100,8 +100,8 @@ void __attribute__((noreturn)) uthread_vcore_entry(void)
         * to do this after we've handled STEALING and DONT_MIGRATE. */
        try_handle_remote_mbox();
        /* Otherwise, go about our usual vcore business (messages, etc). */
-       check_preempt_pending(vcoreid);
        handle_events(vcoreid);
+       __check_preempt_pending(vcoreid);
        assert(in_vcore_context());     /* double check, in case an event changed it */
        assert(sched_ops->sched_entry);
        sched_ops->sched_entry();
@@ -385,20 +385,42 @@ static void __run_current_uthread_raw(void)
 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
  * function, it will get run.  Returns true if you got preempted.  Called
  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
- * shit a preempt is on its way ASAP".  While it is isn't too involved with
- * uthreads, it is tied in to sched_ops. */
-bool check_preempt_pending(uint32_t vcoreid)
+ * shit a preempt is on its way ASAP".
+ *
+ * Be careful calling this: you might not return, so don't call it if you can't
+ * handle that.  If you are calling this from an event handler, you'll need to
+ * do things like ev_might_not_return().  If the event can via an INDIR ev_q,
+ * that ev_q must be a NOTHROTTLE.
+ *
+ * Finally, don't call this from a place that might have a DONT_MIGRATE
+ * cur_uth.  This should be safe for most 2LS code. */
+bool __check_preempt_pending(uint32_t vcoreid)
 {
        bool retval = FALSE;
-       if (__procinfo.vcoremap[vcoreid].preempt_pending) {
+       assert(in_vcore_context());
+       if (__preempt_is_pending(vcoreid)) {
                retval = TRUE;
                if (sched_ops->preempt_pending)
                        sched_ops->preempt_pending();
-               /* this tries to yield, but will pop back up if this was a spurious
-                * preempt_pending.  Note this will handle events internally, and then
-                * recurse once per event in the queue.  This sucks, but keeps us from
-                * missing messages for now. */
-               vcore_yield(TRUE);
+               /* If we still have a cur_uth, copy it out and hand it back to the 2LS
+                * before yielding. */
+               if (current_uthread) {
+                       assert(!(current_uthread->flags & UTHREAD_DONT_MIGRATE));
+                       copyout_uthread(vcpd_of(vcoreid), current_uthread);
+                       assert(sched_ops->thread_paused);
+                       sched_ops->thread_paused(current_uthread);
+                       current_uthread = 0;
+               }
+               /* vcore_yield tries to yield, and will pop back up if this was a spurious
+                * preempt_pending or if it handled an event.  For now, we'll just keep
+                * trying to yield so long as a preempt is coming in.  Eventually, we'll
+                * handle all of our events and yield, or else the preemption will hit
+                * and someone will recover us (at which point we'll break out of the
+                * loop) */
+               while (__procinfo.vcoremap[vcoreid].preempt_pending) {
+                       vcore_yield(TRUE);
+                       cpu_relax();
+               }
        }
        return retval;
 }
index cbb2105..4765c6c 100644 (file)
@@ -78,6 +78,7 @@ void __attribute__((noreturn)) pth_sched_entry(void)
         * had a new event come in, one that may make us able to get a new_thread */
        do {
                handle_events(vcoreid);
+               __check_preempt_pending(vcoreid);
                mcs_pdr_lock(&queue_lock);
                new_thread = TAILQ_FIRST(&ready_queue);
                if (new_thread) {