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

Author: Philippe Gerum <r...@xenomai.org>
Date:   Wed May  7 18:12:07 2014 +0200

copperplate/notifier: enable notifying remote threads

In shared multi-processing mode, we may have to suspend/resume threads
which belong to sibling processes from the same Copperplate session.

To this end, we drop the local pipe previously used for waiting for
the resume event. The suspended thread now waits for SIGRESM directly
via sigsuspend(), which is async-signal-safe.

As a side-effect, a suspended thread won't be allowed to handle any
blockable signal but SIGRESM until it is resumed, which follows the
principle of least astonishment.

---

 include/copperplate/notifier.h       |    7 +-
 include/mercury/boilerplate/signal.h |    9 ++-
 lib/copperplate/notifier.c           |  133 ++++++++--------------------------
 lib/copperplate/threadobj.c          |   28 ++++---
 4 files changed, 61 insertions(+), 116 deletions(-)

diff --git a/include/copperplate/notifier.h b/include/copperplate/notifier.h
index 61e2ec4..011accf 100644
--- a/include/copperplate/notifier.h
+++ b/include/copperplate/notifier.h
@@ -23,7 +23,6 @@
 
 struct notifier {
        pid_t owner;
-       int waitfd[2];
        struct pvholder link;
 };
 
@@ -33,11 +32,15 @@ extern "C" {
 
 int notifier_init(struct notifier *nf, pid_t pid);
 
+static inline void notifier_destroy(struct notifier *nf)
+{
+}
+
 void notifier_destroy(struct notifier *nf);
 
 void notifier_signal(struct notifier *nf);
 
-void notifier_wait(const struct notifier *nf);
+void notifier_wait(void);
 
 void notifier_disable(struct notifier *nf);
 
diff --git a/include/mercury/boilerplate/signal.h 
b/include/mercury/boilerplate/signal.h
index df131fa..914c02c 100644
--- a/include/mercury/boilerplate/signal.h
+++ b/include/mercury/boilerplate/signal.h
@@ -24,15 +24,16 @@
 #define sigev_notify_thread_id  _sigev_un._tid
 #endif
 
-#define SIGNOTIFY      (SIGRTMIN + 8) /* Internal notification */
-#define SIGRELS                (SIGRTMIN + 9) /* Syscall abort */
-#define SIGRRB         (SIGRTMIN + 10) /* Round-robin event */
+#define SIGSUSP                (SIGRTMIN + 8)
+#define SIGRESM                (SIGRTMIN + 9)
+#define SIGRELS                (SIGRTMIN + 10) /* Syscall abort */
+#define SIGRRB         (SIGRTMIN + 11) /* Round-robin event */
 
 #define SIGSAFE_LOCK_ENTRY(__safelock)                                 \
        do {                                                            \
                sigset_t __safeset, __oldsafeset;                       \
                sigemptyset(&__safeset);                                \
-               sigaddset(&__safeset, SIGNOTIFY);                       \
+               sigaddset(&__safeset, SIGSUSP);                         \
                pthread_sigmask(SIG_BLOCK, &__safeset, &__oldsafeset);  \
                push_cleanup_lock(__safelock);                          \
                write_lock(__safelock);
diff --git a/lib/copperplate/notifier.c b/lib/copperplate/notifier.c
index 0e8c832..0f02059 100644
--- a/lib/copperplate/notifier.c
+++ b/lib/copperplate/notifier.c
@@ -18,140 +18,69 @@
 
 #include <signal.h>
 #include <memory.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <assert.h>
 #include <errno.h>
 #include "copperplate/notifier.h"
-#include "boilerplate/lock.h"
 #include "boilerplate/signal.h"
-#include "copperplate/debug.h"
 #include "internal.h"
 
-static DEFINE_PRIVATE_LIST(notifier_list);
-
-static pthread_mutex_t notifier_lock;
-
-static struct sigaction notifier_old_sa;
-
-static void notifier_sighandler(int sig, siginfo_t *siginfo, void *uc)
+static void suspend_sighandler(int sig)
 {
-       struct notifier *nf;
-       pid_t tid;
-
-       tid = copperplate_get_tid();
-
-       if (pvlist_empty(&notifier_list))
-               goto ouch;
-
-       /* We may NOT alter the notifier list, but only scan it. */
-       pvlist_for_each_entry(nf, &notifier_list, link) {
-               if (nf->owner == tid) {
-                       notifier_wait(nf);
-                       return;
-               }
-       }
-ouch:
-       panic("received spurious notification on thread[%d] "
-             "(sig=%d, code=%d, fd=%d)",
-             tid, sig, siginfo->si_code, siginfo->si_fd);
+       notifier_wait();
 }
 
-static void lock_notifier_list(sigset_t *oset)
+static void resume_sighandler(int sig)
 {
-       sigset_t set;
-
-       sigemptyset(&set);
-       sigaddset(&set, SIGNOTIFY);
-       pthread_sigmask(SIG_BLOCK, &set, oset);
-       write_lock(&notifier_lock);
-}
-
-static void unlock_notifier_list(sigset_t *oset)
-{
-       pthread_sigmask(SIG_SETMASK, oset, NULL);
-       write_unlock(&notifier_lock);
+       /* nop */
 }
 
 int notifier_init(struct notifier *nf, pid_t pid)
 {
-       sigset_t oset;
-       int ret;
-
-       if (pipe(nf->waitfd) < 0) {
-               ret = -errno;
-               goto fail;
-       }
+       sigset_t set;
 
        nf->owner = pid;
-
-       push_cleanup_lock(&notifier_lock);
-       lock_notifier_list(&oset);
-       pvlist_append(&nf->link, &notifier_list);
-       unlock_notifier_list(&oset);
-       pop_cleanup_lock(&notifier_lock);
+       sigemptyset(&set);
+       sigaddset(&set, SIGRESM);
+       pthread_sigmask(SIG_BLOCK, &set, NULL);
 
        return 0;
-fail:
-       warning("failed to create notifier pipe");
-
-       return __bt(ret);
-}
-
-void notifier_destroy(struct notifier *nf)
-{
-       sigset_t oset;
-
-       push_cleanup_lock(&notifier_lock);
-       lock_notifier_list(&oset);
-       pvlist_remove(&nf->link);
-       unlock_notifier_list(&oset);
-       pop_cleanup_lock(&notifier_lock);
-       close(nf->waitfd[0]); /* May fail if disabled. */
-       close(nf->waitfd[1]);
 }
 
 void notifier_signal(struct notifier *nf)
 {
-       copperplate_kill_tid(nf->owner, SIGNOTIFY);
+       copperplate_kill_tid(nf->owner, SIGSUSP);
 }
 
-void notifier_disable(struct notifier *nf)
+void notifier_release(struct notifier *nf)
 {
-       close(nf->waitfd[0]);
+       copperplate_kill_tid(nf->owner, SIGRESM);
 }
 
-void notifier_release(struct notifier *nf)
+void notifier_wait(void)
 {
-       char c = 1;
-       int ret;
+       sigset_t set;
 
-       do
-               ret = write(nf->waitfd[1], &c, 1);
-       while (ret == -1 && errno == EINTR);
+       /*
+        * A suspended thread is supposed to do nothing but wait for
+        * the wake up signal, so we may happily block all signals but
+        * SIGRESM. Note that SIGRRB won't be accumulated during the
+        * sleep time anyhow, as the round-robin timer is based on
+        * CLOCK_THREAD_CPUTIME_ID, and we'll obviously don't consume
+        * any CPU time while blocked.
+        */
+       sigfillset(&set);
+       sigdelset(&set, SIGRESM);
+       sigsuspend(&set);
 }
 
-void notifier_wait(const struct notifier *nf)
+void notifier_disable(struct notifier *nf)
 {
-       int ret;
-       char c;
-
-       do
-               ret = read(nf->waitfd[0], &c, 1);
-       while (ret == -1 && errno == EINTR);
+       /* Unblock any ongoing wait. */
+       copperplate_kill_tid(nf->owner, SIGRESM);
 }
 
 void notifier_pkg_init(void)
 {
-       pthread_mutexattr_t mattr;
        struct sigaction sa;
-
-       pthread_mutexattr_init(&mattr);
-       pthread_mutexattr_settype(&mattr, mutex_type_attribute);
-       pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
-       pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);
-       pthread_mutex_init(&notifier_lock, &mattr);
-       pthread_mutexattr_destroy(&mattr);
        /*
         * We have two basic requirements for the notification
         * scheme implementing the suspend/resume mechanism:
@@ -166,7 +95,9 @@ void notifier_pkg_init(void)
         * receipt.
         */
        memset(&sa, 0, sizeof(sa));
-       sa.sa_sigaction = &notifier_sighandler;
-       sa.sa_flags = SA_SIGINFO|SA_RESTART;
-       sigaction(SIGNOTIFY, &sa, &notifier_old_sa);
+       sa.sa_flags = SA_RESTART;
+       sa.sa_handler = suspend_sighandler;
+       sigaction(SIGSUSP, &sa, NULL);
+       sa.sa_handler = resume_sighandler;
+       sigaction(SIGRESM, &sa, NULL);
 }
diff --git a/lib/copperplate/threadobj.c b/lib/copperplate/threadobj.c
index 1908d86..8508444 100644
--- a/lib/copperplate/threadobj.c
+++ b/lib/copperplate/threadobj.c
@@ -117,7 +117,11 @@ static inline void threadobj_run_corespec(struct threadobj 
*thobj)
        cobalt_thread_harden();
 }
 
