Module: xenomai-forge
Branch: master
Commit: dcfd4d53baa1c55c37f994ec85ba25c625eb0002
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=dcfd4d53baa1c55c37f994ec85ba25c625eb0002

Author: Philippe Gerum <r...@xenomai.org>
Date:   Thu Mar 22 22:41:08 2012 +0100

copperplate: synchronize threadobj_start() with target thread

A thread started with high priority should enter the user code before
threadobj_start() returns to a low priority caller. IOW, we must avoid
priority inversions due to some resources not being immediately
available to the started thread when it executes the internal
prologue, causing such thread to block or be demoted priority-wise.

This is particularly required when running over the Cobalt core, when
the started thread has to migrate to secondary mode for running
portions of the prologue code, in which case its priority is
automatically demoted.

This patch introduces the required synchronization between the
starting thread and the threadobj_start() caller, only when the latter
has a strictly lower priority than the former. In all other cases, the
thread issuing threadobj_start() may keep running without
synchronizing.

NOTE: this mechanism makes most sense in uniprocessor configurations,
where assumptions could be made on the effect of the respective
priorities of both the started and starter thread. I.e. the starter
may assume the started thread has entered its processing routine
before threadobj_start() returns, for any relevant purpose. However,
we enable it for SMP configurations as well, to keep these assumptions
valid regardless.

---

 include/copperplate/threadobj.h |    9 +++-
 lib/alchemy/task.c              |    5 +-
 lib/copperplate/threadobj.c     |  100 +++++++++++++++++++++++++++------------
 lib/psos/task.c                 |    3 +-
 lib/vxworks/taskLib.c           |    4 +-
 5 files changed, 83 insertions(+), 38 deletions(-)

diff --git a/include/copperplate/threadobj.h b/include/copperplate/threadobj.h
index d086dcb..56f8199 100644
--- a/include/copperplate/threadobj.h
+++ b/include/copperplate/threadobj.h
@@ -85,8 +85,9 @@ struct threadobj_stat {
 #define THREADOBJ_ROUNDROBIN   0x2     /* Undergoes round-robin. */
 #define THREADOBJ_STARTED      0x4     /* threadobj_start() called. */
 #define THREADOBJ_WARMUP       0x8     /* threadobj_prologue() not called yet. 
*/
-#define THREADOBJ_ABORTED      0x10    /* cancelled before start. */
+#define THREADOBJ_ABORTED      0x10    /* Cancelled before start. */
 #define THREADOBJ_LOCKED       0x20    /* threadobj_lock() granted (debug 
only). */
+#define THREADOBJ_RUNNING      0x40    /* Running user code. */
 #define THREADOBJ_DEBUG                0x8000  /* Debug mode enabled. */
 
 #define THREADOBJ_IRQCONTEXT    ((struct threadobj *)-2UL)
@@ -242,10 +243,14 @@ void threadobj_init(struct threadobj *thobj,
 
 void threadobj_start(struct threadobj *thobj);
 
+void threadobj_shadow(struct threadobj *thobj);
+
 int threadobj_prologue(struct threadobj *thobj,
                       const char *name);
 
-void threadobj_wait_start(struct threadobj *thobj);
+void threadobj_wait_start(void);
+
+void threadobj_notify_entry(void);
 
 int threadobj_cancel(struct threadobj *thobj);
 
diff --git a/lib/alchemy/task.c b/lib/alchemy/task.c
index d455ab0..f0b343a 100644
--- a/lib/alchemy/task.c
+++ b/lib/alchemy/task.c
@@ -173,7 +173,7 @@ static int task_prologue(struct alchemy_task *tcb)
 
        COPPERPLATE_PROTECT(svc);
 
-       threadobj_wait_start(&tcb->thobj);
+       threadobj_wait_start();
 
        threadobj_lock(&tcb->thobj);
 
@@ -198,6 +198,7 @@ static void *task_trampoline(void *arg)
                goto out;
        }
 
+       threadobj_notify_entry();
        tcb->entry(tcb->arg);
 out:
        threadobj_lock(&tcb->thobj);
@@ -409,7 +410,7 @@ int rt_task_shadow(RT_TASK *task, const char *name, int 
prio, int mode)
                goto out;
 
        threadobj_lock(&tcb->thobj);
