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:
parent
ad2cddb6e2
commit
b1bf234bf3
@ -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
24
TODO
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user