From f2933cc4b65836a712f1ab996512999959a8c48a 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: svn://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 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