-       threadobj_start(&tcb->thobj); /* We won't wait in prologue. */
+       threadobj_shadow(&tcb->thobj); /* We won't wait in prologue. */
        threadobj_unlock(&tcb->thobj);
        ret = task_prologue(tcb);
        if (ret) {
diff --git a/lib/copperplate/threadobj.c b/lib/copperplate/threadobj.c
index 5743798..70f4435 100644
--- a/lib/copperplate/threadobj.c
+++ b/lib/copperplate/threadobj.c
@@ -923,42 +923,31 @@ void threadobj_init(struct threadobj *thobj,
        threadobj_init_corespec(thobj);
 }
 
-void threadobj_start(struct threadobj *thobj)  /* thobj->lock held. */
-{
-       __threadobj_check_locked(thobj);
-
-       if (thobj->status & THREADOBJ_STARTED)
-               return;
-
-       thobj->status |= THREADOBJ_STARTED;
-       __RT(pthread_cond_signal(&thobj->barrier));
-}
+/*
+ * NOTE: to spare us the need for passing the equivalent of a
+ * syncstate argument to each thread locking operation, we hold the
+ * cancel state of the locker directly into the locked thread, prior
+ * to disabling cancellation for the calling thread.
+ *
+ * However, this means that we must save some state information on the
+ * stack prior to calling any service which releases that lock
+ * implicitly, such as pthread_cond_wait(). Failing to do so would
+ * introduce the possibility for the saved state to be overwritten by
+ * another thread which managed to grab the lock after
+ * pthread_cond_wait() dropped it.
+ *
+ * XXX: cancel_state is held in the descriptor of the target thread,
+ * not the current one, because we allow non-copperplate threads to
+ * call these services, and these have no threadobj descriptor.
+ */
 
-void threadobj_wait_start(struct threadobj *thobj) /* thobj->lock free. */
+static int start_sync(struct threadobj *thobj, int mask)
 {
        int oldstate, status;
 
-       threadobj_lock(thobj);
-
-       /*
-        * NOTE: to spare us the need for passing the equivalent of a
-        * syncstate argument to each thread locking operation, we
-        * hold the cancel state of the locker directly into the
-        * locked thread, prior to disabling cancellation for the
-        * calling thread.
-        *
-        * However, this means that we must save some state
-        * information on the stack prior to calling any service which
-        * releases that lock implicitly, such as
-        * pthread_cond_wait(). Failing to do so would introduce the
-        * possibility for the saved state to be overwritten by
-        * another thread which managed to grab the lock after
-        * pthread_cond_wait() dropped it.
-        */
-
        for (;;) {
                status = thobj->status;
-               if (status & (THREADOBJ_STARTED|THREADOBJ_ABORTED))
+               if (status & mask)
                        break;
                oldstate = thobj->cancel_state;
                __threadobj_tag_unlocked(thobj);
@@ -967,7 +956,46 @@ void threadobj_wait_start(struct threadobj *thobj) /* 
thobj->lock free. */
                thobj->cancel_state = oldstate;
        }
 
-       threadobj_unlock(thobj);
+       return status;
+}
+
+void threadobj_start(struct threadobj *thobj)  /* thobj->lock held. */
+{
+       struct threadobj *current = threadobj_current();
+
+       __threadobj_check_locked(thobj);
+
+       if (thobj->status & THREADOBJ_STARTED)
+               return;
+
+       thobj->status |= THREADOBJ_STARTED;
+       __RT(pthread_cond_signal(&thobj->barrier));
+
+       if (current && thobj->priority <= current->priority)
+               return;
+       /*
+        * Caller needs synchronization with the thread being started,
+        * which has higher priority. We shall wait until that thread
+        * enters the user code, or aborts prior to reaching that
+        * point, whichever comes first.
+        */
+       start_sync(thobj, THREADOBJ_RUNNING);
+}
+
+void threadobj_shadow(struct threadobj *thobj)
+{
+       __threadobj_check_locked(thobj);
+       thobj->status |= THREADOBJ_STARTED|THREADOBJ_RUNNING;
+}
+
+void threadobj_wait_start(void) /* current->lock free. */
+{
+       struct threadobj *current = threadobj_current();
+       int status;
+
+       threadobj_lock(current);
+       status = start_sync(current, THREADOBJ_STARTED|THREADOBJ_ABORTED);
+       threadobj_unlock(current);
 
        /*
         * We may have preempted the guy who set THREADOBJ_ABORTED in
@@ -979,6 +1007,16 @@ void threadobj_wait_start(struct threadobj *thobj) /* 
thobj->lock free. */
                pause();
 }
 
+void threadobj_notify_entry(void) /* current->lock free. */
+{
+       struct threadobj *current = threadobj_current();
+
+       threadobj_lock(current);
+       current->status |= THREADOBJ_RUNNING;
+       __RT(pthread_cond_signal(&current->barrier));
+       threadobj_unlock(current);
+}
+
 /* thobj->lock free, cancellation disabled. */
 int threadobj_prologue(struct threadobj *thobj, const char *name)
 {
diff --git a/lib/psos/task.c b/lib/psos/task.c
index ecce2d7..0ebe4b1 100644
--- a/lib/psos/task.c
+++ b/lib/psos/task.c
@@ -185,7 +185,7 @@ static void *task_trampoline(void *arg)
 
        COPPERPLATE_PROTECT(svc);
 
-       threadobj_wait_start(&task->thobj);
+       threadobj_wait_start();
 
        threadobj_lock(&task->thobj);
 
@@ -199,6 +199,7 @@ static void *task_trampoline(void *arg)
 
        COPPERPLATE_UNPROTECT(svc);
 
+       threadobj_notify_entry();
        args->entry(args->arg0, args->arg1, args->arg2, args->arg3);
 done:
        threadobj_lock(&task->thobj);
diff --git a/lib/vxworks/taskLib.c b/lib/vxworks/taskLib.c
index 5f42430..996ac71 100644
--- a/lib/vxworks/taskLib.c
+++ b/lib/vxworks/taskLib.c
@@ -242,8 +242,8 @@ static void *task_trampoline(void *arg)
        COPPERPLATE_UNPROTECT(svc);
 
        /* Wait for someone to run taskActivate() upon us. */
-       threadobj_wait_start(&task->thobj);
-
+       threadobj_wait_start();
+       threadobj_notify_entry();
        args->entry(args->arg0, args->arg1, args->arg2, args->arg3,
                    args->arg4, args->arg5, args->arg6, args->arg7,
                    args->arg8, args->arg9);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to