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

Author: Philippe Gerum <r...@xenomai.org>
Date:   Fri Sep 27 16:49:14 2013 +0200

cobalt/thread: serialize pthread_join() with Cobalt thread exit handler

This change addresses the following situation:

1. parent(high-prio) waits in pthread_join(child)
2. child(low-prio) enters exit path, unblocks parent
3. parent resumes, invokes Cobalt service for child

Normally, the parent would expect the child's thread_id to be invalid
upon return from pthread_join(). To guarantee this, we have to
serialize pthread_join() with the thread exit handler within the
Cobalt core, which actually dismisses the local thread identifier.

---

 include/cobalt/pthread.h            |    2 +
 include/cobalt/uapi/kernel/thread.h |    1 +
 include/cobalt/uapi/syscall.h       |    2 +-
 kernel/cobalt/posix/syscall.c       |    1 +
 kernel/cobalt/posix/thread.c        |   49 ++++++++++++++++++++++++++++++++++-
 kernel/cobalt/posix/thread.h        |   10 +++++-
 lib/cobalt/cobalt.wrappers          |    1 +
 lib/cobalt/internal.c               |   36 +++++++++++++++++++++++++
 lib/cobalt/internal.h               |    2 +
 lib/cobalt/thread.c                 |   13 +++++++++
 lib/cobalt/wrappers.c               |    6 ++++
 11 files changed, 119 insertions(+), 4 deletions(-)

diff --git a/include/cobalt/pthread.h b/include/cobalt/pthread.h
index 881f5f7..84158f5 100644
--- a/include/cobalt/pthread.h
+++ b/include/cobalt/pthread.h
@@ -124,6 +124,8 @@ COBALT_DECL(int, pthread_cond_broadcast(pthread_cond_t 
*cond));
 
 COBALT_DECL(int, pthread_kill(pthread_t tid, int sig));
 
+COBALT_DECL(int, pthread_join(pthread_t tid, void **retval));
+
 COBALT_DECL(int, pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
                                               int *proto));
 
diff --git a/include/cobalt/uapi/kernel/thread.h 
b/include/cobalt/uapi/kernel/thread.h
index 860ef92..b359f7a 100644
--- a/include/cobalt/uapi/kernel/thread.h
+++ b/include/cobalt/uapi/kernel/thread.h
@@ -49,6 +49,7 @@
 #define XNROOT    0x00010000 /**< Root thread (that is, Linux/IDLE) */
 #define XNWEAK    0x00020000 /**< Non real-time shadow (from the WEAK class) */
 #define XNUSER    0x00040000 /**< Shadow thread running in userland */
+#define XNJOINED  0x00080000 /**< Another thread waits for joining this thread 
*/
 
 /** @} */
 
diff --git a/include/cobalt/uapi/syscall.h b/include/cobalt/uapi/syscall.h
index 3d92f29..ed60c35 100644
--- a/include/cobalt/uapi/syscall.h
+++ b/include/cobalt/uapi/syscall.h
@@ -93,7 +93,7 @@
 #define sc_cobalt_condattr_setclock     72
 #define sc_cobalt_condattr_getpshared   73
 #define sc_cobalt_condattr_setpshared   74
-/* 75 unimplemented */
+#define sc_cobalt_thread_join           75
 #define sc_cobalt_thread_kill           76
 #define sc_cobalt_select                77
 #define sc_cobalt_thread_setschedparam_ex      78
diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c
index 81ea80a..e3a135d 100644
--- a/kernel/cobalt/posix/syscall.c
+++ b/kernel/cobalt/posix/syscall.c
@@ -93,6 +93,7 @@ static struct xnsyscall cobalt_syscalls[] = {
        SKINCALL_DEF(sc_cobalt_thread_probe, cobalt_thread_probe_np, any),
        SKINCALL_DEF(sc_cobalt_thread_kill, cobalt_thread_kill, conforming),
        SKINCALL_DEF(sc_cobalt_thread_getstat, cobalt_thread_stat, any),
+       SKINCALL_DEF(sc_cobalt_thread_join, cobalt_thread_join, primary),
        SKINCALL_DEF(sc_cobalt_sem_init, cobalt_sem_init, any),
        SKINCALL_DEF(sc_cobalt_sem_destroy, cobalt_sem_destroy, any),
        SKINCALL_DEF(sc_cobalt_sem_post, cobalt_sem_post, any),
diff --git a/kernel/cobalt/posix/thread.c b/kernel/cobalt/posix/thread.c
index c2c4a24..6f124a2 100644
--- a/kernel/cobalt/posix/thread.c
+++ b/kernel/cobalt/posix/thread.c
@@ -210,6 +210,17 @@ struct cobalt_thread *cobalt_thread_find_local(pid_t pid) 
/* nklocked, IRQs off
 }
 EXPORT_SYMBOL_GPL(cobalt_thread_find_local);
 
+struct cobalt_thread *cobalt_thread_lookup(unsigned long pth) /* nklocked, 
IRQs off */
+{
+       struct cobalt_thread *thread;
+       struct cobalt_local_hkey hkey;
+
+       hkey.u_pth = pth;
+       hkey.mm = current->mm;
+       return thread_lookup(&hkey);
+}
+EXPORT_SYMBOL_GPL(cobalt_thread_lookup);
+
 struct xnpersonality *cobalt_thread_map(struct xnthread *curr)
 {
        struct cobalt_thread *thread;
@@ -240,6 +251,9 @@ struct xnpersonality *cobalt_thread_exit(struct xnthread 
*curr)
        cobalt_signal_flush(thread);
        xnsynch_destroy(&thread->monitor_synch);
        xnsynch_destroy(&thread->sigwait);
+       /* Waiters will receive EIDRM */
+       xnsynch_destroy(&thread->join_synch);
+       xnsched_run();
 
        return NULL;
 }
@@ -512,8 +526,9 @@ static inline int pthread_create(struct cobalt_thread 
**thread_p, const pthread_
 
        thread->hkey.u_pth = 0;
        thread->hkey.mm = NULL;
+       xnsynch_init(&thread->join_synch, XNSYNCH_FIFO, NULL);
 
-       *thread_p = thread; /* Must be done before the thread is started. */
+       *thread_p = thread;
 
        return 0;
 }
@@ -1099,6 +1114,38 @@ int cobalt_thread_kill(unsigned long pth, int sig)
        return ret;
 }
 
+int cobalt_thread_join(unsigned long pth)
+{
+       struct cobalt_local_hkey hkey;
+       struct cobalt_thread *thread;
+       int ret;
+       spl_t s;
+
+       xnlock_get_irqsave(&nklock, s);
+
+       hkey.u_pth = pth;
+       hkey.mm = current->mm;
+       thread = thread_lookup(&hkey);
+       if (thread == NULL)
+               ret = -ESRCH;
+       else if (thread == cobalt_current_thread())
+               ret = -EDEADLK;
+       else if (xnsynch_pended_p(&thread->join_synch))
+               ret = -EBUSY;
+       else {
+               xnthread_set_state(&thread->threadbase, XNJOINED);
+               ret = xnsynch_sleep_on(&thread->join_synch,
+                                      XN_INFINITE, XN_RELATIVE);
+               ret = ret & XNBREAK ? -EINTR : 0;
+               if (ret != -EIDRM && thread_lookup(&hkey) == thread)
+                       xnthread_clear_state(&thread->threadbase, XNJOINED);
+       }
+
+       xnlock_put_irqrestore(&nklock, s);
+
+       return ret;
+}
+
 int cobalt_thread_stat(pid_t pid,
                       struct cobalt_threadstat __user *u_stat)
 {
diff --git a/kernel/cobalt/posix/thread.h b/kernel/cobalt/posix/thread.h
index 512aa60..7a30647 100644
--- a/kernel/cobalt/posix/thread.h
+++ b/kernel/cobalt/posix/thread.h
@@ -116,15 +116,17 @@ struct cobalt_thread {
        struct xnsynch sigwait;
        struct list_head signext;
 
-       /* Cached value for current policy (user side). */
+       /** Cached value for current policy (user side). */
        int sched_u_policy;
 
-       /* Monitor wait object and link holder. */
+       /** Monitor wait object and link holder. */
        struct xnsynch monitor_synch;
        struct list_head monitor_link;
        int monitor_queued;
 
        struct cobalt_local_hkey hkey;
+       /** Exit event for joining the thread. */
+       struct xnsynch join_synch;
 };
 
 struct cobalt_sigwait_context {
@@ -143,6 +145,8 @@ struct cobalt_thread *cobalt_thread_find(pid_t pid);
 
 struct cobalt_thread *cobalt_thread_find_local(pid_t pid);
 
+struct cobalt_thread *cobalt_thread_lookup(unsigned long pth);
+
 int cobalt_thread_create(unsigned long tid, int policy,
                         struct sched_param_ex __user *u_param,
                         unsigned long __user *u_window_offset);
@@ -167,6 +171,8 @@ int cobalt_thread_probe_np(pid_t h_tid);
 
 int cobalt_thread_kill(unsigned long tid, int sig);
 
+int cobalt_thread_join(unsigned long tid);
+
 int cobalt_thread_stat(pid_t pid,
                       struct cobalt_threadstat __user *u_stat);
 
diff --git a/lib/cobalt/cobalt.wrappers b/lib/cobalt/cobalt.wrappers
index 5a16583..519973d 100644
--- a/lib/cobalt/cobalt.wrappers
+++ b/lib/cobalt/cobalt.wrappers
@@ -6,6 +6,7 @@
 --wrap sched_get_priority_min
 --wrap sched_get_priority_max
 --wrap pthread_kill
+--wrap pthread_join
 --wrap sem_init
 --wrap sem_destroy
 --wrap sem_post
diff --git a/lib/cobalt/internal.c b/lib/cobalt/internal.c
index 64f7acc..f44ab71 100644
--- a/lib/cobalt/internal.c
+++ b/lib/cobalt/internal.c
@@ -49,6 +49,42 @@ int __cobalt_thread_stat(pid_t pid, struct cobalt_threadstat 
*stat)
                                 sc_cobalt_thread_getstat, pid, stat);
 }
 
