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

Author: Philippe Gerum <r...@xenomai.org>
Date:   Tue Apr 22 18:57:18 2014 +0200

copperplate/threadobj: protect threadobj_sleep() against spurious wakeups

threadobj_sleep() should return upon wait completion or
threadobj_unblock() request, whichever comes first. However, receiving
a regular linux signal should not abort this call.

---

 include/copperplate/clockobj.h       |    2 +
 include/copperplate/threadobj.h      |    4 +
 include/mercury/boilerplate/signal.h |    1 +
 lib/copperplate/syncobj.c            |    6 +-
 lib/copperplate/threadobj.c          |  173 ++++++++++++++++++++++++++--------
 5 files changed, 143 insertions(+), 43 deletions(-)

diff --git a/include/copperplate/clockobj.h b/include/copperplate/clockobj.h
index 58dbbf9..bbc2cf2 100644
--- a/include/copperplate/clockobj.h
+++ b/include/copperplate/clockobj.h
@@ -51,6 +51,8 @@ struct clockobj {
        const char *name;       /* __ref FIXME */
 };
 
+#define zero_time      ((struct timespec){ .tv_sec = 0, .tv_nsec = 0 })
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/include/copperplate/threadobj.h b/include/copperplate/threadobj.h
index 2c6794f..5438790 100644
--- a/include/copperplate/threadobj.h
+++ b/include/copperplate/threadobj.h
@@ -22,6 +22,7 @@
 #include <time.h>
 #include <sched.h>
 #include <semaphore.h>
+#include <signal.h>
 #include <pthread.h>
 #include <boilerplate/list.h>
 #include <boilerplate/lock.h>
