summaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx')
-rw-r--r--nuttx/ChangeLog9
-rw-r--r--nuttx/Documentation/NuttxUserGuide.html2
-rw-r--r--nuttx/include/nuttx/sched.h3
-rw-r--r--nuttx/include/sched.h1
-rw-r--r--nuttx/sched/Makefile17
-rw-r--r--nuttx/sched/group_foreachchild.c96
-rw-r--r--nuttx/sched/group_internal.h7
-rw-r--r--nuttx/sched/group_killchildren.c111
-rw-r--r--nuttx/sched/group_leave.c31
-rw-r--r--nuttx/sched/group_signal.c236
-rw-r--r--nuttx/sched/mq_recover.c5
-rw-r--r--nuttx/sched/mq_timedreceive.c24
-rw-r--r--nuttx/sched/mq_timedsend.c23
-rw-r--r--nuttx/sched/os_internal.h1
-rw-r--r--nuttx/sched/pthread_condtimedwait.c26
-rw-r--r--nuttx/sched/sem_timedwait.c24
-rw-r--r--nuttx/sched/sig_timedwait.c12
-rw-r--r--nuttx/sched/task_exithook.c7
-rw-r--r--nuttx/sched/task_recover.c123
-rw-r--r--nuttx/sched/task_restart.c15
-rw-r--r--nuttx/sched/wd_create.c1
21 files changed, 586 insertions, 188 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index cd9d7b474..f35ebdbec 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -4144,4 +4144,13 @@
waiting on a message queue. task_delete() and pthread_cancel()
are dangerous interfaces. This is only one feeble recover measure
of *many* that would be needed to do this safely.
+ * sched/group_killchildren.c, task_recover.c, group_foreachchild.c,
+ sched/restart.c, sched/task_delete.c, and others: Beef up logic
+ to better support task deletion and pthread cancellation. Needed
+ to pass need OS test case for task_restart().
+ * sched/include/sched.h and all timed functions in sched/: Move
+ timer from local variables to TCB. This is needed so that if a
+ task is canceled or restarted while it is waiting for a timed
+ event, we can gracefully recover. We can't let the timer expire
+ after the task has been deleted.
diff --git a/nuttx/Documentation/NuttxUserGuide.html b/nuttx/Documentation/NuttxUserGuide.html
index 34e6fe442..3df2dfa9d 100644
--- a/nuttx/Documentation/NuttxUserGuide.html
+++ b/nuttx/Documentation/NuttxUserGuide.html
@@ -4680,7 +4680,7 @@ interface of the same name.
</p>
<p>
You can control which thread receives the signal by controlling the signal mask.
- You should, for example, be able to create a separate thread whose sole purpose it is to catch a particular signal and respond to it. Simply block the thread in the main task; then the signal will be blocked in all of the pthreads in the group too. In the one "signal processing" pthread, enable the blocked signal. This thread will then be only thread that will receive the signal.
+ You can, for example, create a single thread whose sole purpose it is to catch a particular signal and respond to it: Simply block the signal in the main task; then the signal will be blocked in all of the pthreads in the group too. In the one "signal processing" pthread, enable the blocked signal. This thread will then be only thread that will receive the signal.
</p>
<p>
<b>Signal Interfaces</b>.
diff --git a/nuttx/include/nuttx/sched.h b/nuttx/include/nuttx/sched.h
index 69ed689b8..eaf9a6b87 100644
--- a/nuttx/include/nuttx/sched.h
+++ b/nuttx/include/nuttx/sched.h
@@ -407,6 +407,8 @@ struct task_group_s
* that includes these common definitions.
*/
+FAR struct wdog_s; /* Forward reference */
+
struct tcb_s
{
/* Fields used to support list management *************************************/
@@ -452,6 +454,7 @@ struct tcb_s
#if CONFIG_RR_INTERVAL > 0
int timeslice; /* RR timeslice interval remaining */
#endif
+ FAR struct wdog_s *waitdog; /* All timed waits used this wdog */
/* Stack-Related Fields *******************************************************/
diff --git a/nuttx/include/sched.h b/nuttx/include/sched.h
index c9ff6d248..bb414b66d 100644
--- a/nuttx/include/sched.h
+++ b/nuttx/include/sched.h
@@ -170,4 +170,3 @@ void sched_note_switch(FAR struct tcb_s *pFromTcb,
#endif /* __ASSEMBLY__ */
#endif /* __INCLUDE_SCHED_H */
-
diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile
index 8866e5d87..f7445142c 100644
--- a/nuttx/sched/Makefile
+++ b/nuttx/sched/Makefile
@@ -41,14 +41,13 @@ AOBJS = $(ASRCS:.S=$(OBJEXT))
MISC_SRCS = os_start.c os_bringup.c errno_getptr.c errno_get.c errno_set.c
MISC_SRCS += sched_garbage.c sched_getfiles.c sched_getsockets.c sched_getstreams.c
-TSK_SRCS = prctl.c task_create.c task_init.c task_setup.c task_activate.c
-TSK_SRCS += task_start.c task_delete.c task_deletecurrent.c task_exithook.c
-TSK_SRCS += task_restart.c task_vfork.c exit.c getpid.c sched_addreadytorun.c
-TSK_SRCS += sched_removereadytorun.c sched_addprioritized.c sched_mergepending.c
-TSK_SRCS += sched_addblocked.c sched_removeblocked.c sched_free.c sched_gettcb.c
-TSK_SRCS += sched_verifytcb.c sched_releasetcb.c
-
-TSK_SRCS += task_spawn.c task_spawnparms.c
+TSK_SRCS = prctl.c exit.c getpid.c
+TSK_SRCS += task_create.c task_init.c task_setup.c task_activate.c task_start.c
+TSK_SRCS += task_delete.c task_deletecurrent.c task_exithook.c task_recover.c
+TSK_SRCS += task_restart.c task_spawn.c task_spawnparms.c task_vfork.c
+TSK_SRCS += sched_addreadytorun.c sched_removereadytorun.c sched_addprioritized.c
+TSK_SRCS += sched_mergepending.c sched_addblocked.c sched_removeblocked.c
+TSK_SRCS += sched_free.c sched_gettcb.c sched_verifytcb.c sched_releasetcb.c
ifneq ($(CONFIG_BINFMT_DISABLE),y)
ifeq ($(CONFIG_LIBC_EXECFUNCS),y)
@@ -86,7 +85,7 @@ endif
GRP_SRCS = group_create.c group_join.c group_leave.c group_find.c
GRP_SRCS += group_setupstreams.c group_setupidlefiles.c group_setuptaskfiles.c
-GRP_SRCS += task_getgroup.c
+GRP_SRCS += task_getgroup.c group_foreachchild.c group_killchildren.c
ifeq ($(CONFIG_SCHED_HAVE_PARENT),y)
GRP_SRCS += task_reparent.c
diff --git a/nuttx/sched/group_foreachchild.c b/nuttx/sched/group_foreachchild.c
new file mode 100644
index 000000000..bf7548e87
--- /dev/null
+++ b/nuttx/sched/group_foreachchild.c
@@ -0,0 +1,96 @@
+/****************************************************************************
+ * sched/group_foreachchild.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/sched.h>
+
+#include "group_internal.h"
+
+#ifdef HAVE_GROUP_MEMBERS
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: group_foreachchild
+ *
+ * Description:
+ * Execute a function for each child of a group.
+ *
+ * Parameters:
+ * group - The group containing the children
+ * handler - The function to be called
+ * arg - An additional argument to provide to the handler
+ *
+ * Return Value:
+ * Success (OK) is always returned unless the handler returns a non-zero
+ * value (a negated errno on errors). In that case, the traversal
+ * terminates and that non-zero value is returned.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int group_foreachchild(FAR struct task_group_s *group,
+ foreachchild_t handler, FAR void *arg)
+{
+ int ret;
+ int i;
+
+ DEBUGASSERT(group);
+
+ for (i = 0; i < group->tg_nmembers; i++)
+ {
+ ret = handler(group->tg_members[i], arg);
+ if (ret != 0)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_GROUP_MEMBERS */
diff --git a/nuttx/sched/group_internal.h b/nuttx/sched/group_internal.h
index f97f07d95..41a9c1017 100644
--- a/nuttx/sched/group_internal.h
+++ b/nuttx/sched/group_internal.h
@@ -61,6 +61,8 @@
* Public Type Definitions
****************************************************************************/
+typedef int (*foreachchild_t)(pid_t pid, FAR void *arg);
+
/****************************************************************************
* Public Data
****************************************************************************/
@@ -87,6 +89,9 @@ void group_leave(FAR struct tcb_s *tcb);
#ifdef HAVE_GROUP_MEMBERS
FAR struct task_group_s *group_findbygid(gid_t gid);
FAR struct task_group_s *group_findbypid(pid_t pid);
+int group_foreachchild(FAR struct task_group_s *group,
+ foreachchild_t handler, FAR void *arg);
+int group_killchildren(FAR struct task_tcb_s *tcb);
#endif
/* Convenience functions */
@@ -96,7 +101,7 @@ FAR struct task_group_s *task_getgroup(pid_t pid);
/* Signaling group members */
#ifndef CONFIG_DISABLE_SIGNALS
-int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info);
+int group_signal(FAR struct task_group_s *group, FAR siginfo_t *siginfo);
#endif
#endif /* HAVE_TASK_GROUP */
diff --git a/nuttx/sched/group_killchildren.c b/nuttx/sched/group_killchildren.c
new file mode 100644
index 000000000..a96ecd113
--- /dev/null
+++ b/nuttx/sched/group_killchildren.c
@@ -0,0 +1,111 @@
+/****************************************************************************
+ * sched/group_killchildren.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sched.h>
+
+#include "group_internal.h"
+
+#if HAVE_GROUP_MEMBERS
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/*****************************************************************************
+ * Name: group_killchildren_handler
+ *
+ * Description:
+ * Callback from group_foreachchild that handles one member of the group.
+ *
+ * Parameters:
+ * pid - The ID of the group member that may be signalled.
+ * arg - The PID of the thread to be retained.
+ *
+ * Return Value:
+ * 0 (OK) on success; a negated errno value on failure.
+ *
+ *****************************************************************************/
+
+static int group_killchildren_handler(pid_t pid, FAR void *arg)
+{
+ int ret = OK;
+
+ /* Is this the pthread that we are looking for? */
+
+ if (pid != (pid_t)((uintptr_t)arg))
+ {
+ /* Yes.. cancel it */
+
+ ret = pthread_cancel(pid);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: group_killchildren
+ *
+ * Description:
+ * Delete all children of a task except for the specified task. This is
+ * used by the task restart logic. When the main task is restarted,
+ * all of its child pthreads must be terminated.
+ *
+ * Parameters:
+ * tcb - TCB of the task to be retained.
+ *
+ * Return Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int group_killchildren(FAR struct task_tcb_s *tcb)
+{
+ return group_foreachchild(tcb->cmn.group, group_killchildren_handler,
+ (FAR void *)((uintptr_t)tcb->cmn.pid));
+}
+
+#endif /* HAVE_GROUP_MEMBERS */
diff --git a/nuttx/sched/group_leave.c b/nuttx/sched/group_leave.c
index b3d2f20f8..490a66ec0 100644
--- a/nuttx/sched/group_leave.c
+++ b/nuttx/sched/group_leave.c
@@ -260,31 +260,18 @@ static inline int group_removemember(FAR struct task_group_s *group, pid_t pid)
if (group->tg_members[i] == pid)
{
- /* Yes.. break out of the loop. We don't do the actual
- * removal here, instead we re-test i and do the adjustments
- * outside of the loop. We do this because we want the
- * DEBUGASSERT to work properly.
+ /* Remove the member from the array of members. This must be an
+ * atomic operation because the member array may be accessed from
+ * interrupt handlers (read-only).
*/
- break;
- }
- }
-
- /* Now, test if we found the task in the array of members. */
-
- if (i < group->tg_nmembers)
- {
- /* Remove the member from the array of members. This must be an
- * atomic operation because the member array may be accessed from
- * interrupt handlers (read-only).
- */
-
- flags = irqsave();
- group->tg_members[i] = group->tg_members[group->tg_nmembers - 1];
- group->tg_nmembers--;
- irqrestore(flags);
+ flags = irqsave();
+ group->tg_members[i] = group->tg_members[group->tg_nmembers - 1];
+ group->tg_nmembers--;
+ irqrestore(flags);
- return group->tg_nmembers;
+ return group->tg_nmembers;
+ }
}
return -ENOENT;
diff --git a/nuttx/sched/group_signal.c b/nuttx/sched/group_signal.c
index 0dc41be1c..663441898 100644
--- a/nuttx/sched/group_signal.c
+++ b/nuttx/sched/group_signal.c
@@ -59,6 +59,17 @@
* Private Types
*****************************************************************************/
+#ifdef HAVE_GROUP_MEMBERS
+struct group_signal_s
+{
+ FAR siginfo_t *siginfo; /* Signal to be dispatched */
+ FAR struct tcb_s *dtcb; /* Default, valid TCB */
+ FAR struct tcb_s *utcb; /* TCB with this signal unblocked */
+ FAR struct tcb_s *atcb; /* This TCB was awakened */
+ FAR struct tcb_s *ptcb; /* This TCB received the signal */
+};
+#endif
+
/*****************************************************************************
* Private Data
*****************************************************************************/
@@ -68,149 +79,182 @@
*****************************************************************************/
/*****************************************************************************
- * Public Functions
- *****************************************************************************/
-
-/*****************************************************************************
- * Name: group_signal
+ * Name: group_signal_handler
*
* Description:
- * Send a signal to every member of the group.
+ * Callback from group_foreachchild that handles one member of the group.
*
* Parameters:
- * group - The task group that needs to be signalled.
+ * pid - The ID of the group member that may be signalled.
+ * arg - A pointer to a struct group_signal_s instance.
*
* Return Value:
* 0 (OK) on success; a negated errno value on failure.
*
- * Assumptions:
- * Called during task termination in a safe context. No special precautions
- * are required here. Because signals can be sent from interrupt handlers,
- * this function may be called indirectly in the context of an interrupt
- * handler.
- *
*****************************************************************************/
-int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info)
-{
#ifdef HAVE_GROUP_MEMBERS
- FAR struct tcb_s *tcb; /* Working TCB */
- FAR struct tcb_s *dtcb = NULL; /* Default, valid TCB */
- FAR struct tcb_s *utcb = NULL; /* TCB with this signal unblocked */
- FAR struct tcb_s *atcb = NULL; /* This TCB was awakened */
- FAR struct tcb_s *ptcb = NULL; /* This TCB received the signal */
+static int group_signal_handler(pid_t pid, FAR void *arg)
+{
+ FAR struct group_signal_s *info = (FAR struct group_signal_s *)arg;
+ FAR struct tcb_s *tcb;
FAR sigactq_t *sigact;
- bool dispatched = false;
- bool done = false;
int ret;
- int i;
- DEBUGASSERT(group && info);
+ DEBUGASSERT(info);
- /* Make sure that pre-emption is disabled to that we signal all of the
- * members of the group before any of them actually run. (This does
- * nothing if were were called from an interrupt handler).
- */
+ /* Get the TCB associated with the group member */
- sched_lock();
+ tcb = sched_gettcb(pid);
+ DEBUGASSERT(tcb);
+ if (tcb)
+ {
+ /* Set this one as the default if we have not already set the default. */
- /* Now visit each member of the group and do the same checks. The main
- * task is always the first member in the member array (unless it has
- * already exited). So the main task will get predence in the following
- * search algorithm.
- */
+ if (!info->dtcb)
+ {
+ info->dtcb = tcb;
+ }
- for (i = 0; i < group->tg_nmembers && !done; i++)
- {
- tcb = sched_gettcb(group->tg_members[i]);
- DEBUGASSERT(tcb);
- if (tcb)
+ /* Is the thread waiting for this signal (in this case, the signal is
+ * probably blocked).
+ */
+
+ if (sigismember(&tcb->sigwaitmask, info->siginfo->si_signo) && !info->atcb)
{
- /* Set this one as the default if we have not already set the
- * default.
+ /* Yes.. This means that the task is suspended, waiting for this
+ * signal to occur. Stop looking and use this TCB. The
+ * requirement is this: If a task group receives a signal and
+ * more than one thread is waiting on that signal, then one and
+ * only one indeterminate thread out of that waiting group will
+ * receive the signal.
*/
- if (!dtcb)
+ ret = sig_tcbdispatch(tcb, info->siginfo);
+ if (ret < 0)
{
- dtcb = tcb;
+ return ret;
}
- /* Is the thread waiting for this signal (in this case, the
- * signal is probably blocked).
- */
+ /* Limit to one thread */
- if (sigismember(&tcb->sigwaitmask, info->si_signo) && !atcb)
+ info->atcb = tcb;
+ if (info->ptcb != NULL);
{
- /* Yes.. This means that the task is suspended, waiting
- * for this signal to occur. Stop looking and use this TCB.
- * The requirement is this: If a task group receives a signal
- * and more than one thread is waiting on that signal, then
- * one and only one indeterminate thread out of that waiting
- * group will receive the signal.
- */
+ return 1; /* Terminate the search */
+ }
+ }
- ret = sig_tcbdispatch(tcb, info);
- if (ret < 0)
- {
- goto errout;
- }
+ /* Is this signal unblocked on this thread? */
- /* Limit to one thread */
+ if (!sigismember(&tcb->sigprocmask, info->siginfo->si_signo) &&
+ !info->ptcb && tcb != info->atcb)
+ {
+ /* Yes.. remember this TCB if we have not encountered any
+ * other threads that have the signal unblocked.
+ */
- atcb = tcb;
- done = dispatched;
+ if (!info->utcb)
+ {
+ info->utcb = tcb;
}
- /* Is this signal unblocked on this thread? */
+ /* Is there also an action associated with the task? */
- if (!sigismember(&tcb->sigprocmask, info->si_signo) &&
- !dispatched && tcb != atcb)
+ sigact = sig_findaction(tcb, info->siginfo->si_signo);
+ if (sigact)
{
- /* Yes.. remember this TCB if we have not encountered any
- * other threads that have the signal unblocked.
+ /* Yes.. then use this thread. The requirement is this:
+ * If a task group receives a signal then one and only one
+ * indeterminate thread in the task group which is not
+ * blocking the signal will receive the signal.
*/
- if (!utcb)
+ ret = sig_tcbdispatch(tcb, info->siginfo);
+ if (ret < 0)
{
- utcb = tcb;
+ return ret;
}
- /* Is there also an action associated with the task? */
+ /* Limit to one thread */
- sigact = sig_findaction(tcb, info->si_signo);
- if (sigact)
+ info->ptcb = tcb;
+ if (info->atcb != NULL)
{
- /* Yes.. then use this thread. The requirement is this:
- * If a task group receives a signal then one and only one
- * indeterminate thread in the task group which is not
- * blocking the signal will receive the signal.
- */
-
- ret = sig_tcbdispatch(tcb, info);
- if (ret < 0)
- {
- goto errout;
- }
-
- /* Limit to one thread */
-
- dispatched = true;
- done = (atcb != NULL);
+ return 1; /* Terminate the search */
}
}
}
}
+ return 0; /* Keep searching */
+}
+#endif
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: group_signal
+ *
+ * Description:
+ * Send a signal to every member of the group.
+ *
+ * Parameters:
+ * group - The task group that needs to be signalled.
+ *
+ * Return Value:
+ * 0 (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ * Called during task termination in a safe context. No special precautions
+ * are required here. Because signals can be sent from interrupt handlers,
+ * this function may be called indirectly in the context of an interrupt
+ * handler.
+ *
+ *****************************************************************************/
+
+int group_signal(FAR struct task_group_s *group, FAR siginfo_t *siginfo)
+{
+#ifdef HAVE_GROUP_MEMBERS
+ struct group_signal_s info;
+ FAR struct tcb_s *tcb;
+ int ret;
+
+ DEBUGASSERT(group && siginfo);
+
+ info.siginfo = siginfo;
+ info.dtcb = NULL; /* Default, valid TCB */
+ info.utcb = NULL; /* TCB with this signal unblocked */
+ info.atcb = NULL; /* This TCB was awakened */
+ info.ptcb = NULL; /* This TCB received the signal */
+
+ /* Make sure that pre-emption is disabled to that we signal all of the
+ * members of the group before any of them actually run. (This does
+ * nothing if were were called from an interrupt handler).
+ */
+
+ sched_lock();
+
+ /* Now visit each member of the group and perform signal handling checks. */
+
+ ret = group_foreachchild(group, group_signal_handler, &info);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
/* We need to dispatch the signal in any event (if nothing else so that it
* can be added to the pending signal list). If we found a thread with the
* signal unblocked, then use that thread.
*/
- if (!dispatched && atcb == NULL)
+ if (info.atcb == NULL && info.ptcb == NULL)
{
- if (utcb)
+ if (info.utcb)
{
- tcb = utcb;
+ tcb = info.utcb;
}
/* Otherwise use the default TCB. There should always be a default
@@ -218,19 +262,17 @@ int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info)
* signal to a pending state.
*/
- else /* if (dtcb) */
+ else /* if (info.dtcb) */
{
- DEBUGASSERT(dtcb);
- tcb = dtcb;
+ DEBUGASSERT(info.dtcb);
+ tcb = info.dtcb;
}
/* Now deliver the signal to the selected group member */
- ret = sig_tcbdispatch(tcb, info);
+ ret = sig_tcbdispatch(tcb, siginfo);
}
- /* Re-enable pre-emption an return success */
-
errout:
sched_unlock();
return ret;
diff --git a/nuttx/sched/mq_recover.c b/nuttx/sched/mq_recover.c
index 040ded64c..8f908395e 100644
--- a/nuttx/sched/mq_recover.c
+++ b/nuttx/sched/mq_recover.c
@@ -91,8 +91,9 @@
void mq_recover(FAR struct tcb_s *tcb)
{
- /* TODO: What if it was waiting for a timed message queue event?
- * We might need the wdog in the TCB so that we can cancel timeouts.
+ /* If were were waiting for a timed message queue event, then the
+ * timer was canceled and deleted in task_recover() before this
+ * function was called.
*/
/* Was the task waiting for a message queue to become non-empty? */
diff --git a/nuttx/sched/mq_timedreceive.c b/nuttx/sched/mq_timedreceive.c
index 0e68ecc67..774374eda 100644
--- a/nuttx/sched/mq_timedreceive.c
+++ b/nuttx/sched/mq_timedreceive.c
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/mq_timedreceive.c
*
- * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007-2009, 2011, 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -182,12 +182,12 @@ static void mq_rcvtimeout(int argc, uint32_t pid)
ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
int *prio, const struct timespec *abstime)
{
- WDOG_ID wdog;
+ FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
FAR mqmsg_t *mqmsg;
- irqstate_t saved_state;
- int ret = ERROR;
+ irqstate_t saved_state;
+ int ret = ERROR;
- DEBUGASSERT(up_interrupt_context() == false);
+ DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);
/* Verify the input parameters and, in case of an error, set
* errno appropriately.
@@ -209,8 +209,8 @@ ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
* before we enter the following critical section.
*/
- wdog = wd_create();
- if (!wdog)
+ rtcb->waitdog = wd_create();
+ if (!rtcb->waitdog)
{
set_errno(EINVAL);
return ERROR;
@@ -261,13 +261,14 @@ ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
set_errno(result);
irqrestore(saved_state);
sched_unlock();
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
return ERROR;
}
/* Start the watchdog */
- wd_start(wdog, ticks, (wdentry_t)mq_rcvtimeout, 1, getpid());
+ wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_rcvtimeout, 1, getpid());
}
/* Get the message from the message queue */
@@ -278,7 +279,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
* it was never started)
*/
- wd_cancel(wdog);
+ wd_cancel(rtcb->waitdog);
/* We can now restore interrupts */
@@ -298,6 +299,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
}
sched_unlock();
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
return ret;
}
diff --git a/nuttx/sched/mq_timedsend.c b/nuttx/sched/mq_timedsend.c
index d48dd32ce..31de84fc4 100644
--- a/nuttx/sched/mq_timedsend.c
+++ b/nuttx/sched/mq_timedsend.c
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/mq_timedsend.c
*
- * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007-2009, 2011, 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -182,13 +182,13 @@ static void mq_sndtimeout(int argc, uint32_t pid)
int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
const struct timespec *abstime)
{
- WDOG_ID wdog;
- FAR msgq_t *msgq;
+ FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
+ FAR msgq_t *msgq;
FAR mqmsg_t *mqmsg = NULL;
- irqstate_t saved_state;
- int ret = ERROR;
+ irqstate_t saved_state;
+ int ret = ERROR;
- DEBUGASSERT(up_interrupt_context() == false);
+ DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);
/* Verify the input parameters -- setting errno appropriately
* on any failures to verify.
@@ -214,8 +214,8 @@ int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
* before we enter the following critical section.
*/
- wdog = wd_create();
- if (!wdog)
+ rtcb->waitdog = wd_create();
+ if (!rtcb->waitdog)
{
set_errno(EINVAL);
return ERROR;
@@ -272,7 +272,7 @@ int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
{
/* Start the watchdog */
- wd_start(wdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());
+ wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());
/* And wait for the message queue to be non-empty */
@@ -282,7 +282,7 @@ int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
* or ETIMEOUT. Cancel the watchdog timer in any event.
*/
- wd_cancel(wdog);
+ wd_cancel(rtcb->waitdog);
}
/* That is the end of the atomic operations */
@@ -313,7 +313,8 @@ int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
}
sched_unlock();
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
return ret;
}
diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h
index eef0d5e70..5d97f62f1 100644
--- a/nuttx/sched/os_internal.h
+++ b/nuttx/sched/os_internal.h
@@ -262,6 +262,7 @@ int task_schedsetup(FAR struct task_tcb_s *tcb, int priority, start_t start,
int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name, FAR char * const argv[]);
void task_exithook(FAR struct tcb_s *tcb, int status);
int task_deletecurrent(void);
+void task_recover(FAR struct tcb_s *tcb);
#ifndef CONFIG_CUSTOM_STACK
int kernel_thread(FAR const char *name, int priority, int stack_size,
diff --git a/nuttx/sched/pthread_condtimedwait.c b/nuttx/sched/pthread_condtimedwait.c
index 3914f40ff..37534dacf 100644
--- a/nuttx/sched/pthread_condtimedwait.c
+++ b/nuttx/sched/pthread_condtimedwait.c
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/pthread_condtimedwait.c
*
- * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -46,6 +46,7 @@
#include <signal.h>
#include <time.h>
#include <errno.h>
+#include <assert.h>
#include <wdog.h>
#include <debug.h>
@@ -179,15 +180,17 @@ static void pthread_condtimedout(int argc, uint32_t pid, uint32_t signo)
int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
FAR const struct timespec *abstime)
{
- WDOG_ID wdog;
- int ticks;
- int mypid = (int)getpid();
- irqstate_t int_state;
- int ret = OK;
- int status;
+ FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
+ int ticks;
+ int mypid = (int)getpid();
+ irqstate_t int_state;
+ int ret = OK;
+ int status;
sdbg("cond=0x%p mutex=0x%p abstime=0x%p\n", cond, mutex, abstime);
+ DEBUGASSERT(rtcb->waitdog == NULL);
+
/* Make sure that non-NULL references were provided. */
if (!cond || !mutex)
@@ -215,8 +218,8 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
{
/* Create a watchdog */
- wdog = wd_create();
- if (!wdog)
+ rtcb->waitdog = wd_create();
+ if (!rtcb->waitdog)
{
ret = EINVAL;
}
@@ -280,7 +283,7 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
{
/* Start the watchdog */
- wd_start(wdog, ticks, (wdentry_t)pthread_condtimedout,
+ wd_start(rtcb->waitdog, ticks, (wdentry_t)pthread_condtimedout,
2, (uint32_t)mypid, (uint32_t)SIGCONDTIMEDOUT);
/* Take the condition semaphore. Do not restore interrupts
@@ -343,7 +346,8 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
/* We no longer need the watchdog */
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
}
}
diff --git a/nuttx/sched/sem_timedwait.c b/nuttx/sched/sem_timedwait.c
index df78a3997..344b3a072 100644
--- a/nuttx/sched/sem_timedwait.c
+++ b/nuttx/sched/sem_timedwait.c
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/sem_timedwait.c
*
- * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2011, 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -164,13 +164,13 @@ static void sem_timeout(int argc, uint32_t pid)
int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
{
- WDOG_ID wdog;
+ FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
irqstate_t flags;
int ticks;
int err;
int ret = ERROR;
- DEBUGASSERT(up_interrupt_context() == false);
+ DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);
/* Verify the input parameters and, in case of an error, set
* errno appropriately.
@@ -189,8 +189,8 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
* front before we enter the following critical section.
*/
- wdog = wd_create();
- if (!wdog)
+ rtcb->waitdog = wd_create();
+ if (!rtcb->waitdog)
{
err = ENOMEM;
goto errout;
@@ -214,7 +214,8 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
/* We got it! */
irqrestore(flags);
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
return OK;
}
@@ -252,7 +253,7 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
/* Start the watchdog */
err = OK;
- wd_start(wdog, ticks, (wdentry_t)sem_timeout, 1, getpid());
+ wd_start(rtcb->waitdog, ticks, (wdentry_t)sem_timeout, 1, getpid());
/* Now perform the blocking wait */
@@ -260,12 +261,13 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
/* Stop the watchdog timer */
- wd_cancel(wdog);
+ wd_cancel(rtcb->waitdog);
/* We can now restore interrupts and delete the watchdog */
irqrestore(flags);
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
/* We are either returning success or an error detected by sem_wait()
* or the timeout detected by sem_timeout(). The 'errno' value has
@@ -279,7 +281,9 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
errout_disabled:
irqrestore(flags);
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
+
errout:
set_errno(err);
return ERROR;
diff --git a/nuttx/sched/sig_timedwait.c b/nuttx/sched/sig_timedwait.c
index 21e40f170..fd8939a1d 100644
--- a/nuttx/sched/sig_timedwait.c
+++ b/nuttx/sched/sig_timedwait.c
@@ -181,11 +181,12 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head;
sigset_t intersection;
FAR sigpendq_t *sigpend;
- WDOG_ID wdog;
irqstate_t saved_state;
int32_t waitticks;
int ret = ERROR;
+ DEBUGASSERT(rtcb->waitdog == NULL);
+
sched_lock(); /* Not necessary */
/* Several operations must be performed below: We must determine if any
@@ -265,8 +266,8 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
/* Create a watchdog */
- wdog = wd_create();
- if (wdog)
+ rtcb->waitdog = wd_create();
+ if (rtcb->waitdog)
{
/* This little of nonsense is necessary for some
* processors where sizeof(pointer) < sizeof(uint32_t).
@@ -278,7 +279,7 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
/* Start the watchdog */
- wd_start(wdog, waitticks, (wdentry_t)sig_timeout, 1, wdparm.dwarg);
+ wd_start(rtcb->waitdog, waitticks, (wdentry_t)sig_timeout, 1, wdparm.dwarg);
/* Now wait for either the signal or the watchdog */
@@ -286,7 +287,8 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
/* We no longer need the watchdog */
- wd_delete(wdog);
+ wd_delete(rtcb->waitdog);
+ rtcb->waitdog = NULL;
}
}
diff --git a/nuttx/sched/task_exithook.c b/nuttx/sched/task_exithook.c
index 5633786aa..0c1c99929 100644
--- a/nuttx/sched/task_exithook.c
+++ b/nuttx/sched/task_exithook.c
@@ -51,7 +51,6 @@
#include "os_internal.h"
#include "group_internal.h"
#include "sig_internal.h"
-#include "mq_internal.h"
/****************************************************************************
* Definitions
@@ -592,12 +591,10 @@ void task_exithook(FAR struct tcb_s *tcb, int status)
task_onexit(tcb, status);
/* If the task was terminated by another task, it may be in an unknown
- * state. Make some feed effort to recover the state.
+ * state. Make some feeble effort to recover the state.
*/
-#ifndef CONFIG_DISABLE_MQUEUE
- mq_recover(tcb);
-#endif
+ task_recover(tcb);
/* Leave the task group */
diff --git a/nuttx/sched/task_recover.c b/nuttx/sched/task_recover.c
new file mode 100644
index 000000000..1185f4f7f
--- /dev/null
+++ b/nuttx/sched/task_recover.c
@@ -0,0 +1,123 @@
+/****************************************************************************
+ * sched/task_recover.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <wdog.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/sched.h>
+
+#include "os_internal.h"
+#include "mq_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Type Declarations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: task_recover
+ *
+ * Description:
+ * This function is called when a task is deleted via task_deleted or
+ * via pthread_cancel. I checks if the task was waiting for a message
+ * queue event and adjusts counts appropriately.
+ *
+ * Inputs:
+ * tcb - The TCB of the terminated task or thread
+ *
+ * Return Value:
+ * None.
+ *
+ * Assumptions:
+ * This function is called from task deletion logic in a safe context.
+ *
+ ****************************************************************************/
+
+void task_recover(FAR struct tcb_s *tcb)
+{
+ irqstate_t flags;
+
+ /* The task is being deleted. If it is waiting for any timed event, then
+ * tcb->waitdog will be non-NULL. Cancel the watchdog now so that no
+ * events occur after the watchdog expires. Obviously there are lots of
+ * race conditions here so this will most certainly have to be revisited in
+ * the future.
+ */
+
+ flags = irqsave();
+ if (tcb->waitdog)
+ {
+ (void)wd_cancel(tcb->waitdog);
+ (void)wd_delete(tcb->waitdog);
+ tcb->waitdog = NULL;
+ }
+
+ irqrestore(flags);
+
+ /* Handle cases where the thread was waiting for a message queue event */
+
+#ifndef CONFIG_DISABLE_MQUEUE
+ mq_recover(tcb);
+#endif
+}
diff --git a/nuttx/sched/task_restart.c b/nuttx/sched/task_restart.c
index 578585c8e..5f3d80156 100644
--- a/nuttx/sched/task_restart.c
+++ b/nuttx/sched/task_restart.c
@@ -46,6 +46,7 @@
#include <nuttx/arch.h>
#include "os_internal.h"
+#include "group_internal.h"
#include "sig_internal.h"
/****************************************************************************
@@ -143,6 +144,16 @@ int task_restart(pid_t pid)
return ERROR;
}
+ /* Try to recover from any bad states */
+
+ task_recover((FAR struct tcb_s *)tcb);
+
+ /* Kill any children of this thread */
+
+#if HAVE_GROUP_MEMBERS
+ (void)group_killchildren(tcb);
+#endif
+
/* Remove the TCB from whatever list it is in. At this point, the
* TCB should no longer be accessible to the system
*/
@@ -186,8 +197,8 @@ int task_restart(pid_t pid)
status = task_activate((FAR struct tcb_s *)tcb);
if (status != OK)
{
- dq_rem((FAR dq_entry_t*)tcb, (dq_queue_t*)&g_inactivetasks);
- sched_releasetcb((FAR struct tcb_s *)tcb);
+ (void)task_delete(pid);
+ set_errno(-status);
return ERROR;
}
}
diff --git a/nuttx/sched/wd_create.c b/nuttx/sched/wd_create.c
index d86171bf7..61385b71a 100644
--- a/nuttx/sched/wd_create.c
+++ b/nuttx/sched/wd_create.c
@@ -42,6 +42,7 @@
#include <stdbool.h>
#include <wdog.h>
#include <queue.h>
+
#include <nuttx/arch.h>
#include "wd_internal.h"