+int __cobalt_thread_join(pthread_t thread)
+{
+       int ret;
+
+       /*
+        * Serialize with the regular task exit path, so that no call
+        * for the joined pthread may succeed after this routine
+        * returns. A successful call to sc_cobalt_thread_join
+        * receives -EIDRM, meaning that we eventually joined the
+        * exiting thread as seen by the Cobalt core.
+        *
+        * -ESRCH means that the joined thread has already exited
+        * linux-wise, while we were about to wait for it from the
+        * Cobalt side, in which case we are fine.
+        *
+        * -EBUSY denotes a multiple join for several threads in
+        * parallel to the same target.
+        *
+        * -EPERM may be received because the current context is not a
+        * Xenomai thread.
+        *
+        * Zero is unexpected.
+        *
+        * CAUTION: this service joins a thread Cobat-wise only, not
+        * glibc-wise.  For a complete join comprising the libc
+        * cleanups, __STD(pthread_join()) should be paired with this
+        * call.
+        */
+       do
+               ret = XENOMAI_SKINCALL1(__cobalt_muxid,
+                                       sc_cobalt_thread_join, thread);
+       while (ret == -EINTR);
+
+       return ret;
+}
+
 void ___cobalt_prefault(void *p, size_t len)
 {
        volatile char *_p = (volatile char *)p, *end;
diff --git a/lib/cobalt/internal.h b/lib/cobalt/internal.h
index 1816c20..aa44897 100644
--- a/lib/cobalt/internal.h
+++ b/lib/cobalt/internal.h
@@ -66,6 +66,8 @@ void __cobalt_thread_harden(void);
 int __cobalt_thread_stat(pid_t pid,
                         struct cobalt_threadstat *stat);
 
+int __cobalt_thread_join(pthread_t thread);
+
 int __cobalt_serial_debug(const char *fmt, ...);
 
 int cobalt_monitor_init(cobalt_monitor_t *mon,
diff --git a/lib/cobalt/thread.c b/lib/cobalt/thread.c
index c6db4b0..0063799 100644
--- a/lib/cobalt/thread.c
+++ b/lib/cobalt/thread.c
@@ -447,6 +447,19 @@ COBALT_IMPL(int, pthread_kill, (pthread_t thread, int sig))
        return ret;
 }
 
+COBALT_IMPL(int, pthread_join, (pthread_t thread, void **retval))
+{
+       int ret;
+
+       ret = __STD(pthread_join(thread, retval));
+       if (ret)
+               return ret;
+
+       ret = __cobalt_thread_join(thread);
+
+       return ret == -EBUSY ? EINVAL : 0;
+}
+
 static __attribute__((constructor)) void cobalt_thread_init(void)
 {
 #ifdef _CS_GNU_LIBPTHREAD_VERSION
diff --git a/lib/cobalt/wrappers.c b/lib/cobalt/wrappers.c
index 6247915..a4f643e 100644
--- a/lib/cobalt/wrappers.c
+++ b/lib/cobalt/wrappers.c
@@ -90,6 +90,12 @@ int __real_pthread_kill(pthread_t tid, int sig)
        return pthread_kill(tid, sig);
 }
 
+__attribute__ ((weak))
+int __real_pthread_join(pthread_t tid, void **retval)
+{
+       return pthread_join(tid, retval);
+}
+
 /* semaphores */
 __attribute__ ((weak))
 int __real_sem_init(sem_t * sem, int pshared, unsigned value)


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

Reply via email to