sched/spawn: remove spawn proxy thread to simplify task/posix_spawn()

The spawn proxy thread is a special existence in NuttX, usually some developers
spend a lot of time on stack overflow of spawn proxy thread:

https://github.com/apache/nuttx/issues/9046
https://github.com/apache/nuttx/pull/9081

In order to avoid similar issues, this PR will remove spawn proxy thread to simplify
the process of task/posix_spawn().

1. Postpone the related processing of spawn file actions until after task_init()
2. Delete the temporary thread of spawn proxy and related global variables

Signed-off-by: chao an <anchao@xiaomi.com>
This commit is contained in:
chao an 2023-04-26 11:44:08 +08:00 committed by Xiang Xiao
parent 89ae45be18
commit 507c8145a9
15 changed files with 499 additions and 606 deletions

View File

@ -62,6 +62,7 @@
* exported by the caller and made available for linking the
* module into the system.
* nexports - The number of symbols in the exports table.
* actions - The spawn file actions
* attr - The spawn attributes.
*
* Returned Value:
@ -72,7 +73,8 @@
int exec_spawn(FAR const char *filename, FAR char * const *argv,
FAR char * const *envp, FAR const struct symtab_s *exports,
int nexports, FAR const posix_spawnattr_t *attr)
int nexports, FAR const posix_spawn_file_actions_t *actions,
FAR const posix_spawnattr_t *attr)
{
FAR struct binary_s *bin;
int pid;
@ -128,7 +130,7 @@ int exec_spawn(FAR const char *filename, FAR char * const *argv,
/* Then start the module */
pid = exec_module(bin, filename, argv, envp);
pid = exec_module(bin, filename, argv, envp, actions);
if (pid < 0)
{
ret = pid;
@ -239,7 +241,7 @@ int exec(FAR const char *filename, FAR char * const *argv,
{
int ret;
ret = exec_spawn(filename, argv, envp, exports, nexports, NULL);
ret = exec_spawn(filename, argv, envp, exports, nexports, NULL, NULL);
if (ret < 0)
{
set_errno(-ret);

View File

@ -35,6 +35,7 @@
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/sched.h>
#include <nuttx/spawn.h>
#include <nuttx/binfmt/binfmt.h>
#include "binfmt.h"
@ -113,7 +114,8 @@ static void exec_ctors(FAR void *arg)
int exec_module(FAR struct binary_s *binp,
FAR const char *filename, FAR char * const *argv,
FAR char * const *envp)
FAR char * const *envp,
FAR const posix_spawn_file_actions_t *actions)
{
FAR struct task_tcb_s *tcb;
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)
@ -216,6 +218,17 @@ int exec_module(FAR struct binary_s *binp,
binfmt_freeargv(argv);
binfmt_freeenv(envp);
/* Perform file actions */
if (actions != NULL)
{
ret = spawn_file_actions(&tcb->cmn, actions);
if (ret < 0)
{
goto errout_with_tcbinit;
}
}
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_ARCH_KERNEL_STACK)
/* Allocate the kernel stack */
@ -283,7 +296,6 @@ int exec_module(FAR struct binary_s *binp,
return (int)pid;
#if defined(CONFIG_ARCH_ADDRENV) || defined(CONFIG_ARCH_VMA_MAPPING)
errout_with_tcbinit:
#ifndef CONFIG_BUILD_KERNEL
if (binp->stackaddr != NULL)
@ -294,7 +306,6 @@ errout_with_tcbinit:
nxtask_uninit(tcb);
return ret;
#endif
errout_with_addrenv:
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)

View File

@ -1750,7 +1750,8 @@ static void uart_launch_worker(void *arg)
CONFIG_TTY_LAUNCH_ENTRYPOINT,
NULL, &attr, argv, NULL);
#else
exec_spawn(CONFIG_TTY_LAUNCH_FILEPATH, argv, NULL, NULL, 0, &attr);
exec_spawn(CONFIG_TTY_LAUNCH_FILEPATH,
argv, NULL, NULL, 0, NULL, &attr);
#endif
posix_spawnattr_destroy(&attr);
}

View File

@ -185,7 +185,7 @@ void files_releaselist(FAR struct filelist *list)
}
/****************************************************************************
* Name: file_allocate
* Name: file_allocate_from_tcb
*
* Description:
* Allocate a struct files instance and associate it with an inode
@ -197,8 +197,9 @@ void files_releaselist(FAR struct filelist *list)
*
****************************************************************************/
int file_allocate(FAR struct inode *inode, int oflags, off_t pos,
FAR void *priv, int minfd, bool addref)
int file_allocate_from_tcb(FAR struct tcb_s *tcb, FAR struct inode *inode,
int oflags, off_t pos, FAR void *priv, int minfd,
bool addref)
{
FAR struct filelist *list;
int ret;
@ -207,7 +208,7 @@ int file_allocate(FAR struct inode *inode, int oflags, off_t pos,
/* Get the file descriptor list. It should not be NULL in this context. */
list = nxsched_get_files();
list = nxsched_get_files_from_tcb(tcb);
DEBUGASSERT(list != NULL);
ret = nxmutex_lock(&list->fl_lock);
@ -285,6 +286,26 @@ int file_allocate(FAR struct inode *inode, int oflags, off_t pos,
return i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
}
/****************************************************************************
* Name: file_allocate
*
* Description:
* Allocate a struct files instance and associate it with an inode
* instance.
*
* Returned Value:
* Returns the file descriptor == index into the files array on success;
* a negated errno value is returned on any failure.
*
****************************************************************************/
int file_allocate(FAR struct inode *inode, int oflags, off_t pos,
FAR void *priv, int minfd, bool addref)
{
return file_allocate_from_tcb(nxsched_self(), inode, oflags,
pos, priv, minfd, addref);
}
/****************************************************************************
* Name: files_duplist
*
@ -432,14 +453,15 @@ int fs_getfilep(int fd, FAR struct file **filep)
}
/****************************************************************************
* Name: nx_dup2
* Name: nx_dup2_from_tcb
*
* Description:
* nx_dup2() is similar to the standard 'dup2' interface except that is
* not a cancellation point and it does not modify the errno variable.
* nx_dup2_from_tcb() is similar to the standard 'dup2' interface
* except that is not a cancellation point and it does not modify the
* errno variable.
*
* nx_dup2() is an internal NuttX interface and should not be called from
* applications.
* nx_dup2_from_tcb() is an internal NuttX interface and should not be
* called from applications.
*
* Clone a file descriptor to a specific descriptor number.
*
@ -449,11 +471,11 @@ int fs_getfilep(int fd, FAR struct file **filep)
*
****************************************************************************/
int nx_dup2(int fd1, int fd2)
int nx_dup2_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2)
{
FAR struct filelist *list;
FAR struct file *filep;
FAR struct file file;
FAR struct file *filep;
FAR struct file file;
int ret;
if (fd1 == fd2)
@ -461,10 +483,9 @@ int nx_dup2(int fd1, int fd2)
return fd1;
}
/* Get the file descriptor list. It should not be NULL in this context. */
list = nxsched_get_files_from_tcb(tcb);
list = nxsched_get_files();
DEBUGASSERT(list != NULL);
/* Get the file descriptor list. It should not be NULL in this context. */
if (fd1 < 0 || fd1 >= CONFIG_NFILE_DESCRIPTORS_PER_BLOCK * list->fl_rows ||
fd2 < 0)
@ -506,6 +527,29 @@ int nx_dup2(int fd1, int fd2)
return ret < 0 ? ret : fd2;
}
/****************************************************************************
* Name: nx_dup2
*
* Description:
* nx_dup2() is similar to the standard 'dup2' interface except that is
* not a cancellation point and it does not modify the errno variable.
*
* nx_dup2() is an internal NuttX interface and should not be called from
* applications.
*
* Clone a file descriptor to a specific descriptor number.
*
* Returned Value:
* fd2 is returned on success; a negated errno value is return on
* any failure.
*
****************************************************************************/
int nx_dup2(int fd1, int fd2)
{
return nx_dup2_from_tcb(nxsched_self(), fd1, fd2);
}
/****************************************************************************
* Name: dup2
*
@ -530,14 +574,15 @@ int dup2(int fd1, int fd2)
}
/****************************************************************************
* Name: nx_close
* Name: nx_close_from_tcb
*
* Description:
* nx_close() is similar to the standard 'close' interface except that is
* not a cancellation point and it does not modify the errno variable.
* nx_close_from_tcb() is similar to the standard 'close' interface
* except that is not a cancellation point and it does not modify the
* errno variable.
*
* nx_close() is an internal NuttX interface and should not be called from
* applications.
* nx_close_from_tcb() is an internal NuttX interface and should not
* be called from applications.
*
* Close an inode (if open)
*
@ -551,19 +596,14 @@ int dup2(int fd1, int fd2)
*
****************************************************************************/
int nx_close(int fd)
int nx_close_from_tcb(FAR struct tcb_s *tcb, int fd)
{
FAR struct filelist *list;
FAR struct file *filep;
FAR struct file file;
FAR struct filelist *list;
int ret;
/* Get the thread-specific file list. It should never be NULL in this
* context.
*/
list = nxsched_get_files();
DEBUGASSERT(list != NULL);
list = nxsched_get_files_from_tcb(tcb);
/* Perform the protected close operation */
@ -592,6 +632,33 @@ int nx_close(int fd)
return file_close(&file);
}
/****************************************************************************
* Name: nx_close
*
* Description:
* nx_close() is similar to the standard 'close' interface except that is
* not a cancellation point and it does not modify the errno variable.
*
* nx_close() is an internal NuttX interface and should not be called from
* applications.
*
* Close an inode (if open)
*
* Returned Value:
* Zero (OK) is returned on success; A negated errno value is returned on
* on any failure.
*
* Assumptions:
* Caller holds the list mutex because the file descriptor will be
* freed.
*
****************************************************************************/
int nx_close(int fd)
{
return nx_close_from_tcb(nxsched_self(), fd);
}
/****************************************************************************
* Name: close
*

View File

@ -224,6 +224,7 @@ errout_with_search:
* applications.
*
* Input Parameters:
* tcb - Address of the task's TCB
* path - The full path to the file to be opened.
* oflags - open flags.
* ap - Variable argument list, may include 'mode_t mode'
@ -234,7 +235,8 @@ errout_with_search:
*
****************************************************************************/
static int nx_vopen(FAR const char *path, int oflags, va_list ap)
static int nx_vopen(FAR struct tcb_s *tcb,
FAR const char *path, int oflags, va_list ap)
{
struct file filep;
int ret;
@ -250,8 +252,8 @@ static int nx_vopen(FAR const char *path, int oflags, va_list ap)
/* Allocate a new file descriptor for the inode */
fd = file_allocate(filep.f_inode, filep.f_oflags,
filep.f_pos, filep.f_priv, 0, false);
fd = file_allocate_from_tcb(tcb, filep.f_inode, filep.f_oflags,
filep.f_pos, filep.f_priv, 0, false);
if (fd < 0)
{
file_close(&filep);
@ -341,6 +343,44 @@ int file_open(FAR struct file *filep, FAR const char *path, int oflags, ...)
return ret;
}
/****************************************************************************
* Name: nx_open_from_tcb
*
* Description:
* nx_open_from_tcb() is similar to the standard 'open' interface except
* that it is not a cancellation point and it does not modify the errno
* variable.
*
* nx_open_from_tcb() is an internal NuttX interface and should not be
* called from applications.
*
* Input Parameters:
* tcb - Address of the task's TCB
* path - The full path to the file to be opened.
* oflags - open flags.
* ... - Variable number of arguments, may include 'mode_t mode'
*
* Returned Value:
* The new file descriptor is returned on success; a negated errno value is
* returned on any failure.
*
****************************************************************************/
int nx_open_from_tcb(FAR struct tcb_s *tcb,
FAR const char *path, int oflags, ...)
{
va_list ap;
int fd;
/* Let nx_vopen() do all of the work */
va_start(ap, oflags);
fd = nx_vopen(tcb, path, oflags, ap);
va_end(ap);
return fd;
}
/****************************************************************************
* Name: nx_open
*
@ -370,7 +410,7 @@ int nx_open(FAR const char *path, int oflags, ...)
/* Let nx_vopen() do all of the work */
va_start(ap, oflags);
fd = nx_vopen(path, oflags, ap);
fd = nx_vopen(nxsched_self(), path, oflags, ap);
va_end(ap);
return fd;
@ -400,7 +440,7 @@ int open(FAR const char *path, int oflags, ...)
/* Let nx_vopen() do most of the work */
va_start(ap, oflags);
fd = nx_vopen(path, oflags, ap);
fd = nx_vopen(nxsched_self(), path, oflags, ap);
va_end(ap);
/* Set the errno value if any errors were reported by nx_open() */

View File

@ -264,7 +264,8 @@ int unload_module(FAR struct binary_s *bin);
int exec_module(FAR struct binary_s *binp,
FAR const char *filename, FAR char * const *argv,
FAR char * const *envp);
FAR char * const *envp,
FAR const posix_spawn_file_actions_t *actions);
/****************************************************************************
* Name: exec
@ -355,6 +356,7 @@ int exec(FAR const char *filename, FAR char * const *argv,
* exported by the caller and made available for linking the
* module into the system.
* nexports - The number of symbols in the exports table.
* actions - The spawn file actions
* attr - The spawn attributes.
*
* Returned Value:
@ -366,7 +368,8 @@ int exec(FAR const char *filename, FAR char * const *argv,
int exec_spawn(FAR const char *filename, FAR char * const *argv,
FAR char * const *envp, FAR const struct symtab_s *exports,
int nexports, FAR const posix_spawnattr_t *attr);
int nexports, FAR const posix_spawn_file_actions_t *actions,
FAR const posix_spawnattr_t *attr);
/****************************************************************************
* Name: binfmt_exit

View File

@ -170,6 +170,7 @@ struct stat;
struct statfs;
struct pollfd;
struct mtd_dev_s;
struct tcb_s;
/* The internal representation of type DIR is just a container for an inode
* reference, and the path of directory.
@ -785,6 +786,23 @@ void files_releaselist(FAR struct filelist *list);
int files_duplist(FAR struct filelist *plist, FAR struct filelist *clist);
/****************************************************************************
* Name: file_allocate_from_tcb
*
* Description:
* Allocate a struct files instance and associate it with an inode
* instance.
*
* Returned Value:
* Returns the file descriptor == index into the files array on success;
* a negated errno value is returned on any failure.
*
****************************************************************************/
int file_allocate_from_tcb(FAR struct tcb_s *tcb, FAR struct inode *inode,
int oflags, off_t pos, FAR void *priv, int minfd,
bool addref);
/****************************************************************************
* Name: file_allocate
*
@ -834,6 +852,27 @@ int file_dup(FAR struct file *filep, int minfd, bool cloexec);
int file_dup2(FAR struct file *filep1, FAR struct file *filep2);
/****************************************************************************
* Name: nx_dup2_from_tcb
*
* Description:
* nx_dup2_from_tcb() is similar to the standard 'dup2' interface
* except that is not a cancellation point and it does not modify the
* errno variable.
*
* nx_dup2_from_tcb() is an internal NuttX interface and should not be
* called from applications.
*
* Clone a file descriptor to a specific descriptor number.
*
* Returned Value:
* fd2 is returned on success; a negated errno value is return on
* any failure.
*
****************************************************************************/
int nx_dup2_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2);
/****************************************************************************
* Name: nx_dup2
*
@ -875,6 +914,32 @@ int nx_dup2(int fd1, int fd2);
int file_open(FAR struct file *filep, FAR const char *path, int oflags, ...);
/****************************************************************************
* Name: nx_open_from_tcb
*
* Description:
* nx_open_from_tcb() is similar to the standard 'open' interface except
* that it is not a cancellation point and it does not modify the errno
* variable.
*
* nx_open_from_tcb() is an internal NuttX interface and should not be
* called from applications.
*
* Input Parameters:
* tcb - Address of the task's TCB
* path - The full path to the file to be opened.
* oflags - open flags.
* ... - Variable number of arguments, may include 'mode_t mode'
*
* Returned Value:
* The new file descriptor is returned on success; a negated errno value is
* returned on any failure.
*
****************************************************************************/
int nx_open_from_tcb(FAR struct tcb_s *tcb,
FAR const char *path, int oflags, ...);
/****************************************************************************
* Name: nx_open
*
@ -931,6 +996,31 @@ int fs_getfilep(int fd, FAR struct file **filep);
int file_close(FAR struct file *filep);
/****************************************************************************
* Name: nx_close_from_tcb
*
* Description:
* nx_close_from_tcb() is similar to the standard 'close' interface
* except that is not a cancellation point and it does not modify the
* errno variable.
*
* nx_close_from_tcb() is an internal NuttX interface and should not
* be called from applications.
*
* Close an inode (if open)
*
* Returned Value:
* Zero (OK) is returned on success; A negated errno value is returned on
* on any failure.
*
* Assumptions:
* Caller holds the list mutex because the file descriptor will be
* freed.
*
****************************************************************************/
int nx_close_from_tcb(FAR struct tcb_s *tcb, int fd);
/****************************************************************************
* Name: nx_close
*
@ -1043,7 +1133,6 @@ int close_mtddriver(FAR struct inode *pinode);
****************************************************************************/
#ifdef CONFIG_FILE_STREAM
struct tcb_s; /* Forward reference */
int fs_fdopen(int fd, int oflags, FAR struct tcb_s *tcb,
FAR struct file_struct **filep);
#endif

View File

@ -847,6 +847,40 @@ int nxsched_release_tcb(FAR struct tcb_s *tcb, uint8_t ttype);
* the currently executing task.
*/
/****************************************************************************
* Name: nxsched_get_files_from_tcb
*
* Description:
* Return a pointer to the file list from task context
*
* Input Parameters:
* tcb - Address of the new task's TCB
*
* Returned Value:
* A pointer to the errno.
*
* Assumptions:
*
****************************************************************************/
FAR struct filelist *nxsched_get_files_from_tcb(FAR struct tcb_s *tcb);
/****************************************************************************
* Name: nxsched_get_files
*
* Description:
* Return a pointer to the file list for this thread
*
* Input Parameters:
* None
*
* Returned Value:
* A pointer to the errno.
*
* Assumptions:
*
****************************************************************************/
FAR struct filelist *nxsched_get_files(void);
/****************************************************************************

View File

@ -100,6 +100,9 @@ extern "C"
void add_file_action(FAR posix_spawn_file_actions_t *file_action,
FAR struct spawn_general_file_action_s *entry);
int spawn_file_actions(FAR struct tcb_s *tcb,
FAR const posix_spawn_file_actions_t *actions);
#ifdef __cplusplus
}
#endif

View File

@ -295,7 +295,7 @@ static inline void nx_start_application(void)
attr.stacksize = CONFIG_INIT_STACKSIZE;
ret = exec_spawn(CONFIG_INIT_FILEPATH, argv, NULL,
CONFIG_INIT_SYMTAB, CONFIG_INIT_NEXPORTS, &attr);
CONFIG_INIT_SYMTAB, CONFIG_INIT_NEXPORTS, NULL, &attr);
#endif
posix_spawnattr_destroy(&attr);
DEBUGASSERT(ret > 0);

View File

@ -30,6 +30,42 @@
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxsched_get_files_from_tcb
*
* Description:
* Return a pointer to the file list from task context
*
* Input Parameters:
* tcb - Address of the new task's TCB
*
* Returned Value:
* A pointer to the errno.
*
* Assumptions:
*
****************************************************************************/
FAR struct filelist *nxsched_get_files_from_tcb(FAR struct tcb_s *tcb)
{
FAR struct task_group_s *group = tcb->group;
/* The group may be NULL under certain conditions. For example, if
* debug output is attempted from the IDLE thead before the group has
* been allocated. I have only seen this case when memory management
* debug is enabled.
*/
if (group)
{
return &group->tg_filelist;
}
/* Higher level logic must handle the NULL gracefully */
return NULL;
}
/****************************************************************************
* Name: nxsched_get_files
*
@ -48,21 +84,5 @@
FAR struct filelist *nxsched_get_files(void)
{
FAR struct tcb_s *rtcb = this_task();
FAR struct task_group_s *group = rtcb->group;
/* The group may be NULL under certain conditions. For example, if
* debug output is attempted from the IDLE thead before the group has
* been allocated. I have only seen this case when memory management
* debug is enabled.
*/
if (group)
{
return &group->tg_filelist;
}
/* Higher level logic must handle the NULL gracefully */
return NULL;
return nxsched_get_files_from_tcb(this_task());
}

