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

Author: Philippe Gerum <r...@xenomai.org>
Date:   Thu Sep 10 17:23:06 2009 +0200

nucleus: introduce wait context for threads

Wait contexts are meant to deal with two current issues:

- deleting a kernel-based thread might leave stale contextual data if
  the target thread was blocked on a synchronization object. This is
  due to the synchronous bahavior of xnpod_delete() for kernel
  threads.

- to pass arguments between the consumer and the producer of a
  resource, to give details about what is actually awaited, currently
  goes through the wait_u union of a TCB. This is barely optimal,
  since pre-canned data types to hold all possible arguments have to
  be defined in the generic TCB as part of the wait_u object, instead
  of allowing arbitrary data types to be used, depending on the code
  which blocks the thread.

  E.g. an event flag group wait mechanism should pass the event mask
  to wait for, along with the wait mode (AND/OR), so that the
  signaling side can wake it up selectively. But since it wants to do
  this only when the awaited events did arrive, it needs to peek at
  one or more active wait contexts to make a decision. Other
  synchronization objects would rather need to pass pointers, or
  anything else that fits the purpose.

Wait contexts deal with this as follows:

- a deferred cancellation mode has been introduced (XNDEFCAN) for
  threads. If present, the cancellation is marked as pending
  (XNCANPND) and the target thread immediately unblocked, when
  xnpod_delete_thread() is called.

  Further action is postponed until the thread exits its active wait
  context (xnthread_finish_wait_context) it should have declared
  before blocking (xnthread_prepare_wait_context).

  If a cancellation is pending, xnthread_finish_wait_context() will
  self-terminate the current thread. Before that, a cleanup handler
  will have been allowed to run, from which necessary housekeeping
  chores can be done.

- the actual wait context which can be defined is a free-form
  structure which should at least enclose an xnthread_wait_context
  data block for internal management by the nucleus. Any kind/number
  of data members may be additionally enclosed.

- a backpointer from any thread to its active wait context can be
  retrieved by any code enforcing proper locking (nklock basically).
  Typically, xnsynch_peek_pendq() may be used to find the thread
  heading the wait queue of a given synchronization object, which wait
  context can be then fetched via xnthread_get_wait_context().

In short:

   struct context {
          struct xnthread_wait_context wc;
          int whatever;
          void *fits;
          struct xnbufd *here;
   };

   struct xnsynch bar;

   void cleanup(struct xnthread_wait_context *wc)
   {
        struct context *p;

        p = container_of(wc, struct context, wc);
        clean_this_up(p);
   }

   ...
   struct context foo; /* likely on stack */

   foo.whatever = 12;
   foo.fits = NULL;
   foo.here = &something;

   xnthread_prepare_wait_context(&foo.wc);
   xnsynch_sleep_on(&bar, XN_INFINITE, XN_RELATIVE);
   xnthread_finish_wait_context(&foo.wc, cleanup);
   ... might not return there...

   if (xnthread_test_info(thread, XNRMID))
       ret = -EIDRM;
   else if (xnthread_test_info(thread, XNBREAK))
       ret = -EINTR;
   else if (...)

---

 include/nucleus/thread.h |   69 ++++++++++++++++++++++++++++++----------------
 ksrc/nucleus/pod.c       |   36 +++++++++++++++++++----
 ksrc/nucleus/thread.c    |   30 ++++++++++++++++++++
 3 files changed, 104 insertions(+), 31 deletions(-)

diff --git a/include/nucleus/thread.h b/include/nucleus/thread.h
index 9f7fc8b..bcd5858 100644
--- a/include/nucleus/thread.h
+++ b/include/nucleus/thread.h
@@ -50,17 +50,18 @@
 #define XNLOCK    0x00004000 /**< Holds the scheduler lock (i.e. not 
preemptible) */
 #define XNRRB     0x00008000 /**< Undergoes a round-robin scheduling */
 #define XNASDI    0x00010000 /**< ASR are disabled */