@@ -88,6 +89,9 @@ struct threadobj_corespec {
        ticks_t period;
        /** Timeout reported by sysregd. */
        struct timespec timeout;
+       /** Timer data for threadobj_sleep() */
+       struct sigevent sleep_sev;
+       timer_t sleep_timer;
 };
 
 struct threadobj_stat {
diff --git a/include/mercury/boilerplate/signal.h 
b/include/mercury/boilerplate/signal.h
index 14523cd..996711f 100644
--- a/include/mercury/boilerplate/signal.h
+++ b/include/mercury/boilerplate/signal.h
@@ -26,6 +26,7 @@
 
 #define SIGNOTIFY      (SIGRTMIN + 8) /* Internal notification */
 #define SIGRELS                (SIGRTMIN + 9) /* Syscall abort */
+#define SIGWAKEUP      (SIGRTMIN + 10) /* Sleep abort */
 
 #define SIGSAFE_LOCK_ENTRY(__safelock)                                 \
        do {                                                            \
diff --git a/lib/copperplate/syncobj.c b/lib/copperplate/syncobj.c
index 7834f86..5aee1a2 100644
--- a/lib/copperplate/syncobj.c
+++ b/lib/copperplate/syncobj.c
@@ -50,9 +50,9 @@
  * emulated via a mutex and two condition variables over Mercury (one
  * of which being hosted by the thread object implementation).
  *
- * NOTE: we do no do error backtracing in this file, since error
- * returns when locking, pending or deleting sync objects express
- * normal runtime conditions.
+ * NOTE: we don't do error backtracing in this file, since error
+ * returns when locking, pending or deleting sync objects usually
+ * express normal runtime conditions.
  */
 
 #ifdef CONFIG_XENO_COBALT
diff --git a/lib/copperplate/threadobj.c b/lib/copperplate/threadobj.c
index a2a9dcd..9901a54 100644
--- a/lib/copperplate/threadobj.c
+++ b/lib/copperplate/threadobj.c
@@ -178,6 +178,43 @@ int threadobj_resume(struct threadobj *thobj) /* 
thobj->lock held */
        return __bt(-ret);
 }
 
+int threadobj_sleep(struct timespec *ts)
+{
+       struct threadobj *current = threadobj_current();
+       sigset_t set;
+       int sig, ret;
+
+       /*
+        * threadobj_sleep() shall return -EINTR immediately upon
+        * threadobj_unblock(), to honor forced wakeup semantics for
+        * RTOS personalities.
+        *
+        * Otherwise, the sleep should be silently restarted until
+        * completion after a Linux signal is handled.
+        */
+       current->run_state = __THREAD_S_DELAYED;
+       threadobj_save_timeout(&current->core, ts);
+
+       do {
+               /*
+                * Waiting on a null signal set causes an infinite
+                * delay, so that only threadobj_unblock() or a linux
+                * signal can unblock us.
+                */
+               if (ts->tv_sec == 0 && ts->tv_nsec == 0) {
+                       sigemptyset(&set);
+                       ret = -__RT(sigwait(&set, &sig));
+               } else
+                       ret = -__RT(clock_nanosleep(CLOCK_COPPERPLATE,
+                                                   TIMER_ABSTIME, ts, NULL));
+       } while (ret == -EINTR &&
+                (current->core.u_window->info & XNBREAK) == 0);
+
+       current->run_state = __THREAD_S_RUNNING;
+
+       return ret;
+}
+
 int __threadobj_lock_sched(struct threadobj *current)
 {
        smp_rmb();
@@ -405,6 +442,7 @@ static inline void pkg_init_corespec(void)
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = unblock_sighandler;
        sigaction(SIGRELS, &sa, NULL);
+       sigaction(SIGWAKEUP, &sa, NULL);
        sa.sa_handler = roundrobin_handler;
        sigaction(SIGVTALRM, &sa, NULL);
 
@@ -467,22 +505,25 @@ static inline int threadobj_setup_corespec(struct 
threadobj *thobj)
        thobj->core.period = 0;
 
        /*
-        * Create the per-thread round-robin timer.
-        *
-        * XXX: It is a bit overkill doing this here instead of on
-        * demand, but we must get the internal ID from the running
-        * thread, and unlike with set_rr(), threadobj_current() ==
-        * thobj is guaranteed in threadobj_setup_corespec().
+        * Setup the per-thread data used in threadobj_sleep().
         */
        memset(&sev, 0, sizeof(sev));
        sev.sigev_notify = SIGEV_SIGNAL|SIGEV_THREAD_ID;
-       sev.sigev_signo = SIGVTALRM;
-       sev.sigev_notify_thread_id = copperplate_get_tid();
+       sev.sigev_signo = SIGWAKEUP;
+       sev.sigev_notify_thread_id = threadobj_get_pid(thobj);
+       thobj->core.sleep_sev = sev;
+       thobj->core.sleep_timer = NULL;
 
+       /*
+        * Create the per-thread round-robin timer.
+        */
+       sev.sigev_signo = SIGVTALRM;
        ret = timer_create(CLOCK_THREAD_CPUTIME_ID, &sev,
                           &thobj->core.rr_timer);
-       if (ret)
-               return __bt(-errno);
+       if (ret) {
+               ret = __bt(-errno);
+               notifier_destroy(&thobj->core.notifier);
+       }
 
        return 0;
 }
@@ -490,8 +531,12 @@ static inline int threadobj_setup_corespec(struct 
threadobj *thobj)
 static inline void threadobj_cleanup_corespec(struct threadobj *thobj)
 {
        notifier_destroy(&thobj->core.notifier);
+
        if (thobj->core.rr_timer)
                timer_delete(thobj->core.rr_timer);
+
+       if (thobj->core.sleep_timer)
+               timer_delete(thobj->core.sleep_timer);
 }
 
 static inline void threadobj_run_corespec(struct threadobj *thobj)
@@ -524,6 +569,69 @@ int threadobj_resume(struct threadobj *thobj) /* 
thobj->lock held */
        return __bt(notifier_release(&thobj->core.notifier));
 }
 
