libc/stdio: Flush streams in userspace when process exits
This fixes a long standing issue, where the kernel does flushing of file streams, although it should not handle such streams at all.
This commit is contained in:
parent
c2fa780ed0
commit
3be81e649f
@ -27,6 +27,7 @@
|
||||
#include <nuttx/atexit.h>
|
||||
#include <nuttx/compiler.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -47,9 +48,15 @@ FAR void *__dso_handle = &__dso_handle;
|
||||
|
||||
void exit(int status)
|
||||
{
|
||||
/* Run the registered exit functions */
|
||||
|
||||
atexit_call_exitfuncs(status);
|
||||
|
||||
/* REVISIT: Need to flush files and streams */
|
||||
/* Flush all streams */
|
||||
|
||||
fflush(NULL);
|
||||
|
||||
/* Then perform the exit */
|
||||
|
||||
_exit(status);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/net/net.h>
|
||||
#include <nuttx/lib/lib.h>
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#ifdef CONFIG_BINFMT_LOADABLE
|
||||
# include <nuttx/binfmt/binfmt.h>
|
||||
|
@ -100,5 +100,5 @@ int pthread_cancel(pthread_t thread)
|
||||
|
||||
/* Then let nxtask_terminate do the real work */
|
||||
|
||||
return nxtask_terminate((pid_t)thread, false);
|
||||
return nxtask_terminate((pid_t)thread);
|
||||
}
|
||||
|
@ -90,17 +90,18 @@ void nx_pthread_exit(FAR void *exit_value)
|
||||
}
|
||||
|
||||
/* Perform common task termination logic. This will get called again later
|
||||
* through logic kicked off by _exit(). However, we need to call it before
|
||||
* calling _exit() in order certain operations if this is the last thread
|
||||
* of a task group: (2) To handle atexit() and on_exit() callbacks and
|
||||
* (2) so that we can flush buffered I/O (which may required suspending).
|
||||
* through logic kicked off by up_exit().
|
||||
*
|
||||
* REVISIT: Tt should not be necessary to call this here, but releasing the
|
||||
* task group (especially the group file list) requires that it is done
|
||||
* here.
|
||||
*
|
||||
* The reason? up_exit removes the current process from the ready-to-run
|
||||
* list and trying to execute code that depends on this_task() crashes at
|
||||
* once, or does something very naughty.
|
||||
*/
|
||||
|
||||
nxtask_exithook(tcb, EXIT_SUCCESS, false);
|
||||
|
||||
/* Then just exit, retaining all file descriptors and without
|
||||
* calling atexit() functions.
|
||||
*/
|
||||
nxtask_exithook(tcb, status);
|
||||
|
||||
up_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -68,12 +68,18 @@ void _exit(int status)
|
||||
#endif
|
||||
|
||||
/* Perform common task termination logic. This will get called again later
|
||||
* through logic kicked off by up_exit(). However, we need to call it here
|
||||
* so that we can flush buffered I/O (both of which may required
|
||||
* suspending). This will be fixed later when I/O flush is moved to libc.
|
||||
* through logic kicked off by up_exit().
|
||||
*
|
||||
* REVISIT: Tt should not be necessary to call this here, but releasing the
|
||||
* task group (especially the group file list) requires that it is done
|
||||
* here.
|
||||
*
|
||||
* The reason? up_exit removes the current process from the ready-to-run
|
||||
* list and trying to execute code that depends on this_task() crashes at
|
||||
* once, or does something very naughty.
|
||||
*/
|
||||
|
||||
nxtask_exithook(tcb, status, false);
|
||||
nxtask_exithook(tcb, status);
|
||||
|
||||
up_exit(status);
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ int nxtask_setup_arguments(FAR struct task_tcb_s *tcb,
|
||||
/* Task exit */
|
||||
|
||||
int nxtask_exit(void);
|
||||
int nxtask_terminate(pid_t pid, bool nonblocking);
|
||||
void nxtask_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking);
|
||||
int nxtask_terminate(pid_t pid);
|
||||
void nxtask_exithook(FAR struct tcb_s *tcb, int status);
|
||||
void nxtask_recover(FAR struct tcb_s *tcb);
|
||||
|
||||
/* Cancellation points */
|
||||
|
@ -132,7 +132,7 @@ int nxtask_delete(pid_t pid)
|
||||
* nxtask_terminate() do all of the heavy lifting.
|
||||
*/
|
||||
|
||||
ret = nxtask_terminate(pid, false);
|
||||
ret = nxtask_terminate(pid);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
|
@ -156,7 +156,7 @@ int nxtask_exit(void)
|
||||
rtcb->irqcount++;
|
||||
#endif
|
||||
|
||||
ret = nxtask_terminate(dtcb->pid, true);
|
||||
ret = nxtask_terminate(dtcb->pid);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
rtcb->irqcount--;
|
||||
|
@ -378,34 +378,6 @@ static inline void nxtask_exitwakeup(FAR struct tcb_s *tcb, int status)
|
||||
# define nxtask_exitwakeup(tcb, status)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_flushstreams
|
||||
*
|
||||
* Description:
|
||||
* Flush all streams when the final thread of a group exits.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_FILE_STREAM
|
||||
static inline void nxtask_flushstreams(FAR struct tcb_s *tcb)
|
||||
{
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
|
||||
/* Have we already left the group? Are we the last thread in the group? */
|
||||
|
||||
if (group && group->tg_nmembers == 1)
|
||||
{
|
||||
#ifdef CONFIG_MM_KERNEL_HEAP
|
||||
lib_flushall(tcb->group->tg_streamlist);
|
||||
#else
|
||||
lib_flushall(&tcb->group->tg_streamlist);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define nxtask_flushstreams(tcb)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -431,13 +403,9 @@ static inline void nxtask_flushstreams(FAR struct tcb_s *tcb)
|
||||
* task_delete will have already removed the tcb from the ready-to-run
|
||||
* list to prevent any further action on this task.
|
||||
*
|
||||
* nonblocking will be set true only when we are called from
|
||||
* nxtask_terminate() via _exit(). In that case, we must be careful to do
|
||||
* nothing that can cause the cause the thread to block.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking)
|
||||
void nxtask_exithook(FAR struct tcb_s *tcb, int status)
|
||||
{
|
||||
/* Under certain conditions, nxtask_exithook() can be called multiple
|
||||
* times. A bit in the TCB was set the first time this function was
|
||||
@ -460,22 +428,6 @@ void nxtask_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking)
|
||||
tcb->cpcount = 0;
|
||||
#endif
|
||||
|
||||
if (!nonblocking)
|
||||
{
|
||||
/* If this is the last thread in the group, then flush all streams
|
||||
* (File descriptors will be closed when the TCB is deallocated).
|
||||
*
|
||||
* NOTES:
|
||||
* 1. We cannot flush the buffered I/O if nonblocking is requested.
|
||||
* that might cause this logic to block.
|
||||
* 2. This function will only be called with non-blocking == true
|
||||
* only when called through _exit(). _exit() behavior does not
|
||||
* require that the streams be flushed
|
||||
*/
|
||||
|
||||
nxtask_flushstreams(tcb);
|
||||
}
|
||||
|
||||
/* If the task was terminated by another task, it may be in an unknown
|
||||
* state. Make some feeble effort to recover the state.
|
||||
*/
|
||||
|
@ -51,8 +51,8 @@
|
||||
* to determine if blocking is permitted or not.
|
||||
*
|
||||
* This function is the final function called all task termination
|
||||
* sequences. nxtask_terminate() is called only from task_delete() (with
|
||||
* nonblocking == false) and from nxtask_exit() (with nonblocking == true).
|
||||
* sequences. nxtask_terminate() is called only from task_delete() and
|
||||
* from nxtask_exit().
|
||||
*
|
||||
* The path through nxtask_exit() supports the final stops of the exit(),
|
||||
* _exit(), and pthread_exit
|
||||
@ -60,18 +60,12 @@
|
||||
* - pthread_exit(). Calls _exit()
|
||||
* - exit(). Calls _exit()
|
||||
* - _exit(). Calls nxtask_exit() making the currently running task
|
||||
* non-running. nxtask_exit then calls nxtask_terminate() (with nonblocking
|
||||
* == true) to terminate the non-running task.
|
||||
*
|
||||
* NOTE: that the state of non-blocking is irrelevant when called through
|
||||
* exit() and pthread_exit(). In those cases nxtask_exithook() has already
|
||||
* been called with nonblocking == false;
|
||||
* non-running. nxtask_exit then calls nxtask_terminate() to terminate
|
||||
* the non-running task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* pid - The task ID of the task to delete. A pid of zero
|
||||
* signifies the calling task.
|
||||
* nonblocking - True: The task is an unhealthy, partially torn down
|
||||
* state and is not permitted to block.
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; or ERROR on failure
|
||||
@ -81,7 +75,7 @@
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
int nxtask_terminate(pid_t pid, bool nonblocking)
|
||||
int nxtask_terminate(pid_t pid)
|
||||
{
|
||||
FAR struct tcb_s *dtcb;
|
||||
FAR dq_queue_t *tasklist;
|
||||
@ -159,18 +153,14 @@ int nxtask_terminate(pid_t pid, bool nonblocking)
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
/* Perform common task termination logic (flushing streams, calling
|
||||
* functions registered by at_exit/on_exit, etc.). We need to do
|
||||
/* Perform common task termination logic. We need to do
|
||||
* this as early as possible so that higher level clean-up logic
|
||||
* can run in a healthy tasking environment.
|
||||
*
|
||||
* In the case where the task exits via exit(), nxtask_exithook()
|
||||
* may be called twice.
|
||||
*
|
||||
* I suppose EXIT_SUCCESS is an appropriate return value???
|
||||
*/
|
||||
|
||||
nxtask_exithook(dtcb, EXIT_SUCCESS, nonblocking);
|
||||
nxtask_exithook(dtcb, EXIT_SUCCESS);
|
||||
|
||||
/* Since all tasks pass through this function as the final step in their
|
||||
* exit sequence, this is an appropriate place to inform any instrumentation
|
||||
|
Loading…
Reference in New Issue
Block a user