View File

@ -29,55 +29,6 @@
#include <nuttx/mutex.h>
#include <spawn.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_POSIX_SPAWN_PROXY_STACKSIZE
# define CONFIG_POSIX_SPAWN_PROXY_STACKSIZE 1024
#endif
/****************************************************************************
* Public Type Definitions
****************************************************************************/
struct spawn_parms_s
{
/* Common parameters */
int result;
FAR pid_t *pid;
FAR const posix_spawn_file_actions_t *file_actions;
FAR const posix_spawnattr_t *attr;
FAR char * const *argv;
FAR char * const *envp;
/* Parameters that differ for posix_spawn[p] and task_spawn */
union
{
struct
{
FAR const char *path;
} posix;
struct
{
FAR const char *name;
main_t entry;
} task;
} u;
};
/****************************************************************************
* Public Data
****************************************************************************/
extern mutex_t g_spawn_parmlock;
#ifndef CONFIG_SCHED_WAITPID
extern sem_t g_spawn_execsem;
#endif
extern struct spawn_parms_s g_spawn_parms;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -116,16 +67,10 @@ int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr);
*
* Input Parameters:
*
* pid - The pid of the new task.
* attr - The attributes to use
* file_actions - The attributes to use
*
* Returned Value:
* 0 (OK) on success; A negated errno value is returned on failure.
*
****************************************************************************/
int spawn_proxyattrs(FAR const posix_spawnattr_t *attr,
FAR const posix_spawn_file_actions_t *file_actions);
void spawn_proxyattrs(FAR const posix_spawnattr_t *attr);
#endif /* __SCHED_TASK_SPAWN_H */