+#define XNDEFCAN  0x00020000 /**< Deferred cancelability mode (self-set only) 
*/
 
 /*
  * Some skins may depend on the following fields to live in the high
  * 16-bit word, in order to be combined with the emulated RTOS flags
  * which use the low one, so don't change them carelessly.
  */
-#define XNTRAPSW  0x00020000 /**< Trap execution mode switches */
-#define XNRPIOFF  0x00040000 /**< Stop priority coupling (shadow only) */
-#define XNFPU     0x00080000 /**< Thread uses FPU */
-#define XNSHADOW  0x00100000 /**< Shadow thread */
-#define XNROOT    0x00200000 /**< Root thread (that is, Linux/IDLE) */
+#define XNTRAPSW  0x00040000 /**< Trap execution mode switches */
+#define XNRPIOFF  0x00080000 /**< Stop priority coupling (shadow only) */
+#define XNFPU     0x00100000 /**< Thread uses FPU */
+#define XNSHADOW  0x00200000 /**< Shadow thread */
+#define XNROOT    0x00400000 /**< Root thread (that is, Linux/IDLE) */
 
 /*! @} */ /* Ends doxygen comment group: nucleus_state_flags */
 
@@ -83,7 +84,7 @@
   'o' -> Priority coupling off.
   'f' -> FPU enabled (for kernel threads).
 */
-#define XNTHREAD_STATE_LABELS  "SWDRU....X.HbTlr.tof.."
+#define XNTHREAD_STATE_LABELS  "SWDRU....X.HbTlr..tof.."
 
 #define XNTHREAD_BLOCK_BITS   
(XNSUSP|XNPEND|XNDELAY|XNDORMANT|XNRELAX|XNMIGRATE|XNHELD)
 #define XNTHREAD_MODE_BITS    (XNLOCK|XNRRB|XNASDI|XNTRAPSW|XNRPIOFF)
@@ -113,6 +114,7 @@
 #define XNAFFSET  0x00000080 /**< CPU affinity changed from primary mode */
 #define XNPRIOSET 0x00000100 /**< Priority changed from primary mode */
 #define XNABORT   0x00000200 /**< Thread is being aborted */
+#define XNCANPND  0x00000400 /**< Cancellation request is pending */
 
 /* These information flags are available to the real-time interfaces */
 #define XNTHREAD_INFO_SPARE0  0x10000000
@@ -192,6 +194,10 @@ struct xnthread_start_attr {
        void *cookie;
 };
 
