Move atexit/on_exit data structures to task group; Now callbacks only occur when the final member of the task group exits

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5607 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2013-02-04 15:29:19 +00:00
parent ad2cddb6e2
commit b1bf234bf3
7 changed files with 139 additions and 108 deletions

View File

@ -4108,3 +4108,8 @@
const char **.
* sched/pthread* and include/nuttx/sched: Move pthread join data
and pthread key calculation data into the "task group" structure.
* sched/atexit.c, on_exit.c, task_exithook.c and include/nuttx/sched.h:
Move atexit and on_exit data structure to task group. These
callbacks are only issued now when the final member of the task
group exits.

24
TODO
View File

@ -7,7 +7,7 @@ standards, things that could be improved, and ideas for enhancements.
nuttx/
(11) Task/Scheduler (sched/)
(2) Memory Managment (mm/)
(1) Memory Managment (mm/)
(3) Signals (sched/, arch/)
(2) pthreads (sched/)
(2) C++ Support
@ -186,9 +186,9 @@ o Task/Scheduler (sched/)
posix_spawn(). However, posix_spawn uses the non-standard, internal
NuttX interface task_reparent() to replace the childs parent task
with the caller of posix_spawn(). That cannot be done with vfork()
because we don't know what vfor() is going to do.
because we don't know what vfork() is going to do.
Any solution to this is either very difficult or impossible with
Any solution to this is either very difficult or impossible without
an MMU.
Status: Open
Priority: Low (it might as well be low since it isn't going to be fixed).
@ -200,7 +200,8 @@ o Task/Scheduler (sched/)
to make this change: Just move the pterrno field from
_TCB to struct task_group_s. However, I am still not sure
if this should be done or not.
Status: Open
Status: Closed. The existing solution is better (although its
incompatibilities could show up in porting some code).
Priority: Low
o Memory Managment (mm/)
@ -269,19 +270,6 @@ o Memory Managment (mm/)
Priority: Medium/Low, a good feature to prevent memory leaks but would
have negative impact on memory usage and code size.
Title: CONTAINER ALLOCATOR
Description: There are several places where the logic requires allocation of
a tiny structure that just contains pointers to other things or
small amounts of data that needs to be bundled together. There
are examples net/net_poll.c and numerous other places.
I am wondering if it would not be good create a pool of generic
containers (say void *[4]). There re-use these when we need
small containers. The code in sched/task_childstatus.c might
be generalized for this purpose.
Status: Open
Priority: Very low (I am not even sure that this is a good idea yet).
o Signals (sched/, arch/)
^^^^^^^^^^^^^^^^^^^^^^^
@ -289,7 +277,7 @@ o Signals (sched/, arch/)
Description: 'Standard' signals and signal actions are not supported.
(e.g., SIGINT, SIGSEGV, etc).
Update: SIG_CHLD is support if configured.
Update: SIG_CHLD is supported if so configured.
Status: Open. No changes are planned.
Priority: Low, required by standards but not so critical for an
embedded system.

View File

@ -64,7 +64,7 @@
#undef HAVE_TASK_GROUP
#undef HAVE_GROUP_MEMBERS
/* We need a group an group members if we are supportint the parent/child
/* We need a group an group members if we are supporting the parent/child
* relationship.
*/
@ -77,13 +77,17 @@
*/
#else
# if !defined(CONFIG_DISABLE_ENVIRON)
# if !defined(CONFIG_DISABLE_ENVIRON) /* Environment variales */
# define HAVE_TASK_GROUP 1
# elif CONFIG_NFILE_DESCRIPTORS > 0
# elif defined(CONFIG_SCHED_ATEXIT) /* Group atexit() function */
# define HAVE_TASK_GROUP 1
# elif CONFIG_NFILE_STREAMS > 0
# elif defined(CONFIG_SCHED_ONEXIT) /* Group on_exit() function */
# define HAVE_TASK_GROUP 1
# elif CONFIG_NSOCKET_DESCRIPTORS > 0
# elif CONFIG_NFILE_DESCRIPTORS > 0 /* File descriptors */
# define HAVE_TASK_GROUP 1
# elif CONFIG_NFILE_STREAMS > 0 /* Standard C buffered I/O */
# define HAVE_TASK_GROUP 1
# elif CONFIG_NSOCKET_DESCRIPTORS > 0 /* Sockets */
# define HAVE_TASK_GROUP 1
# endif
#endif
@ -296,6 +300,26 @@ struct task_group_s
FAR pid_t *tg_members; /* Members of the group */
#endif
/* atexit/on_exit support ****************************************************/
#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT)
# if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
atexitfunc_t tg_atexitfunc[CONFIG_SCHED_ATEXIT_MAX];
# else
atexitfunc_t tg_atexitfunc; /* Called when exit is called. */
# endif
#endif
#ifdef CONFIG_SCHED_ONEXIT
# if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
onexitfunc_t tg_onexitfunc[CONFIG_SCHED_ONEXIT_MAX];
FAR void *tg_onexitarg[CONFIG_SCHED_ONEXIT_MAX];
# else
onexitfunc_t tg_onexitfunc; /* Called when exit is called. */
FAR void *tg_onexitarg; /* The argument passed to the function */
# endif
#endif
/* Child exit status **********************************************************/
#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
@ -384,24 +408,6 @@ struct _TCB
FAR void *starthookarg; /* The argument passed to the function */
#endif
#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT)
# if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
atexitfunc_t atexitfunc[CONFIG_SCHED_ATEXIT_MAX];
# else
atexitfunc_t atexitfunc; /* Called when exit is called. */
# endif
#endif
#ifdef CONFIG_SCHED_ONEXIT
# if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
onexitfunc_t onexitfunc[CONFIG_SCHED_ONEXIT_MAX];
FAR void *onexitarg[CONFIG_SCHED_ONEXIT_MAX];
# else
onexitfunc_t onexitfunc; /* Called when exit is called. */
FAR void *onexitarg; /* The argument passed to the function */
# endif
#endif
#if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_SCHED_HAVE_PARENT)
sem_t exitsem; /* Support for waitpid */
int *stat_loc; /* Location to return exit status */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/atexit.c
*
* Copyright (C) 2007, 2009, 2011-2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2007, 2009, 2011-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -117,12 +117,15 @@ int atexit(void (*func)(void))
* can handle a callback function that recieves more parameters than it expects).
*/
return on_exit(onexitfunc_t func, NULL);
return on_exit((onexitfunc_t)func, NULL);
#elif defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
_TCB *tcb = (_TCB*)g_readytorun.head;
int index;
int ret = ERROR;
FAR _TCB *tcb = (FAR _TCB*)g_readytorun.head;
FAR struct task_group_s *group = tcb->group;
int index;
int ret = ERROR;
DEBUGASSERT(group);
/* The following must be atomic */
@ -139,9 +142,9 @@ int atexit(void (*func)(void))
available = -1;
for (index = 0; index < CONFIG_SCHED_ATEXIT_MAX; index++)
{
if (!tcb->atexitfunc[index])
if (!group->tg_atexitfunc[index])
{
tcb->atexitfunc[index] = func;
group->tg_atexitfunc[index] = func;
ret = OK;
break;
}
@ -152,15 +155,18 @@ int atexit(void (*func)(void))
return ret;
#else
_TCB *tcb = (_TCB*)g_readytorun.head;
int ret = ERROR;
FAR _TCB *tcb = (FAR _TCB*)g_readytorun.head;
FAR struct task_group_s *group = tcb->group;
int ret = ERROR;
DEBUGASSERT(group);
/* The following must be atomic */
sched_lock();
if (func && !tcb->atexitfunc)
if (func && !group->tg_atexitfunc)
{
tcb->atexitfunc = func;
group->tg_atexitfunc = func;
ret = OK;
}

View File

@ -48,8 +48,9 @@
#include <nuttx/net/net.h>
#include <nuttx/lib.h>
#include "group_internal.h"
#include "env_internal.h"
#include "pthread_internal.h"
#include "group_internal.h"
#ifdef HAVE_TASK_GROUP

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/on_exit.c
*
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2012-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -115,10 +115,13 @@
int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg)
{
#if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
_TCB *tcb = (_TCB*)g_readytorun.head;
FAR _TCB *tcb = (FAR _TCB*)g_readytorun.head;
FAR struct task_group_s *group = tcb->group;
int index;
int ret = ENOSPC;
DEBUGASSERT(group);
/* The following must be atomic */
if (func)
@ -133,10 +136,10 @@ int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg)
for (index = 0; index < CONFIG_SCHED_ONEXIT_MAX; index++)
{
if (!tcb->onexitfunc[index])
if (!group->tg_onexitfunc[index])
{
tcb->onexitfunc[index] = func;
tcb->onexitarg[index] = arg;
group->tg_onexitfunc[index] = func;
group->tg_onexitarg[index] = arg;
ret = OK;
break;
}
@ -147,16 +150,19 @@ int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg)
return ret;
#else
_TCB *tcb = (_TCB*)g_readytorun.head;
FAR _TCB *tcb = (FAR _TCB*)g_readytorun.head;
FAR struct task_group_s *group = tcb->group;
int ret = ENOSPC;
DEBUGASSERT(group);
/* The following must be atomic */
sched_lock();
if (func && !tcb->onexitfunc)
if (func && !group->tg_onexitfunc)
{
tcb->onexitfunc = func;
tcb->onexitarg = arg;
group->tg_onexitfunc = func;
group->tg_onexitarg = arg;
ret = OK;
}

