From 25e9b8d0846b68a18014c63146234193bfe539e8 Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 24 Jan 2013 19:19:38 +0000 Subject: Fix poll/select issue reported by Qiang git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5559 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/net/net_poll.c | 156 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 45 deletions(-) (limited to 'nuttx/net/net_poll.c') diff --git a/nuttx/net/net_poll.c b/nuttx/net/net_poll.c index 2e73bd73c..ea6f54a6b 100644 --- a/nuttx/net/net_poll.c +++ b/nuttx/net/net_poll.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/net_poll.c * - * Copyright (C) 2008-2009, 2011-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2008-2009, 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -46,11 +46,13 @@ #include #include #include +#include #include +#include +#include #include #include -#include #include @@ -75,6 +77,14 @@ /**************************************************************************** * Private Types ****************************************************************************/ +/* This is an allocated container that holds the poll-related information */ + +struct net_poll_s +{ + FAR struct socket *psock; /* Needed to handle loss of connection */ + struct pollfd *fds; /* Needed to handle poll events */ + FAR struct uip_callback_s *cb; /* Needed to teardown the poll */ +}; /**************************************************************************** * Private Functions @@ -101,16 +111,18 @@ ****************************************************************************/ #ifdef HAVE_NETPOLL -static uint16_t poll_interrupt(struct uip_driver_s *dev, FAR void *conn, +static uint16_t poll_interrupt(FAR struct uip_driver_s *dev, FAR void *conn, FAR void *pvpriv, uint16_t flags) { - FAR struct pollfd *fds = (FAR struct pollfd *)pvpriv; + FAR struct net_poll_s *info = (FAR struct net_poll_s *)pvpriv; nllvdbg("flags: %04x\n", flags); + DEBUGASSERT(info && info->psock && info->fds); + /* 'priv' might be null in some race conditions (?) */ - if (fds) + if (info->fds) { pollevent_t eventset = 0; @@ -118,24 +130,23 @@ static uint16_t poll_interrupt(struct uip_driver_s *dev, FAR void *conn, if ((flags & (UIP_NEWDATA|UIP_BACKLOG)) != 0) { - eventset |= POLLIN & fds->events; + eventset |= POLLIN & info->fds->events; } /* A poll is a sign that we are free to send data. */ if ((flags & UIP_POLL) != 0) { - eventset |= (POLLOUT & fds->events); + eventset |= (POLLOUT & info->fds->events); } - /* Check for a loss of connection events. - * - * REVISIT: Need to call net_lostconnection() here, but don't have - * the psock instance. What should we do? - */ + /* Check for a loss of connection events. */ if ((flags & (UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT)) != 0) { + /* Make the the connection has been lost */ + + net_lostconnection(info->psock, flags); eventset |= (POLLERR | POLLHUP); } @@ -143,8 +154,8 @@ static uint16_t poll_interrupt(struct uip_driver_s *dev, FAR void *conn, if (eventset) { - fds->revents |= eventset; - sem_post(fds->sem); + info->fds->revents |= eventset; + sem_post(info->fds->sem); } } @@ -169,9 +180,11 @@ static uint16_t poll_interrupt(struct uip_driver_s *dev, FAR void *conn, ****************************************************************************/ #ifdef HAVE_NETPOLL -static inline int net_pollsetup(FAR struct socket *psock, struct pollfd *fds) +static inline int net_pollsetup(FAR struct socket *psock, + FAR struct pollfd *fds) { FAR struct uip_conn *conn = psock->s_conn; + FAR struct net_poll_s *info; FAR struct uip_callback_s *cb; uip_lock_t flags; int ret; @@ -185,6 +198,14 @@ static inline int net_pollsetup(FAR struct socket *psock, struct pollfd *fds) } #endif + /* Allocate a container to hold the poll information */ + + info = (FAR struct net_poll_s *)kmalloc(sizeof(struct net_poll_s)); + if (!info) + { + return -ENOMEM; + } + /* Some of the following must be atomic */ flags = uip_lock(); @@ -195,18 +216,29 @@ static inline int net_pollsetup(FAR struct socket *psock, struct pollfd *fds) if (!cb) { ret = -EBUSY; - goto errout_with_irq; + goto errout_with_lock; } - /* Initialize the callback structure */ + /* Initialize the poll info container */ + + info->psock = psock; + info->fds = fds; + info->cb = cb; + + /* Initialize the callback structure. Save the reference to the info + * structure as callback private data so that it will be available during + * callback processing. + */ cb->flags = (UIP_NEWDATA|UIP_BACKLOG|UIP_POLL|UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT); - cb->priv = (FAR void *)fds; + cb->priv = (FAR void *)info; cb->event = poll_interrupt; - /* Save the nps reference in the poll structure for use at teardown as well */ + /* Save the reference in the poll info structure as fds private as well + * for use durring poll teardown as well. + */ - fds->priv = (FAR void *)cb; + fds->priv = (FAR void *)info; #ifdef CONFIG_NET_TCPBACKLOG /* Check for read data or backlogged connection availability now */ @@ -240,7 +272,8 @@ static inline int net_pollsetup(FAR struct socket *psock, struct pollfd *fds) uip_unlock(flags); return OK; -errout_with_irq: +errout_with_lock: + kfree(info); uip_unlock(flags); return ret; } @@ -261,10 +294,11 @@ errout_with_irq: ****************************************************************************/ #ifdef HAVE_NETPOLL -static inline int net_pollteardown(FAR struct socket *psock, struct pollfd *fds) +static inline int net_pollteardown(FAR struct socket *psock, + FAR struct pollfd *fds) { FAR struct uip_conn *conn = psock->s_conn; - FAR struct uip_callback_s *cb; + FAR struct net_poll_s *info; uip_lock_t flags; /* Sanity check */ @@ -278,18 +312,23 @@ static inline int net_pollteardown(FAR struct socket *psock, struct pollfd *fds) /* Recover the socket descriptor poll state info from the poll structure */ - cb = (FAR struct uip_callback_s *)fds->priv; - if (cb) + info = (FAR struct net_poll_s *)fds->priv; + DEBUGASSERT(info && info->fds && info->cb); + if (info) { /* Release the callback */ flags = uip_lock(); - uip_tcpcallbackfree(conn, cb); + uip_tcpcallbackfree(conn, info->cb); uip_unlock(flags); /* Release the poll/select data slot */ - fds->priv = NULL; + info->fds->priv = NULL; + + /* Then free the poll info container */ + + kfree(info); } return OK; @@ -308,7 +347,7 @@ static inline int net_pollteardown(FAR struct socket *psock, struct pollfd *fds) * to this function. * * Input Parameters: - * fd - The socket descriptor of interest + * psock - An instance of the internal socket structure. * fds - The structure describing the events to be monitored, OR NULL if * this is a request to stop monitoring events. * setup - true: Setup up the poll; false: Teardown the poll @@ -318,26 +357,11 @@ static inline int net_pollteardown(FAR struct socket *psock, struct pollfd *fds) * ****************************************************************************/ -#ifndef CONFIG_DISABLE_POLL -int net_poll(int sockfd, struct pollfd *fds, bool setup) +#if !defined(CONFIG_DISABLE_POLL) && defined(HAVE_NETPOLL) +int psock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) { -#ifndef HAVE_NETPOLL - return -ENOSYS; -#else - FAR struct socket *psock; int ret; - /* Get the underlying socket structure and verify that the sockfd - * corresponds to valid, allocated socket - */ - - psock = sockfd_socket(sockfd); - if (!psock || psock->s_crefs <= 0) - { - ret = -EBADF; - goto errout; - } - #ifdef CONFIG_NET_UDP /* poll() not supported for UDP */ @@ -365,6 +389,48 @@ int net_poll(int sockfd, struct pollfd *fds, bool setup) errout: return ret; +} +#endif + +/**************************************************************************** + * Function: net_poll + * + * Description: + * The standard poll() operation redirects operations on socket descriptors + * to this function. + * + * Input Parameters: + * fd - The socket descriptor of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +int net_poll(int sockfd, struct pollfd *fds, bool setup) +{ +#ifndef HAVE_NETPOLL + return -ENOSYS; +#else + FAR struct socket *psock; + + /* Get the underlying socket structure and verify that the sockfd + * corresponds to valid, allocated socket + */ + + psock = sockfd_socket(sockfd); + if (!psock || psock->s_crefs <= 0) + { + return -EBADF; + } + + /* Then let psock_poll() do the heavy lifting */ + + return psock_poll(psock, fds, setup); #endif /* HAVE_NETPOLL */ } #endif /* !CONFIG_DISABLE_POLL */ -- cgit v1.2.3 From a2ec48846f786e72a976038c9467b25a61ad5a9f Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 24 Jan 2013 23:18:32 +0000 Subject: Fix some missing logic and inconsistencies in child status logic; Fix a bug introduced into sigaction() git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5560 42af7a65-404d-4744-a932-0658087f49c3 --- apps/examples/ostest/ostest_main.c | 28 ++++++++ nuttx/TODO | 15 +++- nuttx/net/net_poll.c | 4 +- nuttx/sched/Kconfig | 7 ++ nuttx/sched/os_internal.h | 1 + nuttx/sched/sched_waitid.c | 136 +++++++++++++++++++++++++++---------- nuttx/sched/sched_waitpid.c | 132 ++++++++++++++++++++++------------- nuttx/sched/sig_action.c | 22 +++--- nuttx/sched/task_childstatus.c | 36 ++++++++++ nuttx/sched/task_reparent.c | 24 ++++++- nuttx/sched/task_setup.c | 23 ++++--- 11 files changed, 322 insertions(+), 106 deletions(-) (limited to 'nuttx/net/net_poll.c') diff --git a/apps/examples/ostest/ostest_main.c b/apps/examples/ostest/ostest_main.c index aab1ff045..3e4197fdc 100644 --- a/apps/examples/ostest/ostest_main.c +++ b/apps/examples/ostest/ostest_main.c @@ -43,8 +43,11 @@ #include #include #include +#include #include #include +#include + #include #include "ostest.h" @@ -264,6 +267,31 @@ static int user_main(int argc, char *argv[]) } check_test_memory_usage(); + /* If retention of child status is enable, then suppress it for this task. + * This task may produce many, many children (especially if + * CONFIG_EXAMPLES_OSTEST_LOOPS) and it does not harvest their exit status. + * As a result, the test may fail inappropriately unless retention of + * child exit status is disabled. + * + * So basically, this tests that child status can be disabled, but cannot + * verify that status is retained correctly. + */ + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + { + struct sigaction sa; + int ret; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + ret = sigaction(SIGCHLD, &sa, NULL); + if (ret < 0) + { + printf("user_main: ERROR: sigaction failed: %d\n", errno); + } + } +#endif + /* Check environment variables */ #ifndef CONFIG_DISABLE_ENVIRON show_environment(true, true, true); diff --git a/nuttx/TODO b/nuttx/TODO index ed91515ba..d6bd18d12 100644 --- a/nuttx/TODO +++ b/nuttx/TODO @@ -7,7 +7,7 @@ standards, things that could be improved, and ideas for enhancements. nuttx/ (11) Task/Scheduler (sched/) - (1) Memory Managment (mm/) + (2) Memory Managment (mm/) (3) Signals (sched/, arch/) (2) pthreads (sched/) (2) C++ Support @@ -278,6 +278,19 @@ o Memory Managment (mm/) Priority: Medium/Low, a good feature to prevent memory leaks but would have negative impact on memory usage and code size. + Title: CONTAINER ALLOCATOR + Description: There are several places where the logic requires allocation of + a tiny structure that just contains pointers to other things or + small amounts of data that needs to be bundled together. There + are examples net/net_poll.c and numerous other places. + + I am wondering if it would not be good create a pool of generic + containers (say void *[4]). There re-use these when we need + small containers. The code in sched/task_childstatus.c might + be generalized for this purpose. + Status: Open + Priority: Very low (I am not even sure that this is a good idea yet). + o Signals (sched/, arch/) ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/nuttx/net/net_poll.c b/nuttx/net/net_poll.c index ea6f54a6b..3021ac35e 100644 --- a/nuttx/net/net_poll.c +++ b/nuttx/net/net_poll.c @@ -367,8 +367,7 @@ int psock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) if (psock->s_type != SOCK_STREAM) { - ret = -ENOSYS; - goto errout; + return -ENOSYS; } #endif @@ -387,7 +386,6 @@ int psock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) ret = net_pollteardown(psock, fds); } -errout: return ret; } #endif diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig index 7ea301600..7745c2c25 100644 --- a/nuttx/sched/Kconfig +++ b/nuttx/sched/Kconfig @@ -124,6 +124,13 @@ config PREALLOC_CHILDSTATUS sa.sa_flags = SA_NOCLDWAIT; int ret = sigaction(SIGCHLD, &sa, NULL); +config DEBUG_CHILDSTATUS + bool "Enable Child Status Debug Output" + default n + depends on SCHED_CHILD_STATUS && DEBUG + ---help--- + Very detailed... I am sure that you do not want this. + config JULIAN_TIME bool "Enables Julian time conversions" default n diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h index 7d5095bad..dc87cb9a4 100644 --- a/nuttx/sched/os_internal.h +++ b/nuttx/sched/os_internal.h @@ -274,6 +274,7 @@ void weak_function task_initialize(void); FAR struct child_status_s *task_allocchild(void); void task_freechild(FAR struct child_status_s *status); void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child); +FAR struct child_status_s *task_exitchild(FAR _TCB *tcb); FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid); FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid); void task_removechildren(FAR _TCB *tcb); diff --git a/nuttx/sched/sched_waitid.c b/nuttx/sched/sched_waitid.c index 37ee26ce0..e73bc223c 100644 --- a/nuttx/sched/sched_waitid.c +++ b/nuttx/sched/sched_waitid.c @@ -53,6 +53,36 @@ * Private Functions *****************************************************************************/ +/***************************************************************************** + * Name: exitted_child + * + * Description: + * Handle the case where a child exitted properlay was we (apparently) lost + * the detch of child signal. + * + *****************************************************************************/ + +#ifdef CONFIG_SCHED_CHILD_STATUS +static void exitted_child(FAR _TCB *rtcb, FAR struct child_status_s *child, + FAR siginfo_t *info) +{ + /* The child has exited. Return the saved exit status (and some fudged + * information. + */ + + info->si_signo = SIGCHLD; + info->si_code = CLD_EXITED; + info->si_value.sival_ptr = NULL; + info->si_pid = child->ch_pid; + info->si_status = child->ch_status; + + /* Discard the child entry */ + + (void)task_removechild(rtcb, child->ch_pid); + task_freechild(child); +} +#endif + /***************************************************************************** * Public Functions *****************************************************************************/ @@ -120,9 +150,14 @@ * *****************************************************************************/ -int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) +int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) { FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head; + FAR _TCB *ctcb; +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; + bool retains; +#endif sigset_t sigset; int err; int ret; @@ -160,7 +195,11 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) */ #ifdef CONFIG_SCHED_CHILD_STATUS - if (rtcb->children == NULL) + /* Does this task retain child status? */ + + retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0); + + if (rtcb->children == NULL && retains) { /* There are no children */ @@ -169,13 +208,29 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) } else if (idtype == P_PID) { - if (task_findchild(rtcb, (pid_t)id) == NULL) - { - /* This specific pid is not a child */ + /* Get the TCB corresponding to this PID and make sure it is our child. */ + ctcb = sched_gettcb((pid_t)id); + if (!ctcb || ctcb->parent != rtcb->pid) + { err = ECHILD; goto errout_with_errno; } + + /* Does this task retain child status? */ + + if (retains) + { + /* Check if this specific pid has allocated child status? */ + + if (task_findchild(rtcb, (pid_t)id) == NULL) + { + /* This specific pid is not a child */ + + err = ECHILD; + goto errout_with_errno; + } + } } #else if (rtcb->nchildren == 0) @@ -189,7 +244,7 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) { /* Get the TCB corresponding to this PID and make sure it is our child. */ - FAR _TCB *ctcb = sched_gettcb((pid_t)id); + ctcb = sched_gettcb((pid_t)id); if (!ctcb || ctcb->parent != rtcb->pid) { err = ECHILD; @@ -209,48 +264,61 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) * instead). */ - DEBUGASSERT(rtcb->children); - if (rtcb->children == NULL) + DEBUGASSERT(!retains || rtcb->children); + if (idtype == P_ALL) { - /* This should not happen. I am just wasting your FLASH. */ + /* We are waiting for any child to exit */ - err = ECHILD; - goto errout_with_errno; + if (retains && (child = task_exitchild(rtcb)) != NULL) + { + /* A child has exitted. Apparently we missed the signal. + * Return the exit status and break out of the loop. + */ + + exitted_child(rtcb, child, info); + break; + } } - else if (idtype == P_PID) - { - FAR struct child_status_s *child; - /* We are waiting for a specific PID. Get the current status - * of the child task. - */ + /* We are waiting for a specific PID. Does this task retain child status? */ + + else if (retains) + { + /* Yes ... Get the current status of the child task. */ child = task_findchild(rtcb, (pid_t)id); DEBUGASSERT(child); - if (!child) - { - /* Yikes! The child status entry just disappeared! */ - - err = ECHILD; - goto errout_with_errno; - } - + /* Did the child exit? */ if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) { - /* The child has exited. Return the saved exit status */ + /* The child has exited. Return the exit status and break out + * of the loop. + */ - info->si_signo = SIGCHLD; - info->si_code = CLD_EXITED; - info->si_value.sival_ptr = NULL; - info->si_pid = (pid_t)id; - info->si_status = child->ch_status; + exitted_child(rtcb, child, info); + break; + } + } + else + { + /* We can use kill() with signal number 0 to determine if that + * task is still alive. + */ - /* Discard the child entry and break out of the loop */ + ret = kill((pid_t)id, 0); + if (ret < 0) + { + /* It is no longer running. We know that the child task + * was running okay when we started, so we must have lost + * the signal. In this case, we know that the task exit'ed, + * but we do not know its exit status. It would be better + * to reported ECHILD than bogus status. + */ - (void)task_removechild(rtcb, (pid_t)id); - task_freechild(child); + err = ECHILD; + goto errout_with_errno; } } #else diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c index fe3f7167d..ecdc60a2c 100644 --- a/nuttx/sched/sched_waitpid.c +++ b/nuttx/sched/sched_waitpid.c @@ -185,7 +185,7 @@ #ifndef CONFIG_SCHED_HAVE_PARENT pid_t waitpid(pid_t pid, int *stat_loc, int options) { - _TCB *tcb; + _TCB *ctcb; bool mystat; int err; int ret; @@ -208,8 +208,8 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) /* Get the TCB corresponding to this PID */ - tcb = sched_gettcb(pid); - if (!tcb) + ctcb = sched_gettcb(pid); + if (!ctcb) { err = ECHILD; goto errout_with_errno; @@ -221,15 +221,15 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) * others? */ - if (stat_loc != NULL && tcb->stat_loc == NULL) + if (stat_loc != NULL && ctcb->stat_loc == NULL) { - tcb->stat_loc = stat_loc; - mystat = true; + ctcb->stat_loc = stat_loc; + mystat = true; } /* Then wait for the task to exit */ - ret = sem_wait(&tcb->exitsem); + ret = sem_wait(&ctcb->exitsem); if (ret < 0) { /* Unlock pre-emption and return the ERROR (sem_wait has already set @@ -239,7 +239,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) if (mystat) { - tcb->stat_loc = NULL; + ctcb->stat_loc = NULL; } goto errout; @@ -274,8 +274,10 @@ errout: pid_t waitpid(pid_t pid, int *stat_loc, int options) { FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head; + FAR _TCB *ctcb; #ifdef CONFIG_SCHED_CHILD_STATUS FAR struct child_status_s *child; + bool retains; #endif FAR struct siginfo info; sigset_t sigset; @@ -303,27 +305,43 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) sched_lock(); - /* Verify that this task actually has children and that the the request - * TCB is actually a child of this task. + /* Verify that this task actually has children and that the requested PID + * is actually a child of this task. */ #ifdef CONFIG_SCHED_CHILD_STATUS - if (rtcb->children == NULL) - { - /* There are no children */ + /* Does this task retain child status? */ + retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0); + + if (rtcb->children == NULL && retains) + { err = ECHILD; goto errout_with_errno; } else if (pid != (pid_t)-1) { - /* This specific pid is not a child */ + /* Get the TCB corresponding to this PID and make sure it is our child. */ - if (task_findchild(rtcb, pid) == NULL) + ctcb = sched_gettcb(pid); + if (!ctcb || ctcb->parent != rtcb->pid) { err = ECHILD; goto errout_with_errno; } + + /* Does this task retain child status? */ + + if (retains) + { + /* Check if this specific pid has allocated child status? */ + + if (task_findchild(rtcb, pid) == NULL) + { + err = ECHILD; + goto errout_with_errno; + } + } } #else if (rtcb->nchildren == 0) @@ -337,7 +355,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) { /* Get the TCB corresponding to this PID and make sure it is our child. */ - FAR _TCB *ctcb = sched_gettcb(pid); + ctcb = sched_gettcb(pid); if (!ctcb || ctcb->parent != rtcb->pid) { err = ECHILD; @@ -350,6 +368,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) for (;;) { +#ifdef CONFIG_SCHED_CHILD_STATUS /* Check if the task has already died. Signals are not queued in * NuttX. So a possibility is that the child has died and we * missed the death of child signal (we got some other signal @@ -362,39 +381,33 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) * chilren. */ -#ifdef CONFIG_SCHED_CHILD_STATUS - DEBUGASSERT(rtcb->children); - if (rtcb->children == NULL) -#else - if (rtcb->nchildren == 0) -#endif + DEBUGASSERT(!retains || rtcb->children); + if (retains && (child = task_exitchild(rtcb)) != NULL) { - /* There were one or more children when we started so they - * must have exit'ed. There are just no bread crumbs left - * behind to tell us the PID(s) of the existed children. - * Reporting ECHLD is about all we can do in this case. + /* A child has exitted. Apparently we missed the signal. + * Return the saved exit status. */ - err = ECHILD; - goto errout_with_errno; + /* The child has exited. Return the saved exit status */ + + *stat_loc = child->ch_status; + + /* Discard the child entry and break out of the loop */ + + (void)task_removechild(rtcb, child->ch_pid); + task_freechild(child); + break; } } - else + + /* We are waiting for a specific PID. Does this task retain child status? */ + + else if (retains) { -#ifdef CONFIG_SCHED_CHILD_STATUS - /* We are waiting for a specific PID. Get the current status - * of the child task. - */ + /* Get the current status of the child task. */ child = task_findchild(rtcb, pid); DEBUGASSERT(child); - if (!child) - { - /* Yikes! The child status entry just disappeared! */ - - err = ECHILD; - goto errout_with_errno; - } /* Did the child exit? */ @@ -408,27 +421,48 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) (void)task_removechild(rtcb, pid); task_freechild(child); + break; } -#else - /* We are waiting for a specific PID. We can use kill() with - * signal number 0 to determine if that task is still alive. + } + else + { + /* We can use kill() with signal number 0 to determine if that + * task is still alive. */ ret = kill(pid, 0); if (ret < 0) { - /* It is no longer running. We know that the child task was - * running okay when we started, so we must have lost the - * signal. In this case, we know that the task exit'ed, but - * we do not know its exit status. It would be better to - * reported ECHILD that bogus status. + /* It is no longer running. We know that the child task + * was running okay when we started, so we must have lost + * the signal. In this case, we know that the task exit'ed, + * but we do not know its exit status. It would be better + * to reported ECHILD than bogus status. */ err = ECHILD; goto errout_with_errno; } -#endif } +#else + /* Check if the task has already died. Signals are not queued in + * NuttX. So a possibility is that the child has died and we + * missed the death of child signal (we got some other signal + * instead). + */ + + if (rtcb->nchildren == 0 || + (pid != (pid_t)-1 && (ret = kill((pid_t)id, 0)) < 0)) + { + /* We know that the child task was running okay we stared, + * so we must have lost the signal. What can we do? + * Let's claim we were interrupted by a signal. + */ + + err = EINTR; + goto errout_with_errno; + } +#endif /* Wait for any death-of-child signal */ diff --git a/nuttx/sched/sig_action.c b/nuttx/sched/sig_action.c index 708667993..7d84b6291 100644 --- a/nuttx/sched/sig_action.c +++ b/nuttx/sched/sig_action.c @@ -169,7 +169,6 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; FAR sigactq_t *sigact; - int ret; /* Since sigactions can only be installed from the running thread of * execution, no special precautions should be necessary. @@ -251,24 +250,31 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * if (act->sa_u._sa_handler == SIG_IGN) { - /* If there is a old sigaction, remove it from sigactionq */ + /* Do we still have a sigaction container from the previous setting? */ - sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); + if (sigact) + { + /* Yes.. Remove it from sigactionq */ + + sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); - /* And deallocate it */ + /* And deallocate it */ - sig_releaseaction(sigact); + sig_releaseaction(sigact); + } } /* A sigaction has been supplied */ else { - /* Check if a sigaction was found */ + /* Do we still have a sigaction container from the previous setting? + * If so, then re-use for the new signal action. + */ if (!sigact) { - /* No sigaction was found, but one is needed. Allocate one. */ + /* No.. Then we need to allocate one for the new action. */ sigact = sig_allocateaction(); @@ -294,7 +300,7 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * COPY_SIGACTION(&sigact->act, act); } - return ret; + return OK; } /**************************************************************************** diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c index 0f6d36c29..09aa48135 100644 --- a/nuttx/sched/task_childstatus.c +++ b/nuttx/sched/task_childstatus.c @@ -297,6 +297,42 @@ FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid) return NULL; } +/***************************************************************************** + * Name: task_exitchild + * + * Description: + * Search for any child that has exitted. + * + * Parameters: + * tcb - The TCB of the parent task to containing the child status. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure for the + * exited child. NULL is returned if not child has exited. + * + * Assumptions: + * Called during SIGCHLD processing in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *task_exitchild(FAR _TCB *tcb) +{ + FAR struct child_status_s *child; + + /* Find the status structure with the matching PID */ + + for (child = tcb->children; child; child = child->flink) + { + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + return child; + } + } + + return NULL; +} + /***************************************************************************** * Name: task_removechild * diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c index 28d371bf1..3a7ece37d 100644 --- a/nuttx/sched/task_reparent.c +++ b/nuttx/sched/task_reparent.c @@ -138,14 +138,32 @@ int task_reparent(pid_t ppid, pid_t chpid) child = task_removechild(otcb, chpid); if (child) { - /* Add the child status entry to the new parent TCB */ + /* Has the new parent supressed child exit status? */ + + if ((ptcb->flags && TCB_FLAG_NOCLDWAIT) == 0) + { + /* No.. Add the child status entry to the new parent TCB */ + + task_addchild(ptcb, child); + } + else + { + /* Yes.. Discard the child status entry */ + + task_freechild(child); + } + + /* Either case is a success */ - task_addchild(ptcb, child); ret = OK; } else { - ret = -ENOENT; + /* This would not be an error if the original parent has + * suppressed child exit status. + */ + + ret = ((otcb->flags && TCB_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; } #else DEBUGASSERT(otcb->nchildren > 0); diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c index 8f54b2e93..66c948cfd 100644 --- a/nuttx/sched/task_setup.c +++ b/nuttx/sched/task_setup.c @@ -150,7 +150,8 @@ static int task_assignpid(FAR _TCB *tcb) * Name: task_saveparent * * Description: - * Save the task ID of the parent task in the child task's TCB. + * Save the task ID of the parent task in the child task's TCB and allocate + * a child status structure to catch the child task's exit status. * * Parameters: * tcb - The TCB of the new, child task. @@ -177,11 +178,15 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) tcb->parent = rtcb->pid; - /* Exit status only needs to be retained for the case of tasks, however */ +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Exit status only needs to be retained for the case of tasks, however. + * Tasks can also suppress retention of their child status by applying + * the SA_NOCLDWAIT flag with sigaction()/ + */ - if (ttype == TCB_FLAG_TTYPE_TASK) + if (ttype == TCB_FLAG_TTYPE_TASK && + (rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0) { -#ifdef CONFIG_SCHED_CHILD_STATUS FAR struct child_status_s *child; /* Make sure that there is not already a structure for this PID in the @@ -212,11 +217,11 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) task_addchild(rtcb, child); } + } #else - DEBUGASSERT(rtcb->nchildren < UINT16_MAX); - rtcb->nchildren++; + DEBUGASSERT(rtcb->nchildren < UINT16_MAX); + rtcb->nchildren++; #endif - } } #else # define task_saveparent(tcb,ttype) @@ -318,7 +323,9 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main, tcb->flags &= ~TCB_FLAG_TTYPE_MASK; tcb->flags |= ttype; - /* Save the task ID of the parent task in the TCB */ + /* Save the task ID of the parent task in the TCB and allocate + * a child status structure. + */ task_saveparent(tcb, ttype); -- cgit v1.2.3 From e95efd5d2a08dcb62c8c635089e4fe146965db45 Mon Sep 17 00:00:00 2001 From: patacongo Date: Fri, 25 Jan 2013 17:23:38 +0000 Subject: Add framework to support task groups git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5562 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/include/nuttx/sched.h | 80 ++++++++++++++++++++++++-- nuttx/net/net_poll.c | 4 +- nuttx/sched/Makefile | 7 ++- nuttx/sched/env_dup.c | 64 ++++++++++----------- nuttx/sched/group_create.c | 111 +++++++++++++++++++++++++++++++++++ nuttx/sched/group_join.c | 107 ++++++++++++++++++++++++++++++++++ nuttx/sched/group_leave.c | 127 +++++++++++++++++++++++++++++++++++++++++ nuttx/sched/os_internal.h | 15 ++++- nuttx/sched/os_start.c | 12 +++- nuttx/sched/pthread_create.c | 43 +++++++++----- nuttx/sched/sched_releasetcb.c | 7 ++- nuttx/sched/sched_waitid.c | 6 +- nuttx/sched/sched_waitpid.c | 6 +- nuttx/sched/sig_action.c | 2 +- nuttx/sched/task_childstatus.c | 27 +++++---- nuttx/sched/task_create.c | 20 ++++++- nuttx/sched/task_exithook.c | 106 ++++++++++++++++++++++------------ nuttx/sched/task_init.c | 54 ++++++++++++++---- nuttx/sched/task_reparent.c | 10 ++-- nuttx/sched/task_setup.c | 10 ++-- 20 files changed, 677 insertions(+), 141 deletions(-) create mode 100644 nuttx/sched/group_create.c create mode 100644 nuttx/sched/group_join.c create mode 100644 nuttx/sched/group_leave.c (limited to 'nuttx/net/net_poll.c') diff --git a/nuttx/include/nuttx/sched.h b/nuttx/include/nuttx/sched.h index 1e75b5020..4a3bae5e4 100644 --- a/nuttx/include/nuttx/sched.h +++ b/nuttx/include/nuttx/sched.h @@ -57,6 +57,14 @@ /******************************************************************************** * Pre-processor Definitions ********************************************************************************/ +/* Configuration ****************************************************************/ +/* Task groups currently only supported for retention of child status */ + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) +# define HAVE_TASK_GROUP 1 +#else +# undef HAVE_TASK_GROUP +#endif /* Task Management Definitins ***************************************************/ @@ -64,7 +72,7 @@ #define MAX_LOCK_COUNT 127 -/* Values for the _TCB flags flag bits */ +/* Values for the _TCB flags bits */ #define TCB_FLAG_TTYPE_SHIFT (0) /* Bits 0-1: thread type */ #define TCB_FLAG_TTYPE_MASK (3 << TCB_FLAG_TTYPE_SHIFT) @@ -74,7 +82,10 @@ #define TCB_FLAG_NONCANCELABLE (1 << 2) /* Bit 2: Pthread is non-cancelable */ #define TCB_FLAG_CANCEL_PENDING (1 << 3) /* Bit 3: Pthread cancel is pending */ #define TCB_FLAG_ROUND_ROBIN (1 << 4) /* Bit 4: Round robin sched enabled */ -#define TCB_FLAG_NOCLDWAIT (1 << 5) /* Bit 5: Do not retain child exit status */ + +/* Values for struct task_group tg_flags */ + +#define GROUP_FLAG_NOCLDWAIT (1 << 0) /* Bit 0: Do not retain child exit status */ /* Values for struct child_status_s ch_flags */ @@ -159,6 +170,7 @@ typedef CODE void (*onexitfunc_t)(int exitcode, FAR void *arg); typedef struct msgq_s msgq_t; +/* struct environ_s **************************************************************/ /* The structure used to maintain environment variables */ #ifndef CONFIG_DISABLE_ENVIRON @@ -173,6 +185,7 @@ typedef struct environ_s environ_t; # define SIZEOF_ENVIRON_T(alloc) (sizeof(environ_t) + alloc - 1) #endif +/* struct child_status_s *********************************************************/ /* This structure is used to maintin information about child tasks. * pthreads work differently, they have join information. This is * only for child tasks. @@ -189,6 +202,7 @@ struct child_status_s }; #endif +/* struct dspace_s ***************************************************************/ /* This structure describes a reference counted D-Space region. This must be a * separately allocated "break-away" structure that can be owned by a task and * any pthreads created by the task. @@ -214,6 +228,58 @@ struct dspace_s }; #endif +/* struct task_group_s ***********************************************************/ +/* All threads created by pthread_create belong in the same task group (along with + * the thread of the original task). struct task_group_s is a shared, "breakaway" + * structure referenced by each TCB. + * + * This structure should contain *all* resources shared by tasks and threads that + * belong to the same task group: + * + * Child exit status + * Environment varibles + * PIC data space and address environments + * File descriptors + * FILE streams + * Sockets + * + * Currenty, however, this implementation only applies to child exit status. + * + * Each instance of struct task_group_s is reference counted. Each instance is + * created with a reference count of one. The reference incremeneted when each + * thread joins the group and decremented when each thread exits, leaving the + * group. When the refernce count decrements to zero, the struc task_group_s + * is free. + */ + +#ifdef HAVE_TASK_GROUP +struct task_group_s +{ + uint16_t tg_crefs; /* Count of threads sharing this data */ + uint8_t tg_flags; /* See GROUP_FLAG_* definitions */ + + /* Child exit status **********************************************************/ + + FAR struct child_status_s *tg_children; /* Head of a list of child status */ + + /* Environment varibles *******************************************************/ + /* Not yet (see type environ_t) */ + + /* PIC data space and address environments */ + /* Not yet (see struct dspace_s) */ + + /* File descriptors */ + /* Not yet (see struct filelist) */ + + /* FILE streams */ + /* Not yet (see streamlist) */ + + /* Sockets */ + /* Not yet (see struct socketlist) */ +}; +#endif + +/* _TCB **************************************************************************/ /* This is the task control block (TCB). Each task or thread is represented by * a TCB. The TCB is the heart of the NuttX task-control logic. */ @@ -225,14 +291,18 @@ struct _TCB FAR struct _TCB *flink; /* Doubly linked list */ FAR struct _TCB *blink; + /* Task Group *****************************************************************/ + +#ifdef HAVE_TASK_GROUP + FAR struct task_group_s *group; /* Pointer to shared task group data */ +#endif + /* Task Management Fields *****************************************************/ pid_t pid; /* This is the ID of the thread */ #ifdef CONFIG_SCHED_HAVE_PARENT /* Support parent-child relationship */ pid_t parent; /* This is the ID of the parent thread */ -#ifdef CONFIG_SCHED_CHILD_STATUS /* Retain child thread status */ - FAR struct child_status_s *children; /* Head of a list of child status */ -#else +#ifndef CONFIG_SCHED_CHILD_STATUS /* Retain child thread status */ uint16_t nchildren; /* This is the number active children */ #endif #endif diff --git a/nuttx/net/net_poll.c b/nuttx/net/net_poll.c index 3021ac35e..1838f541e 100644 --- a/nuttx/net/net_poll.c +++ b/nuttx/net/net_poll.c @@ -118,11 +118,11 @@ static uint16_t poll_interrupt(FAR struct uip_driver_s *dev, FAR void *conn, nllvdbg("flags: %04x\n", flags); - DEBUGASSERT(info && info->psock && info->fds); + DEBUGASSERT(!info || (info->psock && info->fds)); /* 'priv' might be null in some race conditions (?) */ - if (info->fds) + if (info) { pollevent_t eventset = 0; diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile index 1ad244450..6a710ff95 100644 --- a/nuttx/sched/Makefile +++ b/nuttx/sched/Makefile @@ -87,6 +87,8 @@ SCHED_SRCS += sched_waitid.c sched_wait.c endif endif +GRP_SRCS = group_create.c group_join.c group_leave.c + ENV_SRCS = env_getenvironptr.c env_dup.c env_share.c env_release.c ENV_SRCS += env_findvar.c env_removevar.c ENV_SRCS += env_clearenv.c env_getenv.c env_putenv.c env_setenv.c env_unsetenv.c @@ -169,8 +171,9 @@ IRQ_SRCS = irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c KMM_SRCS = kmm_initialize.c kmm_addregion.c kmm_semaphore.c KMM_SRCS = kmm_kmalloc.c kmm_kzalloc.c kmm_krealloc.c kmm_kfree.c -CSRCS = $(MISC_SRCS) $(TSK_SRCS) $(SCHED_SRCS) $(WDOG_SRCS) $(TIME_SRCS) \ - $(SEM_SRCS) $(TIMER_SRCS) $(WORK_SRCS) $(PGFILL_SRCS) $(IRQ_SRCS) +CSRCS = $(MISC_SRCS) $(TSK_SRCS) $(GRP_SRCS) $(SCHED_SRCS) $(WDOG_SRCS) +CSRCS += $(TIME_SRCS) $(SEM_SRCS) $(TIMER_SRCS) $(WORK_SRCS) $(PGFILL_SRCS) +CSRCS += $(IRQ_SRCS) ifneq ($(CONFIG_DISABLE_CLOCK),y) CSRCS += $(CLOCK_SRCS) diff --git a/nuttx/sched/env_dup.c b/nuttx/sched/env_dup.c index 033348411..30c0b7773 100644 --- a/nuttx/sched/env_dup.c +++ b/nuttx/sched/env_dup.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/env_dup.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 * * Redistribution and use in source and binary forms, with or without @@ -68,7 +68,7 @@ * exact duplicate of the parent task's environment. * * Parameters: - * ptcb The tcb to receive the newly allocated copy of the parspecifiedent + * ptcb The tcb to receive the newly allocated copy of the parent * TCB's environment structure with reference count equal to one * * Return Value: @@ -81,48 +81,42 @@ int env_dup(FAR _TCB *ptcb) { + FAR _TCB *parent = (FAR _TCB*)g_readytorun.head; + environ_t *envp = NULL; int ret = OK; - if (!ptcb ) - { - ret = -EINVAL; - } - else - { - FAR _TCB *parent = (FAR _TCB*)g_readytorun.head; - environ_t *envp = NULL; - /* Pre-emption must be disabled throughout the following because the - * environment may be shared. - */ + DEBUGASSERT(ptcb); + + /* Pre-emption must be disabled throughout the following because the + * environment may be shared. + */ + + sched_lock(); - sched_lock(); + /* Does the parent task have an environment? */ - /* Does the parent task have an environment? */ + if (parent->envp) + { + /* Yes..The parent task has an environment, duplicate it */ - if (parent->envp) + size_t envlen = parent->envp->ev_alloc; + envp = (environ_t*)kmalloc(SIZEOF_ENVIRON_T(envlen)); + if (!envp) { - /* Yes..The parent task has an environment, duplicate it */ - - size_t envlen = parent->envp->ev_alloc; - envp = (environ_t*)kmalloc(SIZEOF_ENVIRON_T( envlen )); - if (!envp) - { - ret = -ENOMEM; - } - else - { - envp->ev_crefs = 1; - envp->ev_alloc = envlen; - memcpy( envp->ev_env, parent->envp->ev_env, envlen ); - } + ret = -ENOMEM; } + else + { + envp->ev_crefs = 1; + envp->ev_alloc = envlen; + memcpy(envp->ev_env, parent->envp->ev_env, envlen); + } + } - /* Save the cloned environment in the new TCB */ - - ptcb->envp = envp; - sched_unlock(); - } + /* Save the cloned environment in the new TCB */ + ptcb->envp = envp; + sched_unlock(); return ret; } diff --git a/nuttx/sched/group_create.c b/nuttx/sched/group_create.c new file mode 100644 index 000000000..d036b9084 --- /dev/null +++ b/nuttx/sched/group_create.c @@ -0,0 +1,111 @@ +/***************************************************************************** + * sched/group_create.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include + +#include + +#ifdef HAVE_TASK_GROUP + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: group_create + * + * Description: + * Create and initialize a new task group structure for the specified TCB. + * This function is called as part of the task creation sequence. + * + * Parameters: + * tcb - The tcb in need of the task group. + * + * Return Value: + * 0 (OK) on success; a negated errno value on failure. + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +int group_create(FAR _TCB *tcb) +{ + FAR struct task_group_s *group; + + DEBUGASSERT(tcb && !tcb->group); + + /* Allocate the group structure */ + + group = (FAR struct task_group_s *)kzalloc(sizeof(struct task_group_s)); + if (!group) + { + return -ENOMEM; + } + + /* Initialize the group structure and assign it to the tcb */ + + group->tg_crefs = 1; + tcb->group = group; + return OK; +} + +#endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/group_join.c b/nuttx/sched/group_join.c new file mode 100644 index 000000000..987c1ba87 --- /dev/null +++ b/nuttx/sched/group_join.c @@ -0,0 +1,107 @@ +/***************************************************************************** + * sched/group_join.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include + +#include "os_internal.h" + +#ifdef HAVE_TASK_GROUP + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: group_join + * + * Description: + * Copy the group structure reference from one TCB to another, incrementing + * the refrence count on the group. This function is called when a pthread + * is produced within the task group so that the pthread can share the + * resources of the task group. + * + * Parameters: + * tcb - The TCB of the new "child" task that need to join the group. + * + * Return Value: + * None + * + * Assumptions: + * - The parent task from which the group will be inherited is the task at + * the thead of the ready to run list. + * - Called during thread creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +void group_join(FAR _TCB *tcb) +{ + FAR _TCB *ptcb = (FAR _TCB *)g_readytorun.head; + + DEBUGASSERT(ptcb && tcb && ptcb->group && !tcb->group); + + /* Copy the group reference from the parent to the child, incrementing the + * reference count. + */ + + tcb->group = ptcb->group; + ptcb->group->tg_crefs++; +} + +#endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/group_leave.c b/nuttx/sched/group_leave.c new file mode 100644 index 000000000..69a6ea8b2 --- /dev/null +++ b/nuttx/sched/group_leave.c @@ -0,0 +1,127 @@ +/***************************************************************************** + * sched/group_leave.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include + +#include "os_internal.h" + +#ifdef HAVE_TASK_GROUP + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: group_leave + * + * Description: + * Release a reference on a group. This function is called when a task or + * thread exits. It decrements the reference count on the group. If the + * reference count decrements to zero, then it frees the group and all of + * resources contained in the group. + * + * Parameters: + * tcb - The TCB of the task that is exiting. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task deletion in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +void group_leave(FAR _TCB *tcb) +{ + FAR struct task_group_s *group; + + DEBUGASSERT(tcb); + + /* Make sure that we have a group */ + + group = tcb->group; + if (group) + { + /* Would the reference count decrement to zero? */ + + if (group->tg_crefs > 1) + { + /* No.. just decrement the reference count and return */ + + group->tg_crefs--; + } + else + { + /* Yes.. Release all of the resource contained within the group */ + /* Free all un-reaped child exit status */ + + task_removechildren(tcb); + + /* Release the group container itself */ + + sched_free(group); + } + + tcb->group = NULL; + } +} + +#endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h index dc87cb9a4..ee5ada165 100644 --- a/nuttx/sched/os_internal.h +++ b/nuttx/sched/os_internal.h @@ -268,7 +268,20 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, int task_argsetup(FAR _TCB *tcb, FAR const char *name, FAR const char *argv[]); void task_exithook(FAR _TCB *tcb, int status); int task_deletecurrent(void); + #ifdef CONFIG_SCHED_HAVE_PARENT +int task_reparent(pid_t ppid, pid_t chpid); + +#ifdef HAVE_TASK_GROUP +int group_create(FAR _TCB *tcb); +void group_join(FAR _TCB *tcb); +void group_leave(FAR _TCB *tcb); +#else +# define group_create(tcb) +# define group_join(tcb) +# define group_leave(tcb) +#endif + #ifdef CONFIG_SCHED_CHILD_STATUS void weak_function task_initialize(void); FAR struct child_status_s *task_allocchild(void); @@ -279,8 +292,8 @@ FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid); FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid); void task_removechildren(FAR _TCB *tcb); #endif -int task_reparent(pid_t ppid, pid_t chpid); #endif + #ifndef CONFIG_CUSTOM_STACK int kernel_thread(FAR const char *name, int priority, int stack_size, main_t entry, FAR const char *argv[]); diff --git a/nuttx/sched/os_start.c b/nuttx/sched/os_start.c index a6d4e83b9..244aec967 100644 --- a/nuttx/sched/os_start.c +++ b/nuttx/sched/os_start.c @@ -202,6 +202,9 @@ const tasklist_t g_tasklisttable[NUM_TASK_STATES] = */ static FAR _TCB g_idletcb; +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) +static struct task_group_s g_idlegroup; +#endif /* This is the name of the idle task */ @@ -280,13 +283,20 @@ void os_start(void) g_idletcb.argv[0] = (char*)g_idlename; #endif /* CONFIG_TASK_NAME_SIZE */ + /* Join the IDLE group */ + +#ifdef HAVE_TASK_GROUP + g_idlegroup.tg_crefs = 1; + g_idlegroup.tg_flags = GROUP_FLAG_NOCLDWAIT; + g_idletcb.group = &g_idlegroup; +#endif + /* Then add the idle task's TCB to the head of the ready to run list */ dq_addfirst((FAR dq_entry_t*)&g_idletcb, (FAR dq_queue_t*)&g_readytorun); /* Initialize the processor-specific portion of the TCB */ - g_idletcb.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NOCLDWAIT); up_initial_state(&g_idletcb); /* Initialize the semaphore facility(if in link). This has to be done diff --git a/nuttx/sched/pthread_create.c b/nuttx/sched/pthread_create.c index f4d0d8fdf..e37c06892 100644 --- a/nuttx/sched/pthread_create.c +++ b/nuttx/sched/pthread_create.c @@ -251,6 +251,7 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, #if CONFIG_RR_INTERVAL > 0 int policy; #endif + int errcode; pid_t pid; /* If attributes were not supplied, use the default attributes */ @@ -268,6 +269,12 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, return ENOMEM; } + /* Join the parent's task group */ + +#ifdef HAVE_TASK_GROUP + group_join(ptcb); +#endif + /* Share the address environment of the parent task. NOTE: Only tasks * created throught the nuttx/binfmt loaders may have an address * environment. @@ -277,8 +284,8 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, ret = up_addrenv_share((FAR const _TCB *)g_readytorun.head, ptcb); if (ret < 0) { - sched_releasetcb(ptcb); - return -ret; + errcode = -ret; + goto errout_with_tcb; } #endif @@ -287,8 +294,8 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, ret = sched_setuppthreadfiles(ptcb); if (ret != OK) { - sched_releasetcb(ptcb); - return ret; + errcode = ret; + goto errout_with_tcb; } /* Share the parent's envionment */ @@ -300,8 +307,8 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, pjoin = (FAR join_t*)kzalloc(sizeof(join_t)); if (!pjoin) { - sched_releasetcb(ptcb); - return ENOMEM; + errcode = ENOMEM; + goto errout_with_tcb; } /* Allocate the stack for the TCB */ @@ -309,9 +316,8 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, ret = up_create_stack(ptcb, attr->stacksize); if (ret != OK) { - sched_releasetcb(ptcb); - sched_free(pjoin); - return ENOMEM; + errcode = ENOMEM; + goto errout_with_join; } /* Should we use the priority and scheduler specified in the @@ -360,9 +366,8 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, TCB_FLAG_TTYPE_PTHREAD); if (ret != OK) { - sched_releasetcb(ptcb); - sched_free(pjoin); - return EBUSY; + errcode = EBUSY; + goto errout_with_join; } /* Configure the TCB for a pthread receiving on parameter @@ -440,10 +445,18 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, dq_rem((FAR dq_entry_t*)ptcb, (dq_queue_t*)&g_inactivetasks); (void)sem_destroy(&pjoin->data_sem); (void)sem_destroy(&pjoin->exit_sem); - sched_releasetcb(ptcb); - sched_free(pjoin); - ret = EIO; + + errcode = EIO; + goto errout_with_join; } return ret; + +errout_with_join: + sched_free(pjoin); + ptcb->joininfo = NULL; + +errout_with_tcb: + sched_releasetcb(ptcb); + return errcode; } diff --git a/nuttx/sched/sched_releasetcb.c b/nuttx/sched/sched_releasetcb.c index 0557c829b..50505f579 100644 --- a/nuttx/sched/sched_releasetcb.c +++ b/nuttx/sched/sched_releasetcb.c @@ -1,7 +1,7 @@ /************************************************************************ * sched/sched_releasetcb.c * - * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2012-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -177,6 +177,11 @@ int sched_releasetcb(FAR _TCB *tcb) ret = up_addrenv_release(tcb); #endif + /* Leave the group (if we did not already leady in task_exithook.c) */ + +#ifdef HAVE_TASK_GROUP + group_leave(tcb); +#endif /* And, finally, release the TCB itself */ sched_free(tcb); diff --git a/nuttx/sched/sched_waitid.c b/nuttx/sched/sched_waitid.c index 56bfb36f0..e47e3c38c 100644 --- a/nuttx/sched/sched_waitid.c +++ b/nuttx/sched/sched_waitid.c @@ -197,9 +197,9 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) #ifdef CONFIG_SCHED_CHILD_STATUS /* Does this task retain child status? */ - retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0); + retains = ((rtcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0); - if (rtcb->children == NULL && retains) + if (rtcb->group->tg_children == NULL && retains) { /* There are no children */ @@ -264,7 +264,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) * instead). */ - DEBUGASSERT(!retains || rtcb->children); + DEBUGASSERT(!retains || rtcb->group->tg_children); if (idtype == P_ALL) { /* We are waiting for any child to exit */ diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c index d7484fca9..2d0fe2e48 100644 --- a/nuttx/sched/sched_waitpid.c +++ b/nuttx/sched/sched_waitpid.c @@ -312,9 +312,9 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) #ifdef CONFIG_SCHED_CHILD_STATUS /* Does this task retain child status? */ - retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0); + retains = ((rtcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0); - if (rtcb->children == NULL && retains) + if (rtcb->group->tg_children == NULL && retains) { err = ECHILD; goto errout_with_errno; @@ -381,7 +381,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) * chilren. */ - DEBUGASSERT(!retains || rtcb->children); + DEBUGASSERT(!retains || rtcb->group->tg_children); if (retains && (child = task_exitchild(rtcb)) != NULL) { /* A child has exited. Apparently we missed the signal. diff --git a/nuttx/sched/sig_action.c b/nuttx/sched/sig_action.c index 7d84b6291..b307c1fb6 100644 --- a/nuttx/sched/sig_action.c +++ b/nuttx/sched/sig_action.c @@ -237,7 +237,7 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * /* Mark that status should be not be retained */ - rtcb->flags |= TCB_FLAG_NOCLDWAIT; + rtcb->group->tg_flags |= GROUP_FLAG_NOCLDWAIT; /* Free all pending exit status */ diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c index 6007f03e5..aff2bdf3a 100644 --- a/nuttx/sched/task_childstatus.c +++ b/nuttx/sched/task_childstatus.c @@ -111,10 +111,11 @@ static struct child_pool_s g_child_pool; static void task_dumpchildren(FAR _TCB *tcb, FAR const char *msg) { FAR struct child_status_s *child; + FAR struct task_group_s *group = tcb->group; int i; - dbg("Parent TCB=%p: %s\n", tcb, msg); - for (i = 0, child = tcb->children; child; i++, child = child->flink) + dbg("Parent TCB=%p group=%p: %s\n", tcb, group, msg); + for (i = 0, child = group->tg_children; child; i++, child = child->flink) { dbg(" %d. ch_flags=%02x ch_pid=%d ch_status=%d\n", i, child->ch_flags, child->ch_pid, child->ch_status); @@ -250,10 +251,12 @@ void task_freechild(FAR struct child_status_s *child) void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child) { + FAR struct task_group_s *group = tcb->group; + /* Add the entry into the TCB list of children */ - child->flink = tcb->children; - tcb->children = child; + child->flink = group->tg_children; + group->tg_children = child; task_dumpchildren(tcb, "task_addchild"); } @@ -282,11 +285,12 @@ void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child) FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid) { + FAR struct task_group_s *group = tcb->group; FAR struct child_status_s *child; /* Find the status structure with the matching PID */ - for (child = tcb->children; child; child = child->flink) + for (child = group->tg_children; child; child = child->flink) { if (child->ch_pid == pid) { @@ -318,11 +322,12 @@ FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid) FAR struct child_status_s *task_exitchild(FAR _TCB *tcb) { + FAR struct task_group_s *group = tcb->group; FAR struct child_status_s *child; /* Find the status structure of any child task that has exitted. */ - for (child = tcb->children; child; child = child->flink) + for (child = group->tg_children; child; child = child->flink) { if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) { @@ -357,12 +362,13 @@ FAR struct child_status_s *task_exitchild(FAR _TCB *tcb) FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid) { + FAR struct task_group_s *group = tcb->group; FAR struct child_status_s *curr; FAR struct child_status_s *prev; /* Find the status structure with the matching PID */ - for (prev = NULL, curr = tcb->children; + for (prev = NULL, curr = group->tg_children; curr; prev = curr, curr = curr->flink) { @@ -384,7 +390,7 @@ FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid) } else { - tcb->children = curr->flink; + group->tg_children = curr->flink; } curr->flink = NULL; @@ -414,18 +420,19 @@ FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid) void task_removechildren(FAR _TCB *tcb) { + FAR struct task_group_s *group = tcb->group; FAR struct child_status_s *curr; FAR struct child_status_s *next; /* Remove all child structures for the TCB and return them to the freelist */ - for (curr = tcb->children; curr; curr = next) + for (curr = group->tg_children; curr; curr = next) { next = curr->flink; task_freechild(curr); } - tcb->children = NULL; + group->tg_children = NULL; task_dumpchildren(tcb, "task_removechildren"); } diff --git a/nuttx/sched/task_create.c b/nuttx/sched/task_create.c index 2ed929ab0..f2aeeeec0 100644 --- a/nuttx/sched/task_create.c +++ b/nuttx/sched/task_create.c @@ -108,6 +108,7 @@ static int thread_create(const char *name, uint8_t ttype, int priority, { FAR _TCB *tcb; pid_t pid; + int errcode; int ret; /* Allocate a TCB for the new task. */ @@ -115,15 +116,28 @@ static int thread_create(const char *name, uint8_t ttype, int priority, tcb = (FAR _TCB*)kzalloc(sizeof(_TCB)); if (!tcb) { + errcode = ENOMEM; goto errout; } + /* Create a new task group */ + +#ifdef HAVE_TASK_GROUP + ret = group_create(tcb); + if (ret < 0) + { + errcode = -ret; + goto errout_with_tcb; + } +#endif + /* Associate file descriptors with the new task */ #if CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0 ret = sched_setuptaskfiles(tcb); if (ret != OK) { + errcode = -ret; goto errout_with_tcb; } #endif @@ -138,6 +152,7 @@ static int thread_create(const char *name, uint8_t ttype, int priority, ret = up_create_stack(tcb, stack_size); if (ret != OK) { + errcode = -ret; goto errout_with_tcb; } #endif @@ -147,6 +162,7 @@ static int thread_create(const char *name, uint8_t ttype, int priority, ret = task_schedsetup(tcb, priority, task_start, entry, ttype); if (ret != OK) { + errcode = -ret; goto errout_with_tcb; } @@ -163,6 +179,8 @@ static int thread_create(const char *name, uint8_t ttype, int priority, ret = task_activate(tcb); if (ret != OK) { + errcode = get_errno(); + /* The TCB was added to the active task list by task_schedsetup() */ dq_rem((FAR dq_entry_t*)tcb, (dq_queue_t*)&g_inactivetasks); @@ -175,7 +193,7 @@ errout_with_tcb: sched_releasetcb(tcb); errout: - errno = ENOMEM; + set_errno(errcode); return ERROR; } diff --git a/nuttx/sched/task_exithook.c b/nuttx/sched/task_exithook.c index 1813c12ed..30ce41f71 100644 --- a/nuttx/sched/task_exithook.c +++ b/nuttx/sched/task_exithook.c @@ -197,39 +197,28 @@ static inline void task_onexit(FAR _TCB *tcb, int status) ****************************************************************************/ #ifdef CONFIG_SCHED_HAVE_PARENT -static inline void task_sigchild(FAR _TCB *tcb, int status) +static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) { - FAR _TCB *ptcb; siginfo_t info; - /* Only exiting tasks should generate SIGCHLD. pthreads use other - * mechansims. + /* Only the final exiting thread in a task group should generate SIGCHLD. + * If task groups are not supported then we will report SIGCHLD when the + * task exits. */ - if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK) +#ifdef CONFIG_SCHED_CHILD_STATUS + if (ctcb->group->tg_crefs == 1) +#else + if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK) +#endif { - /* Keep things stationary through the following */ - - sched_lock(); - - /* Get the TCB of the receiving task */ - - ptcb = sched_gettcb(tcb->parent); - if (!ptcb) - { - /* The parent no longer exists... bail */ - - sched_unlock(); - return; - } - #ifdef CONFIG_SCHED_CHILD_STATUS - /* Check if the parent task has suppressed retention of child exit + /* Check if the parent task group has suppressed retention of child exit * status information. Only 'tasks' report exit status, not pthreads. * pthreads have a different mechanism. */ - if ((ptcb->flags & TCB_FLAG_NOCLDWAIT) == 0) + if ((ptcb->group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) { FAR struct child_status_s *child; @@ -255,15 +244,6 @@ static inline void task_sigchild(FAR _TCB *tcb, int status) ptcb->nchildren--; #endif - /* Set the parent to an impossible PID. We do this because under - * certain conditions, task_exithook() can be called multiple times. - * If this function is called again, sched_gettcb() will fail on the - * invalid parent PID above, nchildren will be decremented once and - * all will be well. - */ - - tcb->parent = INVALID_PROCESS_ID; - /* Create the siginfo structure. We don't actually know the cause. * That is a bug. Let's just say that the child task just exit-ted * for now. @@ -272,7 +252,7 @@ static inline void task_sigchild(FAR _TCB *tcb, int status) info.si_signo = SIGCHLD; info.si_code = CLD_EXITED; info.si_value.sival_ptr = NULL; - info.si_pid = tcb->pid; + info.si_pid = ctcb->pid; info.si_status = status; /* Send the signal. We need to use this internal interface so that we @@ -280,11 +260,59 @@ static inline void task_sigchild(FAR _TCB *tcb, int status) */ (void)sig_received(ptcb, &info); + } +} +#else +# define task_sigchild(ptct,ctcb,status) +#endif + +/**************************************************************************** + * Name: task_leavegroup + * + * Description: + * Send the SIGCHILD signal to the parent thread + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_HAVE_PARENT +static inline void task_leavegroup(FAR _TCB *ctcb, int status) +{ + FAR _TCB *ptcb; + + /* Keep things stationary throughout the following */ + + sched_lock(); + + /* Get the TCB of the receiving, parent task. We do this early to + * handle multiple calls to task_leavegroup. ctcb->parent is set to an + * invalid value below and the following call will fail if we are + * called again. + */ + + ptcb = sched_gettcb(ctcb->parent); + if (!ptcb) + { + /* The parent no longer exists... bail */ + sched_unlock(); + return; } + + /* Send SIGCHLD to all members of the parent's task group */ + + task_sigchild(ptcb, ctcb, status); + + /* Set the parent to an impossible PID. We do this because under certain + * conditions, task_exithook() can be called multiple times. If this + * function is called again, sched_gettcb() will fail on the invalid + * parent PID above and all will be well. + */ + + ctcb->parent = INVALID_PROCESS_ID; + sched_unlock(); } #else -# define task_sigchild(tcb,status) +# define task_leavegroup(ctcb,status) #endif /**************************************************************************** @@ -363,9 +391,9 @@ void task_exithook(FAR _TCB *tcb, int status) task_onexit(tcb, status); - /* Send SIGCHLD to the parent of the exit-ing task */ + /* Leave the task group */ - task_sigchild(tcb, status); + task_leavegroup(tcb, status); /* Wakeup any tasks waiting for this task to exit */ @@ -379,10 +407,12 @@ void task_exithook(FAR _TCB *tcb, int status) (void)lib_flushall(tcb->streams); #endif - /* Discard any un-reaped child status (no zombies here!) */ + /* Leave the task group. Perhaps discarding any un-reaped child + * status (no zombies here!) + */ -#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) - task_removechildren(tcb); +#ifdef HAVE_TASK_GROUP + group_leave(tcb); #endif /* Free all file-related resources now. This gets called again diff --git a/nuttx/sched/task_init.c b/nuttx/sched/task_init.c index 0f0fdc68e..5ba6b7e7d 100644 --- a/nuttx/sched/task_init.c +++ b/nuttx/sched/task_init.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_init.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,8 @@ #include #include #include +#include + #include #include "os_internal.h" @@ -102,10 +104,10 @@ * parameters are required, argv may be NULL. * * Return Value: - * OK on success; ERROR on failure. (See task_schedsetup() for possible - * failure conditions). On failure, the caller is responsible for freeing - * the stack memory and for calling sched_releasetcb() to free the TCB - * (which could be in most any state). + * OK on success; ERROR on failure with errno set appropriately. (See + * task_schedsetup() for possible failure conditions). On failure, the + * caller is responsible for freeing the stack memory and for calling + * sched_releasetcb() to free the TCB (which could be in most any state). * ****************************************************************************/ @@ -118,14 +120,28 @@ int task_init(FAR _TCB *tcb, const char *name, int priority, main_t entry, const char *argv[]) #endif { + int errcode; int ret; + /* Create a new task group */ + +#ifdef HAVE_TASK_GROUP + ret = group_create(tcb); + if (ret < 0) + { + errcode = -ret; + goto errout; + } +#endif + /* Associate file descriptors with the new task */ #if CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0 - if (sched_setuptaskfiles(tcb) != OK) + ret = sched_setuptaskfiles(tcb); + if (ret < 0) { - return ERROR; + errcode = -ret; + goto errout_with_group; } #endif @@ -143,13 +159,27 @@ int task_init(FAR _TCB *tcb, const char *name, int priority, ret = task_schedsetup(tcb, priority, task_start, entry, TCB_FLAG_TTYPE_TASK); - if (ret == OK) + if (ret < OK) { - /* Setup to pass parameters to the new task */ - - (void)task_argsetup(tcb, name, argv); + errcode = -ret; + goto errout_with_env; } - return ret; + /* Setup to pass parameters to the new task */ + + (void)task_argsetup(tcb, name, argv); + return OK; + +errout_with_env: + env_release(tcb); + +errout_with_group: +#ifdef HAVE_TASK_GROUP + group_leave(tcb); + +errout: +#endif + set_errno(errcode); + return ERROR; } diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c index 3a7ece37d..0b502dcce 100644 --- a/nuttx/sched/task_reparent.c +++ b/nuttx/sched/task_reparent.c @@ -138,11 +138,11 @@ int task_reparent(pid_t ppid, pid_t chpid) child = task_removechild(otcb, chpid); if (child) { - /* Has the new parent supressed child exit status? */ + /* Has the new parent's task group supressed child exit status? */ - if ((ptcb->flags && TCB_FLAG_NOCLDWAIT) == 0) + if ((ptcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) { - /* No.. Add the child status entry to the new parent TCB */ + /* No.. Add the child status entry to the new parent's task group */ task_addchild(ptcb, child); } @@ -159,11 +159,11 @@ int task_reparent(pid_t ppid, pid_t chpid) } else { - /* This would not be an error if the original parent has + /* This would not be an error if the original parent's task group has * suppressed child exit status. */ - ret = ((otcb->flags && TCB_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; + ret = ((otcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; } #else DEBUGASSERT(otcb->nchildren > 0); diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c index 66c948cfd..7675c7481 100644 --- a/nuttx/sched/task_setup.c +++ b/nuttx/sched/task_setup.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_setup.c * - * Copyright (C) 2007-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -179,13 +179,11 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) tcb->parent = rtcb->pid; #ifdef CONFIG_SCHED_CHILD_STATUS - /* Exit status only needs to be retained for the case of tasks, however. - * Tasks can also suppress retention of their child status by applying - * the SA_NOCLDWAIT flag with sigaction()/ + /* Tasks can also suppress retention of their child status by applying + * the SA_NOCLDWAIT flag with sigaction(). */ - if (ttype == TCB_FLAG_TTYPE_TASK && - (rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0) + if ((rtcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) { FAR struct child_status_s *child; -- cgit v1.2.3