From 03a486c40db2af681a40776e54d7f936c7e9d499 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 26 Jan 2013 17:28:20 +0000 Subject: Don't keep the parent task's task ID in the child task's TCB. Instead, keep the parent task group IN the child task's task group. git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5566 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/ChangeLog | 4 +- nuttx/include/nuttx/sched.h | 12 +- nuttx/sched/Makefile | 16 +- nuttx/sched/group_childstatus.c | 441 ++++++++++++++++++++++++++++++++++++++++ nuttx/sched/group_create.c | 99 ++++++++- nuttx/sched/group_find.c | 125 ++++++++++++ nuttx/sched/group_internal.h | 71 +++++-- nuttx/sched/group_join.c | 48 ++++- nuttx/sched/group_leave.c | 212 ++++++++++++++----- nuttx/sched/group_signal.c | 40 +++- nuttx/sched/sched_waitid.c | 22 +- nuttx/sched/sched_waitpid.c | 40 ++-- nuttx/sched/sig_action.c | 2 +- nuttx/sched/task_childstatus.c | 440 --------------------------------------- nuttx/sched/task_exithook.c | 175 ++++++++++++---- nuttx/sched/task_reparent.c | 156 +++++++++++++- nuttx/sched/task_setup.c | 32 ++- 17 files changed, 1322 insertions(+), 613 deletions(-) create mode 100644 nuttx/sched/group_childstatus.c create mode 100644 nuttx/sched/group_find.c delete mode 100644 nuttx/sched/task_childstatus.c diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index f23a3ed73..858f82819 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -4031,4 +4031,6 @@ members for the parent task group. * include/nuttx/sched.h and sched/env_*.c: Move environment variables into task group structure. - + * sched/: Lots of file changed. Don't keep the parent task's + task ID in the child task's TCB. Instead, keep the parent + task group IN the child task's task group. diff --git a/nuttx/include/nuttx/sched.h b/nuttx/include/nuttx/sched.h index dd33b4570..8ebb7db4c 100644 --- a/nuttx/include/nuttx/sched.h +++ b/nuttx/include/nuttx/sched.h @@ -95,6 +95,7 @@ #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_EXIT_PROCESSING (1 << 5) /* Bit 5: Exitting */ /* Values for struct task_group tg_flags */ @@ -253,6 +254,11 @@ struct dspace_s #ifdef HAVE_TASK_GROUP struct task_group_s { +#ifdef HAVE_GROUP_MEMBERS + struct task_group_s *flink; /* Supports a singly linked list */ + gid_t tg_gid; /* The ID of this task group */ + gid_t tg_pgid; /* The ID of the parent task group */ +#endif uint8_t tg_flags; /* See GROUP_FLAG_* definitions */ /* Group membership ***********************************************************/ @@ -311,12 +317,16 @@ struct _TCB /* 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 */ +#ifndef HAVE_GROUP_MEMBERS /* Don't know pids of group members */ + pid_t ppid; /* This is the ID of the parent thread */ #ifndef CONFIG_SCHED_CHILD_STATUS /* Retain child thread status */ uint16_t nchildren; /* This is the number active children */ #endif #endif +#endif /* CONFIG_SCHED_HAVE_PARENT */ + start_t start; /* Thread start function */ entry_t entry; /* Entry Point into the thread */ diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile index 6a0b99144..d059152e0 100644 --- a/nuttx/sched/Makefile +++ b/nuttx/sched/Makefile @@ -73,13 +73,6 @@ ifeq ($(CONFIG_PRIORITY_INHERITANCE),y) SCHED_SRCS += sched_reprioritize.c endif -ifeq ($(CONFIG_SCHED_HAVE_PARENT),y) -SCHED_SRCS += task_reparent.c -ifeq ($(CONFIG_SCHED_CHILD_STATUS),y) -SCHED_SRCS += task_childstatus.c -endif -endif - ifeq ($(CONFIG_SCHED_WAITPID),y) SCHED_SRCS += sched_waitpid.c ifeq ($(CONFIG_SCHED_HAVE_PARENT),y) @@ -87,7 +80,14 @@ SCHED_SRCS += sched_waitid.c sched_wait.c endif endif -GRP_SRCS = group_create.c group_join.c group_leave.c +GRP_SRCS = group_create.c group_join.c group_leave.c group_find.c + +ifeq ($(CONFIG_SCHED_HAVE_PARENT),y) +GRP_SRCS += task_reparent.c +ifeq ($(CONFIG_SCHED_CHILD_STATUS),y) +GRP_SRCS += group_childstatus.c +endif +endif ifneq ($(CONFIG_DISABLE_SIGNALS),y) GRP_SRCS += group_signal.c diff --git a/nuttx/sched/group_childstatus.c b/nuttx/sched/group_childstatus.c new file mode 100644 index 000000000..ef42b6c34 --- /dev/null +++ b/nuttx/sched/group_childstatus.c @@ -0,0 +1,441 @@ +/***************************************************************************** + * sched/group_childstatus.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" +#include "group_internal.h" + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ +/* Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + * However, the number of child status structures may need to be significantly + * larger because this number includes the maximum number of tasks that are + * running PLUS the number of tasks that have exit'ed without having their + * exit status reaped (via wait(), waitid(), or waitpid()). + * + * Obviously, if tasks spawn children indefinitely and never have the exit + * status reaped, then you have a memory leak! + */ + +#if !defined(CONFIG_PREALLOC_CHILDSTATUS) || CONFIG_PREALLOC_CHILDSTATUS == 0 +# undef CONFIG_PREALLOC_CHILDSTATUS +# define CONFIG_PREALLOC_CHILDSTATUS (2*CONFIG_MAX_TASKS) +#endif + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_CHILDSTATUS +#endif + +/***************************************************************************** + * Private Types + *****************************************************************************/ +/* Globals are maintained in a structure to minimize name collisions. */ + +struct child_pool_s +{ + struct child_status_s alloc[CONFIG_PREALLOC_CHILDSTATUS]; + FAR struct child_status_s *freelist; +}; + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +static struct child_pool_s g_child_pool; + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: group_dumpchildren + * + * Description: + * Dump all of the children when the part TCB list is modified. + * + * Parameters: + * group - The task group containing the child status. + * + * Return Value: + * None. + * + * Assumptions: + * Called early in initialization. No special precautions are required. + * + *****************************************************************************/ + +#ifdef CONFIG_DEBUG_CHILDSTATUS +static void group_dumpchildren(FAR struct task_group_s *group, + FAR const char *msg) +{ + FAR struct child_status_s *child; + int i; + + dbg("Task group=%p: %s\n", 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); + } +} +#else +# define group_dumpchildren(t,m) +#endif + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: task_initialize + * + * Description: + * Initialize task related status. At present, this includes only the + * initialize of the child status pool. + * + * Parameters: + * None. + * + * Return Value: + * None. + * + * Assumptions: + * Called early in initialization. No special precautions are required. + * + *****************************************************************************/ + +void task_initialize(void) +{ + FAR struct child_status_s *curr; + FAR struct child_status_s *prev; + int i; + + /* Save all of the child status structures in a free list */ + + prev = &g_child_pool.alloc[0]; + g_child_pool.freelist = prev; + for (i = 0; i < CONFIG_PREALLOC_CHILDSTATUS; i++) + { + curr = &g_child_pool.alloc[i]; + prev->flink = curr; + prev = curr; + } +} + +/***************************************************************************** + * Name: group_allocchild + * + * Description: + * Allocate a child status structure by removing the next entry from a + * free list. + * + * Parameters: + * None. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there are no remaining, pre-allocated child status structures. + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *group_allocchild(void) +{ + FAR struct child_status_s *ret; + + /* Return the status block at the head of the free list */ + + ret = g_child_pool.freelist; + if (ret) + { + g_child_pool.freelist = ret->flink; + ret->flink = NULL; + } + + return ret; +} + +/***************************************************************************** + * Name: group_freechild + * + * Description: + * Release a child status structure by returning it to a free list. + * + * Parameters: + * status - The child status structure to be freed. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +void group_freechild(FAR struct child_status_s *child) +{ + /* Return the child status structure to the free list */ + + if (child) + { + child->flink = g_child_pool.freelist; + g_child_pool.freelist = child; + } +} + +/***************************************************************************** + * Name: group_addchild + * + * Description: + * Add a child status structure in the given TCB. + * + * Parameters: + * group - The task group for the child status. + * child - The structure to be added + * + * Return Value: + * N + * + * Assumptions: + * Called during task creation processing in a safe context. No special + * precautions are required here. + * + *****************************************************************************/ + +void group_addchild(FAR struct task_group_s *group, + FAR struct child_status_s *child) +{ + /* Add the entry into the TCB list of children */ + + child->flink = group->tg_children; + group->tg_children = child; + + group_dumpchildren(group, "group_addchild"); +} + +/***************************************************************************** + * Name: group_findchild + * + * Description: + * Find a child status structure in the given task group. A reference to + * the child structure is returned, but the child remains the the group's + * list of children. + * + * Parameters: + * group - The ID of the parent task group to containing the child status. + * pid - The ID of the child to find. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there is child status structure for that pid in the TCB. + * + * Assumptions: + * Called during SIGCHLD processing in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *group_findchild(FAR struct task_group_s *group, + pid_t pid) +{ + FAR struct child_status_s *child; + + DEBUGASSERT(group); + + /* Find the status structure with the matching PID */ + + for (child = group->tg_children; child; child = child->flink) + { + if (child->ch_pid == pid) + { + return child; + } + } + + return NULL; +} + +/***************************************************************************** + * Name: group_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 *group_exitchild(FAR struct task_group_s *group) +{ + FAR struct child_status_s *child; + + /* Find the status structure of any child task that has exitted. */ + + for (child = group->tg_children; child; child = child->flink) + { + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + return child; + } + } + + return NULL; +} + +/***************************************************************************** + * Name: group_removechild + * + * Description: + * Remove one child structure from a task group. The child is removed, but + * is not yet freed. group_freechild must be called in order to free the + * child status structure. + * + * Parameters: + * group - The task group containing the child status. + * pid - The ID of the child to find. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there is child status structure for that pid in the TCB. + * + * Assumptions: + * Called during SIGCHLD processing in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *group_removechild(FAR struct task_group_s *group, + pid_t pid) +{ + FAR struct child_status_s *curr; + FAR struct child_status_s *prev; + + DEBUGASSERT(group); + + /* Find the status structure with the matching PID */ + + for (prev = NULL, curr = group->tg_children; + curr; + prev = curr, curr = curr->flink) + { + if (curr->ch_pid == pid) + { + break; + } + } + + /* Did we find it? If so, remove it from the group. */ + + if (curr) + { + /* Do we remove it from mid-list? Or from the head of the list? */ + + if (prev) + { + prev->flink = curr->flink; + } + else + { + group->tg_children = curr->flink; + } + + curr->flink = NULL; + group_dumpchildren(group, "group_removechild"); + } + + return curr; +} + +/***************************************************************************** + * Name: group_removechildren + * + * Description: + * Remove and free all child structure from the task group. + * + * Parameters: + * group - The task group containing the child status. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task exit processing in a safe context. No special + * precautions are required here. + * + *****************************************************************************/ + +void group_removechildren(FAR struct task_group_s *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 = group->tg_children; curr; curr = next) + { + next = curr->flink; + group_freechild(curr); + } + + group->tg_children = NULL; + group_dumpchildren(group, "group_removechildren"); +} + +#endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_SCHED_CHILD_STATUS */ diff --git a/nuttx/sched/group_create.c b/nuttx/sched/group_create.c index da91fa065..768641be1 100644 --- a/nuttx/sched/group_create.c +++ b/nuttx/sched/group_create.c @@ -1,5 +1,5 @@ /***************************************************************************** - * sched/group_allocate.c + * sched/group_create.c * * Copyright (C) 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -65,11 +65,87 @@ /***************************************************************************** * Private Data *****************************************************************************/ +/* This is counter that is used to generate unique task group IDs */ + +#ifdef HAVE_GROUP_MEMBERS +static gid_t g_gidcounter; +#endif + +/***************************************************************************** + * Public Data + *****************************************************************************/ +/* This is the head of a list of all group members */ + +#ifdef HAVE_GROUP_MEMBERS +FAR struct task_group_s *g_grouphead; +#endif /***************************************************************************** * Private Functions *****************************************************************************/ +/***************************************************************************** + * Name: group_assigngid + * + * Description: + * Create a unique group ID. + * + * Parameters: + * tcb - The tcb in need of the task group. + * + * Return Value: + * None + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +#ifdef HAVE_GROUP_MEMBERS +void group_assigngid(FAR struct task_group_s *group) +{ + irqstate_t flags; + gid_t gid; + + /* Pre-emption should already be enabled, but lets be paranoid careful */ + + sched_lock(); + + /* Loop until we create a unique ID */ + + for (;;) + { + /* Increment the ID counter. This is global data so be extra paraoid. */ + + flags = irqsave(); + gid = ++g_gidcounter; + + /* Check for overflow */ + + if (gid <= 0) + { + g_gidcounter = 1; + irqrestore(flags); + } + else + { + /* Does a task group with this ID already exist? */ + + irqrestore(flags); + if (group_find(gid) == NULL) + { + /* Now assign this ID to the group and return */ + + group->tg_gid = gid; + sched_unlock(); + return; + } + } + } +} +#endif /* HAVE_GROUP_MEMBERS */ + /***************************************************************************** * Public Functions *****************************************************************************/ @@ -112,6 +188,14 @@ int group_allocate(FAR _TCB *tcb) return -ENOMEM; } + /* Assign the group a unique ID. If g_gidcounter were to wrap before we + * finish with task creation, that would be a problem. + */ + +#ifdef HAVE_GROUP_MEMBERS + group_assigngid(tcb->group); +#endif + /* Duplicate the parent tasks envionment */ ret = env_dup(tcb); @@ -149,6 +233,9 @@ int group_allocate(FAR _TCB *tcb) int group_initialize(FAR _TCB *tcb) { FAR struct task_group_s *group; +#ifdef HAVE_GROUP_MEMBERS + irqstate_t flags; +#endif DEBUGASSERT(tcb && tcb->group); group = tcb->group; @@ -172,9 +259,17 @@ int group_initialize(FAR _TCB *tcb) */ group->tg_mxmembers = GROUP_INITIAL_MEMBERS; /* Number of members in allocation */ + + /* Add the initialized entry to the list of groups */ + + flags = irqsave(); + group->flink = g_grouphead; + g_grouphead = group; + irqrestore(flags); + #endif - group->tg_nmembers = 1; /* Number of members in the group */ + group->tg_nmembers = 1; /* Number of members in the group */ return OK; } diff --git a/nuttx/sched/group_find.c b/nuttx/sched/group_find.c new file mode 100644 index 000000000..eb3989223 --- /dev/null +++ b/nuttx/sched/group_find.c @@ -0,0 +1,125 @@ +/***************************************************************************** + * sched/group_find.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 + +#include "group_internal.h" +#include "env_internal.h" + +#ifdef HAVE_TASK_GROUP + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +/***************************************************************************** + * Public Data + *****************************************************************************/ + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: group_find + * + * Description: + * Given a group ID, find the group task structure with that ID. IDs are + * used instead of pointers to group structures. This is done because a + * group can disappear at any time leaving a stale pointer; an ID is cleaner + * because if the group disappears, this function will fail gracefully. + * + * Parameters: + * gid - The group ID to find. + * + * Return Value: + * On success, a pointer to the group task structure is returned. This + * function can fail only if there is no group that corresponds to the + * groupd ID. + * + * Assumptions: + * Called during when signally tasks in a safe context. No special + * precautions should be required here. However, extra care is taken when + * accessing the global g_grouphead list. + * + *****************************************************************************/ + +#ifdef HAVE_GROUP_MEMBERS +FAR struct task_group_s *group_find(gid_t gid) +{ + FAR struct task_group_s *group; + irqstate_t flags; + + /* Find the status structure with the matching PID */ + + flags = irqsave(); + for (group = g_grouphead; group; group = group->flink) + { + if (group->tg_gid == gid) + { + irqrestore(flags); + return group; + } + } + + irqrestore(flags); + return NULL; +} +#endif + +#endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/group_internal.h b/nuttx/sched/group_internal.h index aa8476e3c..b78b38453 100644 --- a/nuttx/sched/group_internal.h +++ b/nuttx/sched/group_internal.h @@ -52,37 +52,62 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* Any negative GID is invalid. */ + +#define INVALID_GROUP_ID (pid_t)-1 +#define IS_INVALID_GID(gid) ((int)(gid) < 0) /**************************************************************************** * Public Type Definitions ****************************************************************************/ /**************************************************************************** - * Global Variables + * Public Data ****************************************************************************/ +/* This is the head of a list of all group members */ + +#ifdef HAVE_GROUP_MEMBERS +extern FAR struct task_group_s *g_grouphead; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ /* Task group data structure management */ #ifdef HAVE_TASK_GROUP -int group_allocate(FAR _TCB *tcb); -int group_initialize(FAR _TCB *tcb); -int group_bind(FAR _TCB *tcb); -int group_join(FAR _TCB *tcb); +int group_allocate(FAR _TCB *tcb); +int group_initialize(FAR _TCB *tcb); +int group_bind(FAR _TCB *tcb); +int group_join(FAR _TCB *tcb); void group_leave(FAR _TCB *tcb); + +#ifdef HAVE_GROUP_MEMBERS +FAR struct task_group_s *group_find(gid_t gid); +int group_addmember(FAR struct task_group_s *group, pid_t pid); +int group_removemember(FAR struct task_group_s *group, pid_t pid); +#else +# define group_find(gid) (NULL) +# define group_addmember(group,pid) (0) +# define group_removemember(group,pid) (1) +#endif + #ifndef CONFIG_DISABLE_SIGNALS -int group_signal(FAR _TCB *tcb, FAR siginfo_t *info); +int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info); #else -# define group_signal(tcb,info) (0) +# define group_signal(tcb,info) (0) #endif + #else -# define group_allocate(tcb) (0) -# define group_initialize(tcb) (0) -# define group_bind(tcb) (0) -# define group_join(tcb) (0) -# define group_leave(tcb) -# define group_signal(tcb,info) (0) +# define group_allocate(tcb) (0) +# define group_initialize(tcb) (0) +# define group_bind(tcb) (0) +# define group_join(tcb) (0) +# define group_leave(tcb) +# define group_find(gid) (NULL) +# define group_addmember(group,pid) (0) +# define group_removemember(group,pid) (1) +# define group_signal(tcb,info) (0) #endif /* HAVE_TASK_GROUP */ /* Parent/child data management */ @@ -91,14 +116,18 @@ int group_signal(FAR _TCB *tcb, FAR siginfo_t *info); int task_reparent(pid_t ppid, pid_t chpid); #ifdef CONFIG_SCHED_CHILD_STATUS -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); -#endif +FAR struct child_status_s *group_allocchild(void); +void group_freechild(FAR struct child_status_s *status); +void group_addchild(FAR struct task_group_s *group, + FAR struct child_status_s *child); +FAR struct child_status_s *group_exitchild(FAR struct task_group_s *group); +FAR struct child_status_s *group_findchild(FAR struct task_group_s *group, + pid_t pid); +FAR struct child_status_s *group_removechild(FAR struct task_group_s *group, + pid_t pid); +void group_removechildren(FAR struct task_group_s *group); + +#endif /* CONFIG_SCHED_CHILD_STATUS */ #endif /* CONFIG_SCHED_HAVE_PARENT */ #endif /* __SCHED_GROUP_INERNAL_H */ diff --git a/nuttx/sched/group_join.c b/nuttx/sched/group_join.c index 077c45763..70319b3a1 100644 --- a/nuttx/sched/group_join.c +++ b/nuttx/sched/group_join.c @@ -139,6 +139,9 @@ int group_bind(FAR _TCB *tcb) int group_join(FAR _TCB *tcb) { FAR struct task_group_s *group; +#ifdef HAVE_GROUP_MEMBERS + int ret; +#endif DEBUGASSERT(tcb && tcb->group && tcb->group->tg_nmembers < UINT8_MAX); @@ -146,8 +149,45 @@ int group_join(FAR _TCB *tcb) /* Get the group from the TCB */ group = tcb->group; - + #ifdef HAVE_GROUP_MEMBERS + /* Add the member to the group */ + + ret = group_addmember(group, tcb->pid); + if (ret < 0) + { + return ret; + } +#endif + + group->tg_nmembers++; + return OK; +} + +/***************************************************************************** + * Name: group_addmember + * + * Description: + * Add a new member to a group. + * + * Parameters: + * group - The task group to add the new member + * pid - The new member + * + * Return Value: + * 0 (OK) on success; a negated errno value on failure. + * + * Assumptions: + * Called during thread creation and during reparenting in a safe context. + * No special precautions are required here. + * + *****************************************************************************/ + +#ifdef HAVE_GROUP_MEMBERS +int group_addmember(FAR struct task_group_s *group, pid_t pid) +{ + DEBUGASSERT(group && group->tg_nmembers < UINT8_MAX); + /* Will we need to extend the size of the array of groups? */ if (group->tg_nmembers >= group->tg_mxmembers) @@ -179,11 +219,9 @@ int group_join(FAR _TCB *tcb) /* Assign this new pid to the group. */ - group->tg_members[group->tg_nmembers] = tcb->pid; -#endif - - group->tg_nmembers++; + group->tg_members[group->tg_nmembers] = pid; return OK; } +#endif /* HAVE_GROUP_MEMBERS */ #endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/group_leave.c b/nuttx/sched/group_leave.c index d5fa9dbaf..add424185 100644 --- a/nuttx/sched/group_leave.c +++ b/nuttx/sched/group_leave.c @@ -65,6 +65,65 @@ * Private Functions *****************************************************************************/ +/***************************************************************************** + * Name: group_remove + * + * Description: + * Remove a group from the list of groups. + * + * Parameters: + * group - The group to be removed. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task deletion in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +#ifdef HAVE_GROUP_MEMBERS +void group_remove(FAR struct task_group_s *group) +{ + FAR struct task_group_s *curr; + FAR struct task_group_s *prev; + irqstate_t flags; + + /* Let's be especially careful while access the global task group list. + * This is probably un-necessary. + */ + + flags = irqsave(); + + /* Find the task group structure */ + + for (prev = NULL, curr = g_grouphead; + curr && curr != group; + prev = curr, curr = curr->flink); + + /* Did we find it? If so, remove it from the list. */ + + if (curr) + { + /* Do we remove it from mid-list? Or from the head of the list? */ + + if (prev) + { + prev->flink = curr->flink; + } + else + { + g_grouphead = curr->flink; + } + + curr->flink = NULL; + } + + irqrestore(flags); +} +#endif + /***************************************************************************** * Public Functions *****************************************************************************/ @@ -90,6 +149,8 @@ * *****************************************************************************/ +#ifdef HAVE_GROUP_MEMBERS + void group_leave(FAR _TCB *tcb) { FAR struct task_group_s *group; @@ -101,64 +162,57 @@ void group_leave(FAR _TCB *tcb) group = tcb->group; if (group) { -#ifdef HAVE_GROUP_MEMBERS - int i; + /* Remove the member from group */ - /* Find the member in the array of members and remove it */ + int ret = group_removemember(group, tcb->pid); + DEBUGASSERT(ret >= 0); - for (i = 0; i < group->tg_nmembers; i++) - { - /* Does this member have the matching pid */ - - if (group->tg_members[i] == tcb->pid) - { - /* Yes.. break out of the loop. We don't do the actual - * removal here, instead we re-test i and do the adjustments - * outside of the loop. We do this because we want the - * DEBUGASSERT to work properly. - */ - - break; - } - } - - /* Now, test if we found the task in the array of members. */ + /* Is the group now empty? */ - DEBUGASSERT(i < group->tg_nmembers); - if (i < group->tg_nmembers) + if (ret == 0) { - /* Yes..Is this the last member of the group? */ - - if (group->tg_nmembers > 1) - { - /* No.. remove the member from the array of members */ - - group->tg_members[i] = group->tg_members[group->tg_nmembers - 1]; - group->tg_nmembers--; - } - - /* Yes.. that was the last member remaining in the group */ - - else - { - /* Release all of the resource contained within the group */ - /* Free all un-reaped child exit status */ + /* Release all of the resource contained within the group */ + /* Free all un-reaped child exit status */ #if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) - task_removechildren(tcb); + group_removechildren(tcb->group); #endif - /* Release all shared environment variables */ + /* Release all shared environment variables */ #ifndef CONFIG_DISABLE_ENVIRON - env_release(tcb); + env_release(tcb); #endif - /* Release the group container itself */ + /* Remove the group from the list of groups */ + + group_remove(group); + + /* Release the group container itself */ - sched_free(group); - } + sched_free(group); } -#else - /* Yes..Is this the last member of the group? */ + + /* In any event, we can detach the group from the TCB so that we won't + * do this again. + */ + + tcb->group = NULL; + } +} + +#else /* HAVE_GROUP_MEMBERS */ + +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) + { + /* Yes, we have a group.. Is this the last member of the group? */ if (group->tg_nmembers > 1) { @@ -175,7 +229,7 @@ void group_leave(FAR _TCB *tcb) /* Free all un-reaped child exit status */ #if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) - task_removechildren(tcb); + group_removechildren(tcb->group); #endif /* Release all shared environment variables */ @@ -186,7 +240,6 @@ void group_leave(FAR _TCB *tcb) sched_free(group); } -#endif /* In any event, we can detach the group from the TCB so we won't do * this again. @@ -196,4 +249,67 @@ void group_leave(FAR _TCB *tcb) } } +#endif /* HAVE_GROUP_MEMBERS */ + +/***************************************************************************** + * Name: group_removemember + * + * Description: + * Remove a member from a group. + * + * Parameters: + * group - The group from which to remove the member. + * pid - The member to be removed. + * + * Return Value: + * On success, returns the number of members remaining in the group (>=0). + * Can fail only if the member is not found in the group. On failure, + * returns -ENOENT + * + * Assumptions: + * Called during task deletion and also from the reparenting logic, both + * in a safe context. No special precautions are required here. + * + *****************************************************************************/ + +#ifdef HAVE_GROUP_MEMBERS +int group_removemember(FAR struct task_group_s *group, pid_t pid) +{ + int i; + + DEBUGASSERT(group); + + /* Find the member in the array of members and remove it */ + + for (i = 0; i < group->tg_nmembers; i++) + { + /* Does this member have the matching pid */ + + if (group->tg_members[i] == pid) + { + /* Yes.. break out of the loop. We don't do the actual + * removal here, instead we re-test i and do the adjustments + * outside of the loop. We do this because we want the + * DEBUGASSERT to work properly. + */ + + break; + } + } + + /* Now, test if we found the task in the array of members. */ + + if (i < group->tg_nmembers) + { + /* Remove the member from the array of members */ + + group->tg_members[i] = group->tg_members[group->tg_nmembers - 1]; + group->tg_nmembers--; + return group->tg_nmembers; + } + + return -ENOENT; +} +#endif /* HAVE_GROUP_MEMBERS */ + #endif /* HAVE_TASK_GROUP */ diff --git a/nuttx/sched/group_signal.c b/nuttx/sched/group_signal.c index 5020ec436..009ab7a55 100644 --- a/nuttx/sched/group_signal.c +++ b/nuttx/sched/group_signal.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "os_internal.h" @@ -74,10 +75,10 @@ * Name: group_signal * * Description: - * Send a signal to every member of the group to which task belongs. + * Send a signal to every member of the group. * * Parameters: - * tcb - The tcb of one task in the task group that needs to be signalled. + * group - The task group that needs to be signalled. * * Return Value: * 0 (OK) on success; a negated errno value on failure. @@ -88,15 +89,13 @@ * *****************************************************************************/ -int group_signal(FAR _TCB *tcb, FAR siginfo_t *info) +int group_signal(FAR struct task_group_s *group, FAR siginfo_t *info) { #ifdef HAVE_GROUP_MEMBERS - FAR struct task_group_s *group; FAR _TCB *gtcb; int i; - DEBUGASSERT(tcb && tcb->group && info); - group = tcb->group; + DEBUGASSERT(group && info); /* Make sure that pre-emption is disabled to that we signal all of teh * members of the group before any of them actually run. @@ -130,8 +129,35 @@ int group_signal(FAR _TCB *tcb, FAR siginfo_t *info) sched_unlock(); return OK; #else - return sig_received(tcb, info); + return -ENOSYS; #endif } +/***************************************************************************** + * Name: group_signalmember + * + * Description: + * Send a signal to every member of the group to which task belongs. + * + * Parameters: + * tcb - The tcb of one task in the task group that needs to be signalled. + * + * Return Value: + * 0 (OK) on success; a negated errno value on failure. + * + * Assumptions: + * Called during task terminatino in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +int group_signalmember(FAR _TCB *tcb, FAR siginfo_t *info) +{ +#ifdef HAVE_GROUP_MEMBERS + DEBUGASSERT(tcb); + return group_signal(tcb->group, info); +#else + return sig_received(tcb, info); +#endif +} #endif /* HAVE_TASK_GROUP && !CONFIG_DISABLE_SIGNALS */ diff --git a/nuttx/sched/sched_waitid.c b/nuttx/sched/sched_waitid.c index 41e488f90..9c24189c4 100644 --- a/nuttx/sched/sched_waitid.c +++ b/nuttx/sched/sched_waitid.c @@ -79,8 +79,8 @@ static void exited_child(FAR _TCB *rtcb, FAR struct child_status_s *child, /* Discard the child entry */ - (void)task_removechild(rtcb, child->ch_pid); - task_freechild(child); + (void)group_removechild(rtcb->group, child->ch_pid); + group_freechild(child); } #endif @@ -212,7 +212,11 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) /* 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) +#ifdef HAVE_GROUP_MEMBERS + if (!ctcb || ctcb->group->tg_pgid != rtcb->group->tg_gid) +#else + if (!ctcb || ctcb->ppid != rtcb->pid) +#endif { err = ECHILD; goto errout_with_errno; @@ -224,7 +228,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) { /* Check if this specific pid has allocated child status? */ - if (task_findchild(rtcb, (pid_t)id) == NULL) + if (group_findchild(rtcb->group, (pid_t)id) == NULL) { /* This specific pid is not a child */ @@ -246,7 +250,11 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) /* 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) +#ifdef HAVE_GROUP_MEMBERS + if (!ctcb || ctcb->group->tg_pgid != rtcb->group->tg_gid) +#else + if (!ctcb || ctcb->ppid != rtcb->pid) +#endif { err = ECHILD; goto errout_with_errno; @@ -270,7 +278,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) { /* We are waiting for any child to exit */ - if (retains && (child = task_exitchild(rtcb)) != NULL) + if (retains && (child = group_exitchild(rtcb->group)) != NULL) { /* A child has exited. Apparently we missed the signal. * Return the exit status and break out of the loop. @@ -287,7 +295,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) { /* Yes ... Get the current status of the child task. */ - child = task_findchild(rtcb, (pid_t)id); + child = group_findchild(rtcb->group, (pid_t)id); DEBUGASSERT(child); /* Did the child exit? */ diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c index 4af7f7ef3..0285c2673 100644 --- a/nuttx/sched/sched_waitpid.c +++ b/nuttx/sched/sched_waitpid.c @@ -172,16 +172,12 @@ * * Assumptions: * - *****************************************************************************/ - -/*************************************************************************** - * NOTE: This is a partially functional, experimental version of waitpid() + * Compatibility + * If there is no SIGCHLD signal supported (CONFIG_SCHED_HAVE_PARENT not + * defined), then waitpid() is still available, but does not obey the + * restriction that the pid be a child of the caller. * - * If there is no SIGCHLD signal supported (CONFIG_SCHED_HAVE_PARENT not - * defined), then waitpid() is still available, but does not obey the - * restriction that the pid be a child of the caller. - * - ***************************************************************************/ + *****************************************************************************/ #ifndef CONFIG_SCHED_HAVE_PARENT pid_t waitpid(pid_t pid, int *stat_loc, int options) @@ -325,7 +321,11 @@ 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. */ ctcb = sched_gettcb(pid); - if (!ctcb || ctcb->parent != rtcb->pid) +#ifdef HAVE_GROUP_MEMBERS + if (!ctcb || ctcb->group->tg_pgid != rtcb->group->tg_gid) +#else + if (!ctcb || ctcb->ppid != rtcb->pid) +#endif { err = ECHILD; goto errout_with_errno; @@ -337,7 +337,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) { /* Check if this specific pid has allocated child status? */ - if (task_findchild(rtcb, pid) == NULL) + if (group_findchild(rtcb->group, pid) == NULL) { err = ECHILD; goto errout_with_errno; @@ -357,7 +357,11 @@ 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. */ ctcb = sched_gettcb(pid); - if (!ctcb || ctcb->parent != rtcb->pid) +#ifdef HAVE_GROUP_MEMBERS + if (!ctcb || ctcb->group->tg_pgid != rtcb->group->tg_gid) +#else + if (!ctcb || ctcb->ppid != rtcb->pid) +#endif { err = ECHILD; goto errout_with_errno; @@ -383,7 +387,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) */ DEBUGASSERT(!retains || rtcb->group->tg_children); - if (retains && (child = task_exitchild(rtcb)) != NULL) + if (retains && (child = group_exitchild(rtcb->group)) != NULL) { /* A child has exited. Apparently we missed the signal. * Return the saved exit status. @@ -395,8 +399,8 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) /* Discard the child entry and break out of the loop */ - (void)task_removechild(rtcb, child->ch_pid); - task_freechild(child); + (void)group_removechild(rtcb->group, child->ch_pid); + group_freechild(child); break; } } @@ -407,7 +411,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) { /* Get the current status of the child task. */ - child = task_findchild(rtcb, pid); + child = group_findchild(rtcb->group, pid); DEBUGASSERT(child); /* Did the child exit? */ @@ -420,8 +424,8 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) /* Discard the child entry and break out of the loop */ - (void)task_removechild(rtcb, pid); - task_freechild(child); + (void)group_removechild(rtcb->group, pid); + group_freechild(child); break; } } diff --git a/nuttx/sched/sig_action.c b/nuttx/sched/sig_action.c index fe72cc22d..5c00179dc 100644 --- a/nuttx/sched/sig_action.c +++ b/nuttx/sched/sig_action.c @@ -242,7 +242,7 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * /* Free all pending exit status */ - task_removechildren(rtcb); + group_removechildren(rtcb->group); irqrestore(flags); } #endif diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c deleted file mode 100644 index c0df3d534..000000000 --- a/nuttx/sched/task_childstatus.c +++ /dev/null @@ -1,440 +0,0 @@ -/***************************************************************************** - * sched/task_childstatus.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" -#include "group_internal.h" - -#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) - -/***************************************************************************** - * Pre-processor Definitions - *****************************************************************************/ -/* Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. - * However, the number of child status structures may need to be significantly - * larger because this number includes the maximum number of tasks that are - * running PLUS the number of tasks that have exit'ed without having their - * exit status reaped (via wait(), waitid(), or waitpid()). - * - * Obviously, if tasks spawn children indefinitely and never have the exit - * status reaped, then you have a memory leak! - */ - -#if !defined(CONFIG_PREALLOC_CHILDSTATUS) || CONFIG_PREALLOC_CHILDSTATUS == 0 -# undef CONFIG_PREALLOC_CHILDSTATUS -# define CONFIG_PREALLOC_CHILDSTATUS (2*CONFIG_MAX_TASKS) -#endif - -#ifndef CONFIG_DEBUG -# undef CONFIG_DEBUG_CHILDSTATUS -#endif - -/***************************************************************************** - * Private Types - *****************************************************************************/ -/* Globals are maintained in a structure to minimize name collisions. */ - -struct child_pool_s -{ - struct child_status_s alloc[CONFIG_PREALLOC_CHILDSTATUS]; - FAR struct child_status_s *freelist; -}; - -/***************************************************************************** - * Private Data - *****************************************************************************/ - -static struct child_pool_s g_child_pool; - -/***************************************************************************** - * Private Functions - *****************************************************************************/ - -/***************************************************************************** - * Name: task_dumpchildren - * - * Description: - * Dump all of the children when the part TCB list is modified. - * - * Parameters: - * tcb - The parent TCB. - * - * Return Value: - * None. - * - * Assumptions: - * Called early in initialization. No special precautions are required. - * - *****************************************************************************/ - -#ifdef CONFIG_DEBUG_CHILDSTATUS -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 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); - } -} -#else -# define task_dumpchildren(t,m) -#endif - -/***************************************************************************** - * Public Functions - *****************************************************************************/ - -/***************************************************************************** - * Name: task_initialize - * - * Description: - * Initialize task related status. At present, this includes only the - * initialize of the child status pool. - * - * Parameters: - * None. - * - * Return Value: - * None. - * - * Assumptions: - * Called early in initialization. No special precautions are required. - * - *****************************************************************************/ - -void task_initialize(void) -{ - FAR struct child_status_s *curr; - FAR struct child_status_s *prev; - int i; - - /* Save all of the child status structures in a free list */ - - prev = &g_child_pool.alloc[0]; - g_child_pool.freelist = prev; - for (i = 0; i < CONFIG_PREALLOC_CHILDSTATUS; i++) - { - curr = &g_child_pool.alloc[i]; - prev->flink = curr; - prev = curr; - } -} - -/***************************************************************************** - * Name: task_allocchild - * - * Description: - * Allocate a child status structure by removing the next entry from a - * free list. - * - * Parameters: - * None. - * - * Return Value: - * On success, a non-NULL pointer to a child status structure. NULL is - * returned if there are no remaining, pre-allocated child status structures. - * - * Assumptions: - * Called during task creation in a safe context. No special precautions - * are required here. - * - *****************************************************************************/ - -FAR struct child_status_s *task_allocchild(void) -{ - FAR struct child_status_s *ret; - - /* Return the status block at the head of the free list */ - - ret = g_child_pool.freelist; - if (ret) - { - g_child_pool.freelist = ret->flink; - ret->flink = NULL; - } - - return ret; -} - -/***************************************************************************** - * Name: task_freechild - * - * Description: - * Release a child status structure by returning it to a free list. - * - * Parameters: - * status - The child status structure to be freed. - * - * Return Value: - * None. - * - * Assumptions: - * Called during task creation in a safe context. No special precautions - * are required here. - * - *****************************************************************************/ - -void task_freechild(FAR struct child_status_s *child) -{ - /* Return the child status structure to the free list */ - - if (child) - { - child->flink = g_child_pool.freelist; - g_child_pool.freelist = child; - } -} - -/***************************************************************************** - * Name: task_addchild - * - * Description: - * Add a child status structure in the given TCB. - * - * Parameters: - * tcb - The TCB of the parent task to containing the child status. - * child - The structure to be added - * - * Return Value: - * N - * - * Assumptions: - * Called during task creation processing in a safe context. No special - * precautions are required here. - * - *****************************************************************************/ - -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 = group->tg_children; - group->tg_children = child; - - task_dumpchildren(tcb, "task_addchild"); -} - -/***************************************************************************** - * Name: task_findchild - * - * Description: - * Find a child status structure in the given TCB. A reference to the - * child structure is returned, but the child remains the the TCB's list - * of children. - * - * Parameters: - * tcb - The TCB of the parent task to containing the child status. - * pid - The ID of the child to find. - * - * Return Value: - * On success, a non-NULL pointer to a child status structure. NULL is - * returned if there is child status structure for that pid in the TCB. - * - * Assumptions: - * Called during SIGCHLD processing in a safe context. No special precautions - * are required here. - * - *****************************************************************************/ - -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 = group->tg_children; child; child = child->flink) - { - if (child->ch_pid == pid) - { - return child; - } - } - - 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 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 = group->tg_children; child; child = child->flink) - { - if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) - { - return child; - } - } - - return NULL; -} - -/***************************************************************************** - * Name: task_removechild - * - * Description: - * Remove one child structure from the TCB. The child is removed, but is - * not yet freed. task_freechild must be called in order to free the child - * status structure. - * - * Parameters: - * tcb - The TCB of the parent task to containing the child status. - * pid - The ID of the child to find. - * - * Return Value: - * On success, a non-NULL pointer to a child status structure. NULL is - * returned if there is child status structure for that pid in the TCB. - * - * Assumptions: - * Called during SIGCHLD processing in a safe context. No special precautions - * are required here. - * - *****************************************************************************/ - -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 = group->tg_children; - curr; - prev = curr, curr = curr->flink) - { - if (curr->ch_pid == pid) - { - break; - } - } - - /* Did we find it? If so, remove it from the TCB. */ - - if (curr) - { - /* Do we remove it from mid-list? Or from the head of the list? */ - - if (prev) - { - prev->flink = curr->flink; - } - else - { - group->tg_children = curr->flink; - } - - curr->flink = NULL; - task_dumpchildren(tcb, "task_removechild"); - } - - return curr; -} - -/***************************************************************************** - * Name: task_removechildren - * - * Description: - * Remove and free all child structure from the TCB. - * - * Parameters: - * tcb - The TCB of the parent task to containing the child status. - * - * Return Value: - * None. - * - * Assumptions: - * Called during task exit processing in a safe context. No special - * precautions are required here. - * - *****************************************************************************/ - -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 = group->tg_children; curr; curr = next) - { - next = curr->flink; - task_freechild(curr); - } - - group->tg_children = NULL; - task_dumpchildren(tcb, "task_removechildren"); -} - -#endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_SCHED_CHILD_STATUS */ diff --git a/nuttx/sched/task_exithook.c b/nuttx/sched/task_exithook.c index cbada851a..3fdf08bf7 100644 --- a/nuttx/sched/task_exithook.c +++ b/nuttx/sched/task_exithook.c @@ -104,10 +104,7 @@ static inline void task_atexit(FAR _TCB *tcb) (*tcb->atexitfunc[index])(); - /* Nullify the atexit function. task_exithook may be called more then - * once in most task exit scenarios. Nullifying the atext function - * pointer will assure that the callback is performed only once. - */ + /* Nullify the atexit function to prevent its reuse. */ tcb->atexitfunc[index] = NULL; } @@ -120,10 +117,7 @@ static inline void task_atexit(FAR _TCB *tcb) (*tcb->atexitfunc)(); - /* Nullify the atexit function. task_exithook may be called more then - * once in most task exit scenarios. Nullifying the atext function - * pointer will assure that the callback is performed only once. - */ + /* Nullify the atexit function to prevent its reuse. */ tcb->atexitfunc = NULL; } @@ -161,10 +155,7 @@ static inline void task_onexit(FAR _TCB *tcb, int status) (*tcb->onexitfunc[index])(status, tcb->onexitarg[index]); - /* Nullify the on_exit function. task_exithook may be called more then - * once in most task exit scenarios. Nullifying the atext function - * pointer will assure that the callback is performed only once. - */ + /* Nullify the on_exit function to prevent its reuse. */ tcb->onexitfunc[index] = NULL; } @@ -176,10 +167,7 @@ static inline void task_onexit(FAR _TCB *tcb, int status) (*tcb->onexitfunc)(status, tcb->onexitarg); - /* Nullify the on_exit function. task_exithook may be called more then - * once in most task exit scenarios. Nullifying the on_exit function - * pointer will assure that the callback is performed only once. - */ + /* Nullify the on_exit function to prevent its reuse. */ tcb->onexitfunc = NULL; } @@ -198,20 +186,96 @@ static inline void task_onexit(FAR _TCB *tcb, int status) ****************************************************************************/ #ifdef CONFIG_SCHED_HAVE_PARENT +#ifdef HAVE_GROUP_MEMBERS +static inline void task_sigchild(gid_t pgid, FAR _TCB *ctcb, int status) +{ + FAR struct task_group_s *chgrp = ctcb->group; + FAR struct task_group_s *pgrp; + siginfo_t info; + + DEBUGASSERT(chgrp); + + /* Only the final exiting thread in a task group should generate SIGCHLD. */ + + if (chgrp->tg_nmembers == 1) + { + /* Get the parent task group */ + + pgrp = group_find(chgrp->tg_pgid); + + /* It is possible that all of the members of the parent task group + * have exited. This would not be an error. In this case, the + * child task group has been orphaned. + */ + + if (!pgrp) + { + /* Set the task group ID to an invalid group ID. The dead parent + * task group ID could get reused some time in the future. + */ + + chgrp->tg_pgid = INVALID_GROUP_ID; + return; + } + +#ifdef CONFIG_SCHED_CHILD_STATUS + /* 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 ((pgrp->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) + { + FAR struct child_status_s *child; + + /* No.. Find the exit status entry for this task in the parent TCB */ + + child = group_findchild(pgrp, getpid()); + DEBUGASSERT(child); + if (child) + { + /* Mark that the child has exit'ed */ + + child->ch_flags |= CHILD_FLAG_EXITED; + + /* Save the exit status */ + + child->ch_status = status; + } + } +#endif /* CONFIG_SCHED_CHILD_STATUS */ + + /* 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. + */ + + info.si_signo = SIGCHLD; + info.si_code = CLD_EXITED; + info.si_value.sival_ptr = NULL; + info.si_pid = ctcb->pid; + info.si_status = status; + + /* Send the signal. We need to use this internal interface so that we + * can provide the correct si_code value with the signal. + */ + + (void)group_signal(pgrp, &info); + } +} + +#else /* HAVE_GROUP_MEMBERS */ + static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) { siginfo_t info; - /* 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 task groups are not supported then we will report SIGCHLD when the + * task exits. Unfortunately, there could still be threads in the group + * that are still running. */ -#ifdef CONFIG_SCHED_CHILD_STATUS - if (ctcb->group->tg_nmembers == 1) -#else if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK) -#endif { #ifdef CONFIG_SCHED_CHILD_STATUS /* Check if the parent task group has suppressed retention of child exit @@ -225,7 +289,7 @@ static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) /* No.. Find the exit status entry for this task in the parent TCB */ - child = task_findchild(ptcb, getpid()); + child = group_findchild(ptcb->group, getpid()); DEBUGASSERT(child); if (child) { @@ -238,12 +302,15 @@ static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) child->ch_status = status; } } -#else + +#else /* CONFIG_SCHED_CHILD_STATUS */ + /* Decrement the number of children from this parent */ DEBUGASSERT(ptcb->nchildren > 0); ptcb->nchildren--; -#endif + +#endif /* CONFIG_SCHED_CHILD_STATUS */ /* 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 @@ -260,16 +327,16 @@ static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) * can provide the correct si_code value with the signal. */ -#ifdef HAVE_GROUP_MEMBERS - (void)group_signal(ptcb, &info); -#else (void)sig_received(ptcb, &info); -#endif } } -#else -# define task_sigchild(ptct,ctcb,status) -#endif + +#endif /* HAVE_GROUP_MEMBERS */ +#else /* CONFIG_SCHED_HAVE_PARENT */ + +# define task_sigchild(x,ctcb,status) + +#endif /* CONFIG_SCHED_HAVE_PARENT */ /**************************************************************************** * Name: task_leavegroup @@ -282,6 +349,18 @@ static inline void task_sigchild(FAR _TCB *ptcb, FAR _TCB *ctcb, int status) #ifdef CONFIG_SCHED_HAVE_PARENT static inline void task_leavegroup(FAR _TCB *ctcb, int status) { +#ifdef HAVE_GROUP_MEMBERS + DEBUGASSERT(ctcb && ctcb->group); + + /* Keep things stationary throughout the following */ + + sched_lock(); + + /* Send SIGCHLD to all members of the parent's task group */ + + task_sigchild(ctcb->group->tg_pgid, ctcb, status); + sched_unlock(); +#else FAR _TCB *ptcb; /* Keep things stationary throughout the following */ @@ -289,12 +368,12 @@ static inline void task_leavegroup(FAR _TCB *ctcb, int status) 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 + * handle multiple calls to task_leavegroup. ctcb->ppid is set to an * invalid value below and the following call will fail if we are * called again. */ - ptcb = sched_gettcb(ctcb->parent); + ptcb = sched_gettcb(ctcb->ppid); if (!ptcb) { /* The parent no longer exists... bail */ @@ -307,14 +386,11 @@ static inline void task_leavegroup(FAR _TCB *ctcb, int status) 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. - */ + /* Forget who our parent was */ - ctcb->parent = INVALID_PROCESS_ID; + ctcb->ppid = INVALID_PROCESS_ID; sched_unlock(); +#endif } #else # define task_leavegroup(ctcb,status) @@ -385,6 +461,16 @@ static inline void task_exitwakeup(FAR _TCB *tcb, int status) void task_exithook(FAR _TCB *tcb, int status) { + /* Under certain conditions, task_exithook() can be called multiple times. + * A bit in the TCB was set the first time this function was called. If + * that bit is set, then just ext doing nothing more.. + */ + + if ((tcb->flags & TCB_FLAG_EXIT_PROCESSING) != 0) + { + return; + } + /* If exit function(s) were registered, call them now before we do any un- * initialization. NOTE: In the case of task_delete(), the exit function * will *not* be called on the thread execution of the task being deleted! @@ -433,4 +519,11 @@ void task_exithook(FAR _TCB *tcb, int status) #ifndef CONFIG_DISABLE_SIGNALS sig_cleanup(tcb); /* Deallocate Signal lists */ #endif + + /* This function can be re-entered in certain cases. Set a flag + * bit in the TCB to not that we have already completed this exit + * processing. + */ + + tcb->flags |= TCB_FLAG_EXIT_PROCESSING; } diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c index 1193c9a7f..5bb62893f 100644 --- a/nuttx/sched/task_reparent.c +++ b/nuttx/sched/task_reparent.c @@ -70,6 +70,141 @@ * *****************************************************************************/ +#ifdef HAVE_GROUP_MEMBERS +int task_reparent(pid_t ppid, pid_t chpid) +{ +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; +#endif + FAR struct task_group_s *chgrp; + FAR struct task_group_s *ogrp; + FAR struct task_group_s *pgrp; + _TCB *tcb; + gid_t ogid; + gid_t pgid; + irqstate_t flags; + int ret; + + /* Disable interrupts so that nothing can change in the relatinoship of + * the three task: Child, current parent, and new parent. + */ + + flags = irqsave(); + + /* Get the child tasks task group */ + + tcb = sched_gettcb(chpid); + if (!tcb) + { + ret = -ECHILD; + goto errout_with_ints; + } + + DEBUGASSERT(tcb->group); + chgrp = tcb->group; + + /* Get the GID of the old parent task's task group (ogid) */ + + ogid = chgrp->tg_pgid; + + /* Get the old parent task's task group (ogrp) */ + + ogrp = group_find(ogid); + if (!ogrp) + { + ret = -ESRCH; + goto errout_with_ints; + } + + /* If new parent task's PID (ppid) is zero, then new parent is the + * grandparent will be the new parent, i.e., the parent of the current + * parent task. + */ + + if (ppid == 0) + { + /* Get the grandparent task's task group (pgrp) */ + + pgid = ogrp->tg_pgid; + pgrp = group_find(pgid); + } + else + { + /* Get the new parent task's task group (pgrp) */ + + tcb = sched_gettcb(ppid); + if (!tcb) + { + ret = -ESRCH; + goto errout_with_ints; + } + + pgrp = tcb->group; + pgid = pgrp->tg_gid; + } + + if (!pgrp) + { + ret = -ESRCH; + goto errout_with_ints; + } + + /* Then reparent the child. Notice that we don't actually change the + * parent of the task. Rather, we change the parent task group for + * all members of the child's task group. + */ + + chgrp->tg_pgid = pgid; + +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Remove the child status entry from old parent task group */ + + child = group_removechild(ogrp, chpid); + if (child) + { + /* Has the new parent's task group supressed child exit status? */ + + if ((pgrp->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) + { + /* No.. Add the child status entry to the new parent's task group */ + + group_addchild(pgrp, child); + } + else + { + /* Yes.. Discard the child status entry */ + + group_freechild(child); + } + + /* Either case is a success */ + + ret = OK; + } + else + { + /* This would not be an error if the original parent's task group has + * suppressed child exit status. + */ + + ret = ((ogrp->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; + } + +#else /* CONFIG_SCHED_CHILD_STATUS */ + + DEBUGASSERT(otcb->nchildren > 0); + + otcb->nchildren--; /* The orignal parent now has one few children */ + ptcb->nchildren++; /* The new parent has one additional child */ + ret = OK; + +#endif /* CONFIG_SCHED_CHILD_STATUS */ + +errout_with_ints: + irqrestore(flags); + return ret; +} +#else int task_reparent(pid_t ppid, pid_t chpid) { #ifdef CONFIG_SCHED_CHILD_STATUS @@ -99,7 +234,7 @@ int task_reparent(pid_t ppid, pid_t chpid) /* Get the PID of the child task's parent (opid) */ - opid = chtcb->parent; + opid = chtcb->ppid; /* Get the TCB of the child task's parent (otcb) */ @@ -117,7 +252,7 @@ int task_reparent(pid_t ppid, pid_t chpid) if (ppid == 0) { - ppid = otcb->parent; + ppid = otcb->ppid; } /* Get the new parent task's TCB (ptcb) */ @@ -131,12 +266,12 @@ int task_reparent(pid_t ppid, pid_t chpid) /* Then reparent the child */ - chtcb->parent = ppid; /* The task specified by ppid is the new parent */ + chtcb->ppid = ppid; /* The task specified by ppid is the new parent */ #ifdef CONFIG_SCHED_CHILD_STATUS /* Remove the child status entry from old parent TCB */ - child = task_removechild(otcb, chpid); + child = group_removechild(otcb->group, chpid); if (child) { /* Has the new parent's task group supressed child exit status? */ @@ -145,13 +280,13 @@ int task_reparent(pid_t ppid, pid_t chpid) { /* No.. Add the child status entry to the new parent's task group */ - task_addchild(ptcb, child); + group_addchild(ptcb->group, child); } else { /* Yes.. Discard the child status entry */ - task_freechild(child); + group_freechild(child); } /* Either case is a success */ @@ -166,17 +301,20 @@ int task_reparent(pid_t ppid, pid_t chpid) ret = ((otcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; } -#else + +#else /* CONFIG_SCHED_CHILD_STATUS */ + DEBUGASSERT(otcb->nchildren > 0); otcb->nchildren--; /* The orignal parent now has one few children */ ptcb->nchildren++; /* The new parent has one additional child */ ret = OK; -#endif + +#endif /* CONFIG_SCHED_CHILD_STATUS */ errout_with_ints: irqrestore(flags); return ret; } - +#endif #endif /* CONFIG_SCHED_HAVE_PARENT */ diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c index b66f3cc7c..b770d46e6 100644 --- a/nuttx/sched/task_setup.c +++ b/nuttx/sched/task_setup.c @@ -172,12 +172,36 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; +#if defined(HAVE_GROUP_MEMBERS) || defined(CONFIG_SCHED_CHILD_STATUS) + DEBUGASSERT(tcb && tcb->group && rtcb->group); +#else +#endif + +#ifdef HAVE_GROUP_MEMBERS + /* Save the ID of the parent tasks' task group in the child's task group. + * Do nothing for pthreads. The parent and the child are both members of + * the same task group. + */ + + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) + { + /* This is a new task in a new task group, we have to copy the ID from + * the parent's task group structure to child's task group. + */ + + tcb->group->tg_pgid = rtcb->group->tg_gid; + } + +#else + DEBUGASSERT(tcb); + /* Save the parent task's ID in the child task's TCB. I am not sure if * this makes sense for the case of pthreads or not, but I don't think it * is harmful in any event. */ - tcb->parent = rtcb->pid; + tcb->ppid = rtcb->pid; +#endif #ifdef CONFIG_SCHED_CHILD_STATUS /* Tasks can also suppress retention of their child status by applying @@ -192,13 +216,13 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) * parent TCB. There should not be. */ - child = task_findchild(rtcb, tcb->pid); + child = group_findchild(rtcb->group, tcb->pid); DEBUGASSERT(!child); if (!child) { /* Allocate a new status structure */ - child = task_allocchild(); + child = group_allocchild(); } /* Did we successfully find/allocate the child status structure? */ @@ -214,7 +238,7 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) /* Add the entry into the TCB list of children */ - task_addchild(rtcb, child); + group_addchild(rtcb->group, child); } } #else -- cgit v1.2.3