diff --git a/ChangeLog b/ChangeLog index 2576e97a8e..bf74d92fea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4563,3 +4563,16 @@ occur when SVC instructions are executed (2013-4-16). * configs/stm3240g-eval/ostest: Converted to use the kconfig-frontends tools (2013-4-17). + * sched/task_exithook.c: Don't flush the streams until the + final thread of the group exits. Flushing may cause the + thread to get suspended at a bad time and other threads in the + group may run while the exiting thread is in an unhealthy state. + This can cause crashes under certain circumstance. This is a + critical bugfix (2013-4-18). + * drivers/mtd/ramtron.c: Extended to support the FM25V01 device. + Contributed by Lorenz Meier (2013-4-18). + * sched/task_deletecurrent.c and task_exit.c, arch/*/up_exit.c: + Renamed task_deletecurrent() and task_exit() since it really + handles the architecture independent part of _exit(). _exit() + is used internally, but if it is called from the user, it should + unregister any atexit() or on_exit() functions (2013-4-18). diff --git a/arch/8051/src/up_exit.c b/arch/8051/src/up_exit.c index c96467bf9d..31d8c99ab7 100644 --- a/arch/8051/src/up_exit.c +++ b/arch/8051/src/up_exit.c @@ -89,7 +89,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/arm/src/common/up_exit.c b/arch/arm/src/common/up_exit.c index 76bf4e0dd3..95a71f832a 100644 --- a/arch/arm/src/common/up_exit.c +++ b/arch/arm/src/common/up_exit.c @@ -153,7 +153,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/avr/src/common/up_exit.c b/arch/avr/src/common/up_exit.c index 3b316123b1..93ec7c487b 100644 --- a/arch/avr/src/common/up_exit.c +++ b/arch/avr/src/common/up_exit.c @@ -153,7 +153,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/hc/src/common/up_exit.c b/arch/hc/src/common/up_exit.c index 9fad697f19..c71bbafd13 100644 --- a/arch/hc/src/common/up_exit.c +++ b/arch/hc/src/common/up_exit.c @@ -153,7 +153,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/mips/src/common/up_exit.c b/arch/mips/src/common/up_exit.c index a5746f8aa8..8791fa40bd 100644 --- a/arch/mips/src/common/up_exit.c +++ b/arch/mips/src/common/up_exit.c @@ -155,7 +155,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/rgmp/src/nuttx.c b/arch/rgmp/src/nuttx.c index da67ac2148..a4f713be6d 100644 --- a/arch/rgmp/src/nuttx.c +++ b/arch/rgmp/src/nuttx.c @@ -422,7 +422,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/sh/src/common/up_exit.c b/arch/sh/src/common/up_exit.c index 31c8e04049..e454d1b643 100644 --- a/arch/sh/src/common/up_exit.c +++ b/arch/sh/src/common/up_exit.c @@ -154,7 +154,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/sim/src/up_exit.c b/arch/sim/src/up_exit.c index 348c4a9e65..7d7d4d0411 100644 --- a/arch/sim/src/up_exit.c +++ b/arch/sim/src/up_exit.c @@ -82,7 +82,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/x86/src/common/up_exit.c b/arch/x86/src/common/up_exit.c index 2aa21f3522..9b0c0cbe10 100644 --- a/arch/x86/src/common/up_exit.c +++ b/arch/x86/src/common/up_exit.c @@ -153,7 +153,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/z16/src/common/up_exit.c b/arch/z16/src/common/up_exit.c index 59764484fd..e4c6dcf9c8 100644 --- a/arch/z16/src/common/up_exit.c +++ b/arch/z16/src/common/up_exit.c @@ -155,7 +155,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/arch/z80/src/common/up_exit.c b/arch/z80/src/common/up_exit.c index c738202a9f..aa578f29e5 100644 --- a/arch/z80/src/common/up_exit.c +++ b/arch/z80/src/common/up_exit.c @@ -156,7 +156,7 @@ void _exit(int status) /* Destroy the task at the head of the ready to run list. */ - (void)task_deletecurrent(); + (void)task_exit(); /* Now, perform the context switch to the new ready-to-run task at the * head of the list. diff --git a/sched/Makefile b/sched/Makefile index 0ee88bf8e1..c711a35fa4 100644 --- a/sched/Makefile +++ b/sched/Makefile @@ -43,7 +43,7 @@ MISC_SRCS += sched_garbage.c sched_getfiles.c sched_getsockets.c sched_getstream TSK_SRCS = prctl.c exit.c getpid.c TSK_SRCS += task_create.c task_init.c task_setup.c task_activate.c task_start.c -TSK_SRCS += task_delete.c task_deletecurrent.c task_exithook.c task_recover.c +TSK_SRCS += task_delete.c task_exit.c task_exithook.c task_recover.c TSK_SRCS += task_restart.c task_spawn.c task_spawnparms.c TSK_SRCS += sched_addreadytorun.c sched_removereadytorun.c sched_addprioritized.c TSK_SRCS += sched_mergepending.c sched_addblocked.c sched_removeblocked.c diff --git a/sched/errno_getptr.c b/sched/errno_getptr.c index 841cb837ca..b233b18e47 100644 --- a/sched/errno_getptr.c +++ b/sched/errno_getptr.c @@ -88,7 +88,7 @@ FAR int *get_errno_ptr(void) /* We were called from the normal tasking context. Verify that the * task at the head of the ready-to-run list is actually running. It * may not be running during very brief times during context switching - * logic (see, for example, task_deletecurrent.c). + * logic (see, for example, task_exit.c). */ FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head; diff --git a/sched/os_internal.h b/sched/os_internal.h index 0b7236b28e..bbfce9696c 100644 --- a/sched/os_internal.h +++ b/sched/os_internal.h @@ -264,8 +264,8 @@ void task_start(void); int task_schedsetup(FAR struct task_tcb_s *tcb, int priority, start_t start, main_t main, uint8_t ttype); int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name, FAR char * const argv[]); +int task_exit(void); void task_exithook(FAR struct tcb_s *tcb, int status); -int task_deletecurrent(void); void task_recover(FAR struct tcb_s *tcb); #ifndef CONFIG_CUSTOM_STACK diff --git a/sched/pthread_cancel.c b/sched/pthread_cancel.c index 6c114ba758..ec464ae15d 100644 --- a/sched/pthread_cancel.c +++ b/sched/pthread_cancel.c @@ -147,4 +147,3 @@ int pthread_cancel(pthread_t thread) return OK; } - diff --git a/sched/task_delete.c b/sched/task_delete.c index 1f51fc15df..64830154c6 100644 --- a/sched/task_delete.c +++ b/sched/task_delete.c @@ -93,7 +93,7 @@ * * - pthread_exit(). Calls exit() * - exit(). Calls _exit() - * - _exit(). Calls task_deletecurrent() making the currently running task + * - _exit(). Calls task_exit() making the currently running task * non-running then calls task_delete() to terminate the non-running * task. * - task_delete() diff --git a/sched/task_deletecurrent.c b/sched/task_exit.c similarity index 53% rename from sched/task_deletecurrent.c rename to sched/task_exit.c index d2fdaefd4d..4414030cfc 100644 --- a/sched/task_deletecurrent.c +++ b/sched/task_exit.c @@ -1,5 +1,5 @@ /**************************************************************************** - * sched/task_deletecurrent.c + * sched/task_exit.c * * Copyright (C) 2008-2009, 2012-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -69,20 +69,136 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: task_cancel_atexit + * + * Description: + * Cncel any registerd atexit function(s) + * + * This function is called from task_exit() which implements the processor- + * independent part of _exit(). _exit() is, in turn, used to implement + * the bottom half of exit() and pthread_exit(). These cases are + * distinguished as follows: + * + * 1) _exit() should be called by user logic only from tasks. In this + * case, atexit() calls will be canceled by this function. + * 2) If the user calls exit(), the exit() function will call task_exithook() + * which will process all pending atexit() call. In that case, this + * function will have no effect. + * 3) If the user called pthread_exit(), the logic in this function will + * do nothing. Only a task can legitimately called _exit(). atexit + * calls will not be cleared. task_exithook() will be called later (from + * task_delete()) and if this is the final thread of the group, any + * registered atexit() calls will be performed. + * + ****************************************************************************/ + +#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT) +static inline void task_cancel_atexit(FAR struct tcb_s *tcb) +{ + FAR struct task_group_s *group = tcb->group; + DEBUGASSERT(group); + + /* This behavior applies only to tasks that _exit() */ + +#ifndef CONFIG_DISABLE_PTHREAD + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) +#endif + { +#if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1 + int index; + + /* Nullify each atexit function pointer */ + + for (index = 0; index < CONFIG_SCHED_ATEXIT_MAX; index++) + { + group->tg_atexitfunc[index] = NULL; + } +#else + /* Nullify the atexit function to prevent its reuse. */ + + group->tg_atexitfunc = NULL; +#endif + } +} +#else +# define task_cancel_atexit(tcb) +#endif + +/**************************************************************************** + * Name: task_cancel_onexit + * + * Description: + * Cancel any registerd on)exit function(s). + * + * This function is called from task_exit() which implements the processor- + * independent part of _exit(). _exit() is, in turn, used to implement + * the bottom half of exit() and pthread_exit(). These cases are + * distinguished as follows: + * + * 1) _exit() should be called by user logic only from tasks. In this + * case, on_exit() calls will be canceled by this function. + * 2) If the user calls exit(), the exit() function will call task_exithook() + * which will process all pending on_exit() call. In that case, this + * function will have no effect. + * 3) If the user called pthread_exit(), the logic in this function will + * do nothing. Only a task can legitimately called _exit(). on_exit + * calls will not be cleared. task_exithook() will be called later (from + * task_delete()) and if this is the final thread of the group, any + * registered on_exit() calls will be performed. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_ONEXIT +static inline void task_cancel_onexit(FAR struct tcb_s *tcb) +{ + FAR struct task_group_s *group = tcb->group; + DEBUGASSERT(group); + + /* This behavior applies only to tasks that _exit() */ + +#ifndef CONFIG_DISABLE_PTHREAD + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) +#endif + { +#if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1 + int index; + + /* Nullify each atexit function pointer */ + + for (index = 0; index < CONFIG_SCHED_ONEXIT_MAX; index++) + { + group->tg_onexitfunc[index] = NULL; + } +#else + group->tg_onexitfunc = NULL; +#endif + } +} +#else +# define task_cancel_onexit(tcb) +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: task_delete + * Name: task_exit * * Description: + * This is a part of the logic used to implement _exit(). The full + * implementation of _exit() is architecture-dependent. The _exit() + * function also implements the bottom half of exit() and pthread_exit(). + * * This function causes the currently running task (i.e., the task at the - * head of the ready-to-run list) to cease to exist. This is a part of - * the logic used to implement _exit(). The full implementation of _exit() - * is architecture-dependent. This function should never be called from - * normal user code, but only from the architecture-specific implementation - * of exit. + * head of the ready-to-run list) to cease to exist. This function should + * never be called from normal user code, but only from the architecture- + * specific implementation of exit. + * + * Threads/tasks could also be terminated via pthread_cancel, task_delete(), + * and task_restart(). In the last two cases, the task will be terminated + * as though exit() were called. * * Inputs: * None @@ -95,7 +211,7 @@ * ****************************************************************************/ -int task_deletecurrent(void) +int task_exit(void) { FAR struct tcb_s *dtcb = (FAR struct tcb_s*)g_readytorun.head; FAR struct tcb_s *rtcb; @@ -111,6 +227,14 @@ int task_deletecurrent(void) (void)sched_removereadytorun(dtcb); rtcb = (FAR struct tcb_s*)g_readytorun.head; + /* Cancel any pending atexit() or on_exit() calls. These are not performed + * when performing _exit(). Different implementations of _exit() may or may + * not* flush buffered I/O. This implemenation *will* flush buffered I/O. + */ + + task_cancel_atexit(rtcb); + task_cancel_onexit(rtcb); + /* We are now in a bad state -- the head of the ready to run task list * does not correspond to the thread that is running. Disabling pre- * emption on this TCB and marking the new ready-to-run task as not @@ -149,4 +273,3 @@ int task_deletecurrent(void) rtcb->lockcount--; return OK; } - diff --git a/sched/task_exithook.c b/sched/task_exithook.c index 6372e628a8..71cc132509 100644 --- a/sched/task_exithook.c +++ b/sched/task_exithook.c @@ -140,7 +140,7 @@ static inline void task_atexit(FAR struct tcb_s *tcb) * Name: task_onexit * * Description: - * Call any registerd on)exit function(s) + * Call any registerd on_exit function(s) * ****************************************************************************/