+int threadobj_sleep(struct timespec *ts)
+{
+       struct threadobj *current = threadobj_current();
+       struct itimerspec it;
+       sigset_t set, oset;
+       timer_t timer;
+       int ret, sig;
+
+       /*
+        * threadobj_sleep() shall return -EINTR immediately upon
+        * threadobj_unblock(), to honor forced wakeup semantics for
+        * RTOS personalities.
+        *
+        * Otherwise, the sleep should be silently restarted until
+        * completion after a signal is handled, except SIGRELS in the
+        * Mercury case.
+        */
+       it.it_value = *ts;
+       it.it_interval = zero_time;
+
+       sigemptyset(&set);
+       sigaddset(&set, SIGRELS);
+
+       timer = current->core.sleep_timer;
+       if (ts->tv_sec == 0 && ts->tv_nsec == 0) {
+               /* Infinite wait. */
+               pthread_sigmask(SIG_BLOCK, &set, &oset);
+               goto sleep;
+       }
+
+       if (timer == NULL) {
+               ret = timer_create(CLOCK_COPPERPLATE,
+                                  &current->core.sleep_sev,
+                                  &current->core.sleep_timer);
+               if (ret)
+                       return __bt(-errno);
+               timer = current->core.sleep_timer;
+       }
+
+       sigaddset(&set, SIGWAKEUP);
+
+       pthread_sigmask(SIG_BLOCK, &set, &oset);
+
+       if (timer_settime(timer, TIMER_ABSTIME, &it, NULL)) {
+               ret = __bt(-errno);
+               goto out;
+       }
+sleep:
+       current->run_state = __THREAD_S_DELAYED;
+       threadobj_save_timeout(&current->core, ts);
+       ret = -sigwait(&set, &sig);
+       current->run_state = __THREAD_S_RUNNING;
+       if (sig == SIGRELS) {
+               it.it_value = zero_time;
+               timer_settime(timer, 0, &it, NULL);
+               ret = -EINTR;
+       }
+out:
+       pthread_sigmask(SIG_SETMASK, &oset, NULL);
+
+       return ret;
+}
+
 int __threadobj_lock_sched(struct threadobj *current) /* current->lock held */
 {
        pthread_t tid = current->tid;
@@ -1082,25 +1190,6 @@ int threadobj_prologue(struct threadobj *thobj, const 
char *name)
        return 0;
 }
 
-int threadobj_sleep(struct timespec *ts)
-{
-       struct threadobj *current = threadobj_current();
-       int ret;
-
-       /*
-        * clock_nanosleep() returns -EINTR upon threadobj_unblock()
-        * with both Cobalt and Mercury cores.
-        */
-       current->run_state = __THREAD_S_DELAYED;
-       threadobj_save_timeout(&current->core, ts);
-       do
-               ret = -__RT(clock_nanosleep(CLOCK_COPPERPLATE, TIMER_ABSTIME, 
ts, NULL));
-       while (ret == -EINTR);
-       current->run_state = __THREAD_S_RUNNING;
-
-       return ret;
-}
-
 static void cancel_sync(struct threadobj *thobj) /* thobj->lock held */
 {
        pthread_t tid = thobj->tid;
@@ -1246,21 +1335,25 @@ static void finalize_thread(void *p) /* thobj->lock 
free */
 
 int threadobj_unblock(struct threadobj *thobj) /* thobj->lock held */
 {
-       pthread_t tid = thobj->tid;
-       int ret = 0;
+       struct syncstate syns;
+       struct syncobj *sobj;
+       int ret;
 
        __threadobj_check_locked(thobj);
 
-       /*
-        * FIXME: racy. We can't assume thobj->wait_sobj is stable.
-        */
-       if (thobj->wait_sobj)   /* Remove PEND (+DELAY timeout) */
-               syncobj_flush(thobj->wait_sobj);
-       else
-               /* Remove standalone DELAY */
-               ret = -__RT(pthread_kill(tid, SIGRELS));
+       sobj = thobj->wait_sobj;
+       if (sobj) {
+               ret = syncobj_lock(sobj, &syns);
+               if (ret == 0) {
+                       /* Remove PEND (+DELAY timeout) */
+                       syncobj_flush(thobj->wait_sobj);
+                       syncobj_unlock(thobj->wait_sobj, &syns);
+                       return 0;
+               }
+       }
 
-       return __bt(ret);
+       /* Remove standalone DELAY condition. */
+       return __bt(-__RT(pthread_kill(thobj->tid, SIGRELS)));
 }
 
 void threadobj_spin(ticks_t ns)


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to