-static inline void threadobj_cancel_corespec(struct threadobj *thobj) /* 
thobj->lock held */
+static inline void threadobj_cancel_1_corespec(struct threadobj *thobj) /* 
thobj->lock held */
+{
+}
+
+static inline void threadobj_cancel_2_corespec(struct threadobj *thobj) /* 
thobj->lock held */
 {
        /*
         * Send a SIGDEMT signal to demote the target thread, to make
@@ -486,15 +490,20 @@ static inline void threadobj_run_corespec(struct 
threadobj *thobj)
 {
 }
 
-static inline void threadobj_cancel_corespec(struct threadobj *thobj) /* 
thobj->lock held */
+static inline void threadobj_cancel_1_corespec(struct threadobj *thobj) /* 
thobj->lock held */
 {
-       struct notifier *nf = &thobj->core.notifier;
-
        /*
-        * Any ongoing or future notify_wait() will return immediately
-        * on error with EBADF.
+        * If the target thread we are about to cancel gets suspended
+        * while it is currently warming up, we have to unblock it
+        * from notifier_wait(), so that we don't get stuck in
+        * cancel_sync(), waiting for a warmed up state which will
+        * never come.
         */
-       notifier_disable(nf);
+       notifier_disable(&thobj->core.notifier);
+}
+
+static inline void threadobj_cancel_2_corespec(struct threadobj *thobj) /* 
thobj->lock held */
+{
 }
 
 int threadobj_suspend(struct threadobj *thobj) /* thobj->lock held */
@@ -506,7 +515,7 @@ int threadobj_suspend(struct threadobj *thobj) /* 
thobj->lock held */
        if (thobj == threadobj_current()) {
                thobj->status |= __THREAD_S_SUSPENDED;
                threadobj_unlock(thobj);
-               notifier_wait(nf);
+               notifier_wait();
                threadobj_lock(thobj);
        } else if ((thobj->status & __THREAD_S_SUSPENDED) == 0) {
                thobj->status |= __THREAD_S_SUSPENDED;
@@ -1118,6 +1127,7 @@ static void cancel_sync(struct threadobj *thobj) /* 
thobj->lock held */
                __STD(sem_init(sem, sem_scope_attribute, 0));
 
        thobj->cancel_sem = sem;
+       threadobj_cancel_1_corespec(thobj);
 
        /*
         * If the thread to delete is warming up, wait until it
@@ -1143,7 +1153,7 @@ static void cancel_sync(struct threadobj *thobj) /* 
thobj->lock held */
                __RT(pthread_cond_signal(&thobj->barrier));
        }
 
-       threadobj_cancel_corespec(thobj);
+       threadobj_cancel_2_corespec(thobj);
 
        threadobj_unlock(thobj);
 


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

Reply via email to