9
0
Fork 0

Changed needed to fix issues with task_restart()

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5615 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2013-02-06 15:43:28 +00:00
parent d8a1b61690
commit f7b7532a41
27 changed files with 808 additions and 202 deletions

View File

@ -515,4 +515,5 @@
creating a pthread.
* apps/examples and nshlib: Change name of _TCB to struct tcb_s to
match NuttX name change.
* apps/examples/ostest/restart.c: Add a test case to verify
task_restart().

View File

@ -46,7 +46,7 @@ STACKSIZE = 2048
# NuttX OS Test
ASRCS =
CSRCS = ostest_main.c dev_null.c
CSRCS = ostest_main.c dev_null.c restart.c
ifeq ($(CONFIG_ARCH_FPU),y)
CSRCS += fpu.c

View File

@ -415,17 +415,17 @@ void mqueue_test(void)
* message queue open.
*/
if (result == PTHREAD_CANCELED && g_send_mqfd)
if (result == PTHREAD_CANCELED && g_recv_mqfd)
{
if (mq_close(g_send_mqfd) < 0)
if (mq_close(g_recv_mqfd) < 0)
{
printf("mqueue_test: ERROR mq_close failed\n");
}
}
else if (result != PTHREAD_CANCELED && g_send_mqfd)
else if (result != PTHREAD_CANCELED && g_recv_mqfd)
{
printf("mqueue_test: ERROR send mqd_t left open\n");
if (mq_close(g_send_mqfd) < 0)
if (mq_close(g_recv_mqfd) < 0)
{
printf("mqueue_test: ERROR mq_close failed\n");
}
@ -433,10 +433,10 @@ void mqueue_test(void)
/* Make sure that the receive queue is closed as well */
if (g_recv_mqfd)
if (g_send_mqfd)
{
printf("mqueue_test: ERROR receive mqd_t left open\n");
if (mq_close(g_recv_mqfd) < 0)
if (mq_close(g_send_mqfd) < 0)
{
printf("sender_thread: ERROR mq_close failed\n");
}

View File

@ -111,6 +111,10 @@ int dev_null(void);
void fpu_test(void);
/* restart.c ****************************************************************/
void restart_test(void);
/* waitpid.c ****************************************************************/
#ifdef CONFIG_SCHED_WAITPID

View File

@ -329,6 +329,12 @@ static int user_main(int argc, char *argv[])
check_test_memory_usage();
#endif
/* Checkout task_restart() */
printf("\nuser_main: task_restart test\n");
restart_test();
check_test_memory_usage();
#ifdef CONFIG_SCHED_WAITPID
/* Check waitpid() and friends */

View File

@ -0,0 +1,197 @@
/****************************************************************************
* examples/ostest/restart.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 <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include "ostest.h"
#ifdef CONFIG_SCHED_WAITPID
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PRIORITY 100
#define NARGS 3
/****************************************************************************
* Private Data
****************************************************************************/
static char * const g_argv[NARGS+1] =
{
"This is argument 1",
"Argument 2 here",
"Lastly, the 3rd argument",
NULL
};
static const char g_varname[] = "VarName";
static const char g_varvalue[] = "VarValue";
static bool g_restarted;
/****************************************************************************
* Private Functions
****************************************************************************/
static int restart_main(int argc, char *argv[])
{
#ifndef CONFIG_DISABLE_ENVIRON
char *actual;
#endif
int i;
printf("restart_main: Started with argc=%d\n", argc);
/* Verify passed arguments */
if (argc != NARGS + 1)
{
printf("restart_main: ERROR: Expected argc=%d got argc=%d\n",
NARGS+1, argc);
}
for (i = 0; i <= NARGS; i++)
{
printf("restart_main: argv[%d]=\"%s\"\n", i, argv[i]);
if (i > 0 && strcmp(argv[i], g_argv[i-1]) != 0)
{
printf("restart_main: ERROR: Expected argv[%d]=\"%s\" got \"%s\"\n",
i, argv[i], g_argv[i-1]);
}
}
#ifndef CONFIG_DISABLE_ENVIRON
actual = getenv(g_varname);
if (actual)
{
if (strcmp(actual, g_varvalue) == 0)
{
printf("restart_main: Variable=%s has value=%s\n", g_varname,
g_varvalue);
}
else
{
printf("restart_main: ERROR Variable=%s has the wrong value\n", g_varname);
printf("restart_main: found=%s expected=%s\n", actual, g_varvalue);
}
}
else
{
printf("restart_main: ERROR: Variable=%s has no value\n", g_varname);
}
#endif
/* Were we restarted? */
if (!g_restarted)
{
/* No.. this is the first time we have been here */
g_restarted = true;
/* Now just wait to be restarted */
for (;;)
{
sleep(2);
printf("restart_main: I am still here\n");
}
}
return 0; /* Won't get here */
}
/****************************************************************************
* Public Functions
****************************************************************************/
void restart_test(void)
{
int ret;
/* Start the children and wait for first one to complete */
printf("\nTest task_restart()\n");
g_restarted = false;
/* Set up an environment variables */
#ifndef CONFIG_DISABLE_ENVIRON
printf("restart_main: setenv(%s, %s, TRUE)\n", g_varname, g_varvalue);
setenv(g_varname, g_varvalue, TRUE); /* Variable1=GoodValue1 */
#endif
/* Start the task */
ret = TASK_CREATE("ostest", PRIORITY, STACKSIZE, restart_main, g_argv);
if (ret < 0)
{
printf("restart_main: ERROR Failed to start restart_main\n");
}
else
{
pid_t pid = ret;
printf("restart_main: Started restart_main at PID=%d\n", pid);
/* Wait a bit and restart the task */
sleep(5);
ret = task_restart(pid);
if (ret < 0)
{
printf("restart_main: ERROR: task_restart failed\n");
}
sleep(1);
}
printf("restart_main: Exitting\n");
}
#endif /* CONFIG_SCHED_WAITPID */

View File

@ -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.

View File

@ -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>.

View File

@ -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 *******************************************************/

View File

@ -170,4 +170,3 @@ void sched_note_switch(FAR struct tcb_s *pFromTcb,
#endif /* __ASSEMBLY__ */
#endif /* __INCLUDE_SCHED_H */

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}
}
flags = irqsave();
group->tg_members[i] = group->tg_members[group->tg_nmembers - 1];
group->tg_nmembers--;
irqrestore(flags);
/* 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);
return group->tg_nmembers;
return group->tg_nmembers;
}
}
return -ENOENT;

View File

@ -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
*****************************************************************************/
@ -67,6 +78,119 @@
* Private Functions
*****************************************************************************/
/*****************************************************************************
* Name: group_signal_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 - A pointer to a struct group_signal_s instance.
*
* Return Value:
* 0 (OK) on success; a negated errno value on failure.
*
*****************************************************************************/
#ifdef HAVE_GROUP_MEMBERS
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;
int ret;
DEBUGASSERT(info);
/* Get the TCB associated with the group member */
tcb = sched_gettcb(pid);
DEBUGASSERT(tcb);
if (tcb)
{
/* Set this one as the default if we have not already set the default. */
if (!info->dtcb)
{
info->dtcb = 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)
{
/* 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.
*/
ret = sig_tcbdispatch(tcb, info->siginfo);
if (ret < 0)
{
return ret;
}
/* Limit to one thread */
info->atcb = tcb;
if (info->ptcb != NULL);
{
return 1; /* Terminate the search */
}
}
/* Is this signal unblocked on this 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.
*/
if (!info->utcb)
{
info->utcb = tcb;
}
/* Is there also an action associated with the task? */
sigact = sig_findaction(tcb, info->siginfo->si_signo);
if (sigact)
{
/* 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->siginfo);
if (ret < 0)
{
return ret;
}
/* Limit to one thread */
info->ptcb = tcb;
if (info->atcb != NULL)
{
return 1; /* Terminate the search */
}
}
}
}
return 0; /* Keep searching */
}
#endif
/*****************************************************************************
* Public Functions
*****************************************************************************/
@ -91,21 +215,20 @@
*
*****************************************************************************/
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)
{
#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 */
FAR sigactq_t *sigact;
bool dispatched = false;
bool done = false;
struct group_signal_s info;
FAR struct tcb_s *tcb;
int ret;
int i;
DEBUGASSERT(group && info);
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
@ -114,91 +237,12 @@ int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info)
sched_lock();
/* 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.
*/
/* Now visit each member of the group and perform signal handling checks. */
for (i = 0; i < group->tg_nmembers && !done; i++)
ret = group_foreachchild(group, group_signal_handler, &info);
if (ret < 0)
{
tcb = sched_gettcb(group->tg_members[i]);
DEBUGASSERT(tcb);
if (tcb)
{
/* Set this one as the default if we have not already set the
* default.
*/
if (!dtcb)
{
dtcb = tcb;
}
/* Is the thread waiting for this signal (in this case, the
* signal is probably blocked).
*/
if (sigismember(&tcb->sigwaitmask, info->si_signo) && !atcb)
{
/* 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.
*/
ret = sig_tcbdispatch(tcb, info);
if (ret < 0)
{
goto errout;
}
/* Limit to one thread */
atcb = tcb;
done = dispatched;
}
/* Is this signal unblocked on this thread? */
if (!sigismember(&tcb->sigprocmask, info->si_signo) &&
!dispatched && tcb != atcb)
{
/* Yes.. remember this TCB if we have not encountered any
* other threads that have the signal unblocked.
*/
if (!utcb)
{
utcb = tcb;
}
/* Is there also an action associated with the task? */
sigact = sig_findaction(tcb, info->si_signo);
if (sigact)
{
/* 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);
}
}
}
goto errout;
}
/* We need to dispatch the signal in any event (if nothing else so that it
@ -206,11 +250,11 @@ int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info)
* 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;

View File

@ -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? */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 */

123
nuttx/sched/task_recover.c Normal file
View File

@ -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
}

View File

@ -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;
}
}

View File

@ -42,6 +42,7 @@
#include <stdbool.h>
#include <wdog.h>
#include <queue.h>
#include <nuttx/arch.h>
#include "wd_internal.h"