View File

@ -59,6 +59,8 @@
* CONFIG_LIBC_ENVPATH is defined, this may be either a relative or
* or an absolute path. Otherwise, it must be an absolute path.
*
* actions - The spawn file actions
*
* attr - If the value of the 'attr' parameter is NULL, the all default
* values for the POSIX spawn attributes will be used. Otherwise, the
* attributes will be set according to the spawn flags. The
@ -84,6 +86,7 @@
****************************************************************************/
static int nxposix_spawn_exec(FAR pid_t *pidp, FAR const char *path,
FAR const posix_spawn_file_actions_t *actions,
FAR const posix_spawnattr_t *attr,
FAR char * const argv[],
FAR char * const envp[])
@ -108,7 +111,7 @@ static int nxposix_spawn_exec(FAR pid_t *pidp, FAR const char *path,
/* Start the task */
pid = exec_spawn(path, argv, envp, symtab, nsymbols, attr);
pid = exec_spawn(path, argv, envp, symtab, nsymbols, actions, attr);
if (pid < 0)
{
ret = -pid;
@ -140,81 +143,6 @@ errout:
return ret;
}
/****************************************************************************
* Name: nxposix_spawn_proxy
*
* Description:
* Perform file_actions, then execute the task from the file system.
*
* Do we really need this proxy task? Isn't that wasteful?
*
* Q: Why not use a starthook so that there is callout from nxtask_start()
* to perform these operations after the file is loaded from
* the file system?
* A: That existing nxtask_starthook() implementation cannot be used in
* this context; any of nxtask_starthook() will also conflict with
* binfmt's use of the start hook to call C++ static initializers.
* task_restart() would also be an issue.
*
* Input Parameters:
* Standard task start-up parameters
*
* Returned Value:
* Standard task return value.
*
****************************************************************************/
static int nxposix_spawn_proxy(int argc, FAR char *argv[])
{
int ret;
/* Perform file actions and/or set a custom signal mask. We get here only
* if the file_actions parameter to posix_spawn[p] was non-NULL and/or the
* option to change the signal mask was selected.
*/
DEBUGASSERT(g_spawn_parms.file_actions ||
(g_spawn_parms.attr &&
(g_spawn_parms.attr->flags & POSIX_SPAWN_SETSIGMASK) != 0));
/* Set the attributes and perform the file actions as appropriate */
ret = spawn_proxyattrs(g_spawn_parms.attr, g_spawn_parms.file_actions);
if (ret == OK)
{
/* Start the task */
ret = nxposix_spawn_exec(g_spawn_parms.pid, g_spawn_parms.u.posix.path,
g_spawn_parms.attr, g_spawn_parms.argv,
g_spawn_parms.envp);
#ifdef CONFIG_SCHED_HAVE_PARENT
if (ret == OK)
{
/* Change of the parent of the task we just spawned to our parent.
* What should we do in the event of a failure?
*/
int tmp = task_reparent(0, *g_spawn_parms.pid);
if (tmp < 0)
{
serr("ERROR: task_reparent() failed: %d\n", tmp);
}
}
#endif
}
/* Post the semaphore to inform the parent task that we have completed
* what we need to do.
*/
g_spawn_parms.result = ret;
#ifndef CONFIG_SCHED_WAITPID
nxsem_post(&g_spawn_execsem);
#endif
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -311,125 +239,15 @@ int posix_spawn(FAR pid_t *pid, FAR const char *path,
FAR const posix_spawnattr_t *attr,
FAR char * const argv[], FAR char * const envp[])
{
struct sched_param param;
int proxy;
#ifdef CONFIG_SCHED_WAITPID
int status;
#endif
int ret;
DEBUGASSERT(path);
sinfo("pid=%p path=%s file_actions=%p attr=%p argv=%p\n",
pid, path, file_actions, attr, argv);
/* If there are no file actions to be performed and there is no change to
* the signal mask, then start the new child task directly from the parent
* task.
*/
if ((file_actions == NULL || *file_actions == NULL) &&
(attr == NULL || (attr->flags & POSIX_SPAWN_SETSIGMASK) == 0))
if (attr != NULL)
{
return nxposix_spawn_exec(pid, path, attr, argv, envp);
spawn_proxyattrs(attr);
}
/* Otherwise, we will have to go through an intermediary/proxy task in
* order to perform the I/O redirection. This would be a natural place
* to fork(). However, true fork() behavior requires an MMU and most
* implementations of vfork() are not capable of these operations.
*
* Even without fork(), we can still do the job, but parameter passing is
* messier. Unfortunately, there is no (clean) way to pass binary values
* as a task parameter, so we will use a semaphore-protected global
* structure.
*/
/* Get exclusive access to the global parameter structure */
ret = nxmutex_lock(&g_spawn_parmlock);
if (ret < 0)
{
serr("ERROR: nxmutex_lock failed: %d\n", ret);
return -ret;
}
/* Populate the parameter structure */
g_spawn_parms.result = ENOSYS;
g_spawn_parms.pid = pid;
g_spawn_parms.file_actions = file_actions ? *file_actions : NULL;
g_spawn_parms.attr = attr;
g_spawn_parms.argv = argv;
g_spawn_parms.envp = envp;
g_spawn_parms.u.posix.path = path;
/* Get the priority of this (parent) task */
ret = nxsched_get_param(0, &param);
if (ret < 0)
{
serr("ERROR: nxsched_get_param failed: %d\n", ret);
nxmutex_unlock(&g_spawn_parmlock);
return -ret;
}
/* Disable pre-emption so that the proxy does not run until waitpid
* is called. This is probably unnecessary since the nxposix_spawn_proxy
* has the same priority as this thread; it should be schedule behind
* this task in the ready-to-run list.
*/
#ifdef CONFIG_SCHED_WAITPID
sched_lock();
#endif
/* Start the intermediary/proxy task at the same priority as the parent
* task.
*/
proxy = nxthread_create("nxposix_spawn_proxy", TCB_FLAG_TTYPE_KERNEL,
param.sched_priority, NULL,
CONFIG_POSIX_SPAWN_PROXY_STACKSIZE,
nxposix_spawn_proxy, NULL, environ);
if (proxy < 0)
{
ret = -proxy;
serr("ERROR: Failed to start nxposix_spawn_proxy: %d\n", ret);
goto errout_with_lock;
}
/* Wait for the proxy to complete its job */
#ifdef CONFIG_SCHED_WAITPID
/* REVISIT: This should not call waitpid() directly. waitpid is a
* cancellation point and modifies the errno value. It is inappropriate
* for use within the OS.
*/
ret = nxsched_waitpid((pid_t)proxy, &status, 0);
if (ret < 0)
{
serr("ERROR: waitpid() failed: %d\n", ret);
goto errout_with_lock;
}
#else
ret = nxsem_wait_uninterruptible(&g_spawn_execsem);
if (ret < 0)
{
serr("ERROR: nxsem_wait_uninterruptible() failed: %d\n", ret);
goto errout_with_lock;
}
#endif
/* Get the result and relinquish our access to the parameter structure */
ret = g_spawn_parms.result;
errout_with_lock:
#ifdef CONFIG_SCHED_WAITPID
sched_unlock();
#endif
nxmutex_unlock(&g_spawn_parmlock);
return ret;
return nxposix_spawn_exec(pid, path,
file_actions != NULL ?
*file_actions : NULL, attr, argv, envp);
}

View File

@ -46,6 +46,92 @@
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxtask_spawn_create
*
* Description:
* This function creates and activates a new thread of the specified type
* with a specified priority and returns its system-assigned ID. It is the
* internal, common implementation of task_create() and kthread_create().
* See comments with task_create() for further information.
*
* Input Parameters:
* name - Name of the new task
* ttype - Type of the new task
* priority - Priority of the new task
* stack_addr - Address of the stack needed
* stack_size - Size (in bytes) of the stack needed
* entry - Entry point of a new task
* arg - A pointer to an array of input parameters. The array
* should be terminated with a NULL argv[] value. If no
* parameters are required, argv may be NULL.
* envp - A pointer to an array of environment strings. Terminated
* with a NULL entry.
* actions - The spawn file actions
*
* Returned Value:
* Returns the positive, non-zero process ID of the new task or a negated
* errno value to indicate the nature of any failure. If memory is
* insufficient or the task cannot be created -ENOMEM will be returned.
*
****************************************************************************/
static int nxtask_spawn_create(FAR const char *name, int priority,
FAR void *stack_addr, int stack_size,
main_t entry, FAR char * const argv[],
FAR char * const envp[],
FAR const posix_spawn_file_actions_t *actions)
{
FAR struct task_tcb_s *tcb;
pid_t pid;
int ret;
/* Allocate a TCB for the new task. */
tcb = (FAR struct task_tcb_s *)kmm_zalloc(sizeof(struct task_tcb_s));
if (tcb == NULL)
{
serr("ERROR: Failed to allocate TCB\n");
return -ENOMEM;
}
/* Setup the task type */
tcb->cmn.flags = TCB_FLAG_TTYPE_TASK;
/* Initialize the task */
ret = nxtask_init(tcb, name, priority, stack_addr, stack_size,
entry, argv, envp);
if (ret < OK)
{
kmm_free(tcb);
return ret;
}
/* Perform file actions */
if (actions != NULL)
{
ret = spawn_file_actions(&tcb->cmn, actions);
if (ret < 0)
{
nxtask_uninit(tcb);
return ret;
}
}
/* Get the assigned pid before we start the task */
pid = tcb->cmn.pid;
/* Activate the task */
nxtask_activate(&tcb->cmn);
return (int)pid;
}
/****************************************************************************
* Name: nxtask_spawn_exec
*
@ -61,6 +147,8 @@
*
* entry - The child task's entry point (an address in memory)
*
* actions - The spawn file actions
*
* attr - If the value of the 'attr' parameter is NULL, the all default
* values for the POSIX spawn attributes will be used. Otherwise, the
* attributes will be set according to the spawn flags. The
@ -89,7 +177,9 @@
****************************************************************************/
static int nxtask_spawn_exec(FAR pid_t *pidp, FAR const char *name,
main_t entry, FAR const posix_spawnattr_t *attr,
main_t entry,
FAR const posix_spawn_file_actions_t *actions,
FAR const posix_spawnattr_t *attr,
FAR char * const *argv, FAR char * const envp[])
{
FAR void *stackaddr = NULL;
@ -131,12 +221,13 @@ static int nxtask_spawn_exec(FAR pid_t *pidp, FAR const char *name,
/* Start the task */
pid = nxtask_create(name, priority, stackaddr,
stacksize, entry, argv, envp);
pid = nxtask_spawn_create(name, priority, stackaddr,
stacksize, entry, argv,
envp ? envp : environ, actions);
if (pid < 0)
{
ret = pid;
serr("ERROR: nxtask_create failed: %d\n", ret);
serr("ERROR: nxtask_spawn_create failed: %d\n", ret);
goto errout;
}
@ -164,92 +255,6 @@ errout:
return ret;
}
/****************************************************************************
* Name: nxtask_spawn_proxy
*
* Description:
* Perform file_actions, then execute the task from the file system.
*
* Do we really need a proxy task in this case? Isn't that wasteful?
*
* Q: Why can we do what we need to do here and the just call the
* new task's entry point.
* A: This would require setting up the name, priority, and stacksize from
* the task_spawn, but it do-able. The only issue I can think of is
* that NuttX supports task_restart(), and you would never be able to
* restart a task from this point.
*
* Q: Why not use a starthook so that there is callout from nxtask_start()
* to perform these operations?
* A: Good idea, except that existing nxtask_starthook() implementation
* cannot be used here unless we get rid of task_create and, instead,
* use nxtask_init() and nxtask_activate(). start_taskhook() could then
* be called between nxtask_init() and nxtask_activate().
* task_restart() would still be an issue.
*
* Input Parameters:
* argc, argv - Ignored. The task's start-up parameters are passed via the
* semaphore-protected global structure g_spawn_parms.
*
* Returned Value:
* Standard task return value.
*
****************************************************************************/
static int nxtask_spawn_proxy(int argc, FAR char *argv[])
{
int ret;
/* Perform file actions and/or set a custom signal mask. We get here only
* if the file_actions parameter to task_spawn[p] was non-NULL and/or the
* option to change the signal mask was selected.
*/
UNUSED(argc);
UNUSED(argv);
DEBUGASSERT(g_spawn_parms.file_actions ||
(g_spawn_parms.attr &&
(g_spawn_parms.attr->flags & POSIX_SPAWN_SETSIGMASK) != 0));
/* Set the attributes and perform the file actions as appropriate */
ret = spawn_proxyattrs(g_spawn_parms.attr, g_spawn_parms.file_actions);
if (ret == OK)
{
/* Start the task */
ret = nxtask_spawn_exec(g_spawn_parms.pid, g_spawn_parms.u.task.name,
g_spawn_parms.u.task.entry, g_spawn_parms.attr,
g_spawn_parms.argv, g_spawn_parms.envp);
#ifdef CONFIG_SCHED_HAVE_PARENT
if (ret == OK)
{
/* Change of the parent of the task we just spawned to our parent.
* What should we do in the event of a failure?
*/
int tmp = task_reparent(0, *g_spawn_parms.pid);
if (tmp < 0)
{
serr("ERROR: task_reparent() failed: %d\n", tmp);
}
}
#endif
}
/* Post the semaphore to inform the parent task that we have completed
* what we need to do.
*/
g_spawn_parms.result = ret;
#ifndef CONFIG_SCHED_WAITPID
nxsem_post(&g_spawn_execsem);
#endif
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -321,142 +326,22 @@ int task_spawn(FAR const char *name, main_t entry,
FAR const posix_spawnattr_t *attr,
FAR char * const argv[], FAR char * const envp[])
{
struct sched_param param;
pid_t proxy;
pid_t pid = INVALID_PROCESS_ID;
#ifdef CONFIG_SCHED_WAITPID
int status;
#endif
int ret;
sinfo("name=%s entry=%p file_actions=%p attr=%p argv=%p\n",
name, entry, file_actions, attr, argv);
/* If there are no file actions to be performed and there is no change to
* the signal mask, then start the new child task directly from the parent
* task.
*/
if ((file_actions == NULL || *file_actions == NULL) &&
(attr == NULL || (attr->flags & POSIX_SPAWN_SETSIGMASK) == 0))
if (attr != NULL)
{
ret = nxtask_spawn_exec(&pid, name, entry, attr, argv, envp);
if (ret < 0)
{
return ret;
}
return pid;
spawn_proxyattrs(attr);
}
/* Otherwise, we will have to go through an intermediary/proxy task in
* order to perform the I/O redirection. This would be a natural place to
* fork(). However, true fork() behavior requires an MMU and most
* implementations of vfork() are not capable of these operations.
*
* Even without fork(), we can still do the job, but parameter passing is
* messier. Unfortunately, there is no (clean) way to pass binary values
* as a task parameter, so we will use a semaphore-protected global
* structure.
*/
ret = nxtask_spawn_exec(&pid, name, entry,
file_actions != NULL ? *file_actions : NULL,
attr, argv, envp);
/* Get exclusive access to the global parameter structure */
ret = nxmutex_lock(&g_spawn_parmlock);
if (ret < 0)
{
serr("ERROR: nxmutex_lock failed: %d\n", ret);
return ret;
}
/* Populate the parameter structure */
g_spawn_parms.result = ENOSYS;
g_spawn_parms.pid = &pid;
g_spawn_parms.file_actions = file_actions ? *file_actions : NULL;
g_spawn_parms.attr = attr;
g_spawn_parms.argv = argv;
g_spawn_parms.envp = envp;
g_spawn_parms.u.task.name = name;
g_spawn_parms.u.task.entry = entry;
/* Get the priority of this (parent) task */
ret = nxsched_get_param(0, &param);
if (ret < 0)
{
serr("ERROR: nxsched_get_param failed: %d\n", ret);
g_spawn_parms.pid = NULL;
nxmutex_unlock(&g_spawn_parmlock);
return ret;
}
#ifdef CONFIG_SCHED_WAITPID
/* Disable pre-emption so that the proxy does not run until waitpid
* is called. This is probably unnecessary since the nxtask_spawn_proxy
* has the same priority as this thread; it should be schedule behind
* this task in the ready-to-run list.
*
* REVISIT: This will may not have the desired effect in SMP mode.
*/
sched_lock();
#endif
/* Start the intermediary/proxy task at the same priority as the parent
* task.
*/
proxy = nxtask_create("nxtask_spawn_proxy", param.sched_priority,
NULL, CONFIG_POSIX_SPAWN_PROXY_STACKSIZE,
nxtask_spawn_proxy, NULL, NULL);
if (proxy < 0)
{
ret = proxy;
serr("ERROR: Failed to start nxtask_spawn_proxy: %d\n", ret);
goto errout_with_lock;
}
/* Wait for the proxy to complete its job */
#ifdef CONFIG_SCHED_WAITPID
/* REVISIT: This should not call waitpid() directly. waitpid is a
* cancellation point and modifies the errno value. It is inappropriate
* for use within the OS.
*/
ret = nxsched_waitpid(proxy, &status, 0);
if (ret < 0)
{
serr("ERROR: waitpid() failed: %d\n", ret);
goto errout_with_lock;
}
#else
ret = nxsem_wait_uninterruptible(&g_spawn_execsem);
if (ret < 0)
{
serr("ERROR: g_spawn_execsem() failed: %d\n", ret);
goto errout_with_lock;
}
#endif
/* Get the result and relinquish our access to the parameter structure */
ret = -g_spawn_parms.result;
if (ret < 0)
{
goto errout_with_lock;
}
ret = (int)pid;
errout_with_lock:
#ifdef CONFIG_SCHED_WAITPID
sched_unlock();
#endif
g_spawn_parms.pid = NULL;
nxmutex_unlock(&g_spawn_parmlock);
return ret;
return ret >= 0 ? (int)pid : ret;
}
#endif /* CONFIG_BUILD_KERNEL */

View File

@ -38,16 +38,6 @@
#include "task/spawn.h"
#include "task/task.h"
/****************************************************************************
* Public Data
****************************************************************************/
mutex_t g_spawn_parmlock = NXMUTEX_INITIALIZER;
#ifndef CONFIG_SCHED_WAITPID
sem_t g_spawn_execsem = SEM_INITIALIZER(0);
#endif
struct spawn_parms_s g_spawn_parms;
/****************************************************************************
* Private Functions
****************************************************************************/
@ -68,73 +58,53 @@ struct spawn_parms_s g_spawn_parms;
*
****************************************************************************/
static inline int nxspawn_close(FAR struct spawn_close_file_action_s *action)
static inline int nxspawn_close(FAR struct tcb_s *tcb,
FAR struct spawn_close_file_action_s *action)
{
/* The return value from nx_close() is ignored */
sinfo("Closing fd=%d\n", action->fd);
nx_close(action->fd);
return OK;
return nx_close_from_tcb(tcb, action->fd);
}
static inline int nxspawn_dup2(FAR struct spawn_dup2_file_action_s *action)
static inline int nxspawn_dup2(FAR struct tcb_s *tcb,
FAR struct spawn_dup2_file_action_s *action)
{
int ret;
/* Perform the dup */
sinfo("Dup'ing %d->%d\n", action->fd1, action->fd2);
ret = nx_dup2(action->fd1, action->fd2);
if (ret < 0)
{
serr("ERROR: dup2 failed: %d\n", ret);
return ret;
}
return OK;
return nx_dup2_from_tcb(tcb, action->fd1, action->fd2);
}
static inline int nxspawn_open(FAR struct spawn_open_file_action_s *action)
static inline int nxspawn_open(FAR struct tcb_s *tcb,
FAR struct spawn_open_file_action_s *action)
{
int fd;
int ret = OK;
int fd;
/* Open the file */
sinfo("Open'ing path=%s oflags=%04x mode=%04x\n",
action->path, action->oflags, action->mode);
fd = nx_open(action->path, action->oflags, action->mode);
nx_close_from_tcb(tcb, action->fd);
fd = nx_open_from_tcb(tcb, action->path, action->oflags, action->mode);
if (fd < 0)
{
ret = fd;
serr("ERROR: open failed: %d\n", ret);
}
/* Does the return file descriptor happen to match the required file
* descriptor number?
*/
else if (fd != action->fd)
{
/* No.. dup2 to get the correct file number */
sinfo("Dup'ing %d->%d\n", fd, action->fd);
ret = nx_dup2(fd, action->fd);
if (ret < 0)
{
serr("ERROR: dup2 failed: %d\n", ret);
}
else
ret = nx_dup2_from_tcb(tcb, fd, action->fd);
if (ret >= 0)
{
ret = OK;
}
sinfo("Closing fd=%d\n", fd);
nx_close(fd);
nx_close_from_tcb(tcb, fd);
}
return ret;
@ -266,61 +236,66 @@ int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr)
*
* Input Parameters:
*
* pid - The pid of the new task.
* attr - The attributes to use
* file_actions - The attributes to use
*
* Returned Value:
* 0 (OK) on success; A negated errno value is returned on failure.
*
****************************************************************************/
int spawn_proxyattrs(FAR const posix_spawnattr_t *attr,
FAR const posix_spawn_file_actions_t *file_actions)
void spawn_proxyattrs(FAR const posix_spawnattr_t *attr)
{
FAR struct spawn_general_file_action_s *entry;
int ret = OK;
/* Check if we need to change the signal mask */
if (attr != NULL && (attr->flags & POSIX_SPAWN_SETSIGMASK) != 0)
{
nxsig_procmask(SIG_SETMASK, &attr->sigmask, NULL);
}
}
/* Were we also requested to perform file actions? */
/****************************************************************************
* Name: spawn_file_actions
*
* Description:
* Perform Spawn file object that specifies file-related actions
*
* Input Parameters:
*
* attr - The spawn file actions
*
* Returned Value:
* 0 (OK) on success; A negated errno value is returned on failure.
*
****************************************************************************/
if (file_actions != NULL)
int spawn_file_actions(FAR struct tcb_s *tcb,
FAR const posix_spawn_file_actions_t *actions)
{
FAR struct spawn_general_file_action_s *entry;
int ret = OK;
/* Execute each file action */
for (entry = (FAR struct spawn_general_file_action_s *)actions;
entry && ret == OK;
entry = entry->flink)
{
/* Yes.. Execute each file action */
for (entry = (FAR struct spawn_general_file_action_s *)file_actions;
entry && ret == OK;
entry = entry->flink)
switch (entry->action)
{
switch (entry->action)
{
case SPAWN_FILE_ACTION_CLOSE:
ret = nxspawn_close((FAR struct spawn_close_file_action_s *)
entry);
break;
case SPAWN_FILE_ACTION_CLOSE:
ret = nxspawn_close(tcb, (FAR void *)entry);
break;
case SPAWN_FILE_ACTION_DUP2:
ret = nxspawn_dup2((FAR struct spawn_dup2_file_action_s *)
entry);
break;
case SPAWN_FILE_ACTION_DUP2:
ret = nxspawn_dup2(tcb, (FAR void *)entry);
break;
case SPAWN_FILE_ACTION_OPEN:
ret = nxspawn_open((FAR struct spawn_open_file_action_s *)
entry);
break;
case SPAWN_FILE_ACTION_OPEN:
ret = nxspawn_open(tcb, (FAR void *)entry);
break;
case SPAWN_FILE_ACTION_NONE:
default:
serr("ERROR: Unknown action: %d\n", entry->action);
ret = EINVAL;
break;
}
case SPAWN_FILE_ACTION_NONE:
default:
serr("ERROR: Unknown action: %d\n", entry->action);
ret = -EINVAL;
break;
}
}