View File

@ -87,41 +87,50 @@
#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT)
static inline void task_atexit(FAR _TCB *tcb)
{
#if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
int index;
FAR struct task_group_s *group = tcb->group;
/* Call each atexit function in reverse order of registration atexit()
* functions are registered from lower to higher arry indices; they must
* be called in the reverse order of registration when task exists, i.e.,
* from higher to lower indices.
/* Make sure that we have not already left the group. Only the final
* exitting thread in the task group should trigger the atexit()
* callbacks.
*/
for (index = CONFIG_SCHED_ATEXIT_MAX-1; index >= 0; index--)
if (group && group->tg_nmembers == 1)
{
if (tcb->atexitfunc[index])
#if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
int index;
/* Call each atexit function in reverse order of registration atexit()
* functions are registered from lower to higher arry indices; they
* must be called in the reverse order of registration when the task
* group exits, i.e., from higher to lower indices.
*/
for (index = CONFIG_SCHED_ATEXIT_MAX-1; index >= 0; index--)
{
if (group->tg_atexitfunc[index])
{
/* Call the atexit function */
(*group->tg_atexitfunc[index])();
/* Nullify the atexit function to prevent its reuse. */
group->tg_atexitfunc[index] = NULL;
}
}
#else
if (group->tg_atexitfunc)
{
/* Call the atexit function */
(*tcb->atexitfunc[index])();
(*group->tg_atexitfunc)();
/* Nullify the atexit function to prevent its reuse. */
tcb->atexitfunc[index] = NULL;
group->tg_atexitfunc = NULL;
}
}
#else
if (tcb->atexitfunc)
{
/* Call the atexit function */
(*tcb->atexitfunc)();
/* Nullify the atexit function to prevent its reuse. */
tcb->atexitfunc = NULL;
}
#endif
}
}
#else
# define task_atexit(tcb)
@ -138,40 +147,50 @@ static inline void task_atexit(FAR _TCB *tcb)
#ifdef CONFIG_SCHED_ONEXIT
static inline void task_onexit(FAR _TCB *tcb, int status)
{
#if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
int index;
FAR struct task_group_s *group = tcb->group;
/* Call each on_exit function in reverse order of registration. on_exit()
* functions are registered from lower to higher arry indices; they must
* be called in the reverse order of registration when task exists, i.e.,
* from higher to lower indices.
/* Make sure that we have not already left the group. Only the final
* exitting thread in the task group should trigger the atexit()
* callbacks.
*/
for (index = CONFIG_SCHED_ONEXIT_MAX-1; index >= 0; index--)
if (group && group->tg_nmembers == 1)
{
if (tcb->onexitfunc[index])
#if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
int index;
/* Call each on_exit function in reverse order of registration.
* on_exit() functions are registered from lower to higher arry
* indices; they must be called in the reverse order of registration
* when the task grroup exits, i.e., from higher to lower indices.
*/
for (index = CONFIG_SCHED_ONEXIT_MAX-1; index >= 0; index--)
{
if (group->tg_onexitfunc[index])
{
/* Call the on_exit function */
(*group->tg_onexitfunc[index])(status, group->tg_onexitarg[index]);
/* Nullify the on_exit function to prevent its reuse. */
group->tg_onexitfunc[index] = NULL;
}
}
#else
if (group->tg_onexitfunc)
{
/* Call the on_exit function */
(*tcb->onexitfunc[index])(status, tcb->onexitarg[index]);
(*group->tg_onexitfunc)(status, group->tg_onexitarg);
/* Nullify the on_exit function to prevent its reuse. */
tcb->onexitfunc[index] = NULL;
group->tg_onexitfunc = NULL;
}
}
#else
if (tcb->onexitfunc)
{
/* Call the on_exit function */
(*tcb->onexitfunc)(status, tcb->onexitarg);
/* Nullify the on_exit function to prevent its reuse. */
tcb->onexitfunc = NULL;
}
#endif
}
}
#else
# define task_onexit(tcb,status)
@ -490,7 +509,7 @@ static inline void task_exitwakeup(FAR _TCB *tcb, int status)
* Description:
* This function implements some of the internal logic of exit() and
* task_delete(). This function performs some cleanup and other actions
* required when a task exists:
* required when a task exits:
*
* - All open streams are flushed and closed.
* - All functions registered with atexit() and on_exit() are called, in