Module: xenomai-forge Branch: rtdm-api-waitqueues Commit: 390e073e93c2e69d4540e79d055e743f01cb7625 URL: http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=390e073e93c2e69d4540e79d055e743f01cb7625
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(¤t->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, + ¤t->core.sleep_sev, + ¤t->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(¤t->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(¤t->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