+struct xnthread_wait_context {
+       unsigned long oldstate;
+};
+
 typedef void (*xnasr_t)(xnsigmask_t sigs);
 
 typedef struct xnthread {
@@ -238,7 +244,7 @@ typedef struct xnthread {
 
        xnholder_t glink;               /* Thread holder in global queue */
 
-#define link2thread(ln, fld)   container_of(ln, xnthread_t, fld)
+#define link2thread(ln, fld)   container_of(ln, struct xnthread, fld)
 
        xnpqueue_t claimq;              /* Owned resources claimed by others 
(PIP) */
 
@@ -270,6 +276,9 @@ typedef struct xnthread {
                size_t size;
        } wait_u;
 
+       /* Active wait context - Obsoletes wait_u. */
+       struct xnthread_wait_context *wcontext;
+
        struct {
                xnstat_counter_t ssw;   /* Primary -> secondary mode switch 
count */
                xnstat_counter_t csw;   /* Context switches (includes secondary 
-> primary switches) */
@@ -324,7 +333,7 @@ typedef struct xnthread {
 typedef struct xnhook {
        xnholder_t link;
 #define link2hook(ln)          container_of(ln, xnhook_t, link)
-       void (*routine)(xnthread_t *thread);
+       void (*routine)(struct xnthread *thread);
 } xnhook_t;
 
 #define xnthread_name(thread)               ((thread)->name)
@@ -372,17 +381,36 @@ typedef struct xnhook {
 #define xnthread_get_lastswitch(thread)    
xnstat_exectime_get_last_switch((thread)->sched)
 
 /* Class-level operations for threads. */
-static inline int xnthread_get_denormalized_prio(xnthread_t *t, int coreprio)
+static inline int xnthread_get_denormalized_prio(struct xnthread *t, int 
coreprio)
 {
        return t->ops && t->ops->get_denormalized_prio
                ? t->ops->get_denormalized_prio(t, coreprio) : coreprio;
 }
 
-static inline unsigned xnthread_get_magic(xnthread_t *t)
+static inline unsigned xnthread_get_magic(struct xnthread *t)
 {
        return t->ops ? t->ops->get_magic() : 0;
 }
 
+static inline
+struct xnthread_wait_context *xnthread_get_wait_context(struct xnthread 
*thread)
+{
+       return thread->wcontext;
+}
+
+static inline
+int xnthread_register(struct xnthread *thread, const char *name)
+{
+       return xnregistry_enter(name, thread, &xnthread_handle(thread), NULL);
+}
+
+static inline
+struct xnthread *xnthread_lookup(xnhandle_t threadh)
+{
+       struct xnthread *thread = xnregistry_fetch(threadh);
+       return (thread && xnthread_handle(thread) == threadh) ? thread : NULL;
+}
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -393,27 +421,20 @@ int xnthread_init(struct xnthread *thread,
                  struct xnsched_class *sched_class,
                  const union xnsched_policy_param *sched_param);
 
-void xnthread_cleanup_tcb(xnthread_t *thread);
+void xnthread_cleanup_tcb(struct xnthread *thread);
 
 char *xnthread_format_status(xnflags_t status, char *buf, int size);
 
-int *xnthread_get_errno_location(xnthread_t *thread);
+int *xnthread_get_errno_location(struct xnthread *thread);
 
-xnticks_t xnthread_get_timeout(xnthread_t *thread, xnticks_t tsc_ns);
+xnticks_t xnthread_get_timeout(struct xnthread *thread, xnticks_t tsc_ns);
 
-xnticks_t xnthread_get_period(xnthread_t *thread);
-
-static inline int xnthread_register(xnthread_t *thread, const char *name)
-{
-       return xnregistry_enter(name, thread, &xnthread_handle(thread), NULL);
-}
+xnticks_t xnthread_get_period(struct xnthread *thread);
 
-static inline xnthread_t *xnthread_lookup(xnhandle_t threadh)
-{
-  xnthread_t *thread = (struct xnthread *)xnregistry_fetch(threadh);
+void xnthread_prepare_wait(struct xnthread_wait_context *wc);
 
-       return (thread && xnthread_handle(thread) == threadh) ? thread : NULL;
-}
+void xnthread_finish_wait(struct xnthread_wait_context *wc,
+                         void (*cleanup)(struct xnthread_wait_context *wc));
 
 #ifdef __cplusplus
 }
diff --git a/ksrc/nucleus/pod.c b/ksrc/nucleus/pod.c
index 21763d5..78dda09 100644
--- a/ksrc/nucleus/pod.c
+++ b/ksrc/nucleus/pod.c
@@ -1085,11 +1085,6 @@ void xnpod_delete_thread(xnthread_t *thread)
                    xnpod_fatal("attempt to delete the root thread");
                );
 
-#ifdef __XENO_SIM__
-       if (nkpod->schedhook)
-               nkpod->schedhook(thread, XNDELETED);
-#endif /* __XENO_SIM__ */
-
        xnlock_get_irqsave(&nklock, s);
 
        if (xnthread_test_state(thread, XNZOMBIE))
@@ -1149,6 +1144,33 @@ void xnpod_delete_thread(xnthread_t *thread)
        }
 #endif /* CONFIG_XENO_OPT_PERVASIVE */
 
+       /*
+        * If thread is not current, has the deferred cancelability
+        * bit set, and is currently blocked on a synchronization
+        * object, then unblock it immediately but defer actual
+        * deletion if it became runnable (i.e. XNSUSP/XNHELP might be
+        * set as well). The code responsible for putting that thread
+        * to sleep should process the XNCANPND condition on the
+        * resumption path, then eventually call xnpod_delete_self()
+        * as soon as all cleanup actions are done.
+        *
+        * In other words, XNDEFCAN pertains to the thread management
+        * logic, to allow wait state finalization code to run before
+        * kernel threads are actually wiped out from the system.
+        */
+       if (sched->curr != thread &&
+           xnthread_test_state(thread, XNDEFCAN) &&
+           xnpod_unblock_thread(thread) &&
+           xnthread_test_state(thread, XNTHREAD_BLOCK_BITS) == 0) {
+               xnthread_set_info(thread, XNCANPND);
+               goto unlock_and_exit;
+       }
+
+#ifdef __XENO_SIM__
+       if (nkpod->schedhook)
+               nkpod->schedhook(thread, XNDELETED);
+#endif /* __XENO_SIM__ */
+
        trace_mark(xn_nucleus, thread_delete, "thread %p thread_name %s",
                   thread, xnthread_name(thread));
 
@@ -2287,7 +2309,7 @@ void xnpod_lock_sched(void)
 
        xnlock_get_irqsave(&nklock, s);
 
-       curr = xnpod_current_sched()->curr;
+       curr = xnpod_current_thread();
 
        if (xnthread_lock_count(curr)++ == 0)
                xnthread_set_state(curr, XNLOCK);
@@ -2303,7 +2325,7 @@ void xnpod_unlock_sched(void)
 
        xnlock_get_irqsave(&nklock, s);
 
-       curr = xnpod_current_sched()->curr;
+       curr = xnpod_current_thread();
 
        if (--xnthread_lock_count(curr) == 0) {
                xnthread_clear_state(curr, XNLOCK);
diff --git a/ksrc/nucleus/thread.c b/ksrc/nucleus/thread.c
index bd12957..7f09ba9 100644
--- a/ksrc/nucleus/thread.c
+++ b/ksrc/nucleus/thread.c
@@ -121,6 +121,7 @@ int xnthread_init(struct xnthread *thread,
        thread->rrcredit = XN_INFINITE;
        thread->wchan = NULL;
        thread->wwake = NULL;
+       thread->wcontext = NULL;
        thread->errcode = 0;
        thread->registry.handle = XN_NO_HANDLE;
        thread->registry.waitkey = NULL;
@@ -309,3 +310,32 @@ xnticks_t xnthread_get_period(xnthread_t *thread)
        return period;
 }
 EXPORT_SYMBOL_GPL(xnthread_get_period);
+
+/* NOTE: caller must provide locking */
+void xnthread_prepare_wait(struct xnthread_wait_context *wc)
+{
+       struct xnthread *curr = xnpod_current_thread();
+
+       curr->wcontext = wc;
+       wc->oldstate = xnthread_test_state(curr, XNDEFCAN);
+       xnthread_set_state(curr, XNDEFCAN);
+}
+EXPORT_SYMBOL_GPL(xnthread_prepare_wait);
+
+/* NOTE: caller must provide locking */
+void xnthread_finish_wait(struct xnthread_wait_context *wc,
+                         void (*cleanup)(struct xnthread_wait_context *wc))
+{
+       struct xnthread *curr = xnpod_current_thread();
+
+       curr->wcontext = NULL;
+       if ((wc->oldstate & XNDEFCAN) == 0)
+               xnthread_clear_state(curr, wc->oldstate);
+
+       if (xnthread_test_state(curr, XNCANPND)) {
+               if (cleanup)
+                       cleanup(wc);
+               xnpod_delete_self();
+       }
+}
+EXPORT_SYMBOL_GPL(xnthread_finish_wait);


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

Reply via email to