forked from xuos/xiuos
modify nuttx bug of lack of files
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
############################################################################
|
||||
# sched/task/Make.defs
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
CSRCS += task_create.c task_init.c task_setup.c task_activate.c
|
||||
CSRCS += task_start.c task_delete.c task_exit.c task_exithook.c
|
||||
CSRCS += task_getgroup.c task_getpid.c task_prctl.c task_recover.c
|
||||
CSRCS += task_restart.c task_spawnparms.c task_setcancelstate.c
|
||||
CSRCS += task_cancelpt.c task_terminate.c task_gettid.c exit.c
|
||||
|
||||
ifeq ($(CONFIG_SCHED_HAVE_PARENT),y)
|
||||
CSRCS += task_getppid.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_HAVE_VFORK),y)
|
||||
ifeq ($(CONFIG_SCHED_WAITPID),y)
|
||||
CSRCS += task_vfork.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_BUILD_KERNEL),y)
|
||||
CSRCS += task_spawn.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_CANCELLATION_POINTS),y)
|
||||
CSRCS += task_setcanceltype.c task_testcancel.c
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_BINFMT_DISABLE),y)
|
||||
ifeq ($(CONFIG_LIBC_EXECFUNCS),y)
|
||||
CSRCS += task_execv.c task_posixspawn.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SCHED_STARTHOOK),y)
|
||||
CSRCS += task_starthook.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SCHED_ATEXIT),y)
|
||||
CSRCS += task_atexit.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SCHED_ONEXIT),y)
|
||||
CSRCS += task_onexit.c
|
||||
endif
|
||||
|
||||
# Include task build support
|
||||
|
||||
DEPPATH += --dep-path task
|
||||
VPATH += :task
|
||||
@@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
* sched/task/exit.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include "task/task.h"
|
||||
#include "group/group.h"
|
||||
#include "sched/sched.h"
|
||||
#include "pthread/pthread.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: _exit
|
||||
*
|
||||
* Description:
|
||||
* This function causes the currently executing task to cease
|
||||
* to exist. This is a special case of task_delete() where the task to
|
||||
* be deleted is the currently executing task. It is more complex because
|
||||
* a context switch must be perform to the next ready to run task.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void _exit(int status)
|
||||
{
|
||||
up_exit(status);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: exit
|
||||
*
|
||||
* Description:
|
||||
* The exit() function causes normal process termination and the value of
|
||||
* status & 0377 to be returned to the parent.
|
||||
*
|
||||
* All functions registered with atexit() and on_exit() are called, in the
|
||||
* reverse order of their registration.
|
||||
*
|
||||
* All open streams are flushed and closed.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void exit(int status)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
|
||||
/* Only the lower 8-bits of status are used */
|
||||
|
||||
status &= 0xff;
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
/* Kill all of the children of the group, preserving only this thread.
|
||||
* exit() is normally called from the main thread of the task. pthreads
|
||||
* exit through a different mechanism.
|
||||
*/
|
||||
|
||||
group_kill_children(tcb);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PTHREAD_CLEANUP
|
||||
/* Perform any stack pthread clean-up callbacks */
|
||||
|
||||
pthread_cleanup_popall(tcb);
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_DISABLE_PTHREAD) && !defined(CONFIG_PTHREAD_MUTEX_UNSAFE)
|
||||
/* Recover any mutexes still held by the canceled thread */
|
||||
|
||||
pthread_mutex_inconsistent(tcb);
|
||||
#endif
|
||||
|
||||
/* 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 to handle atexit() and on_exit() callbacks and
|
||||
* so that we can flush buffered I/O (both of which may required
|
||||
* suspending).
|
||||
*/
|
||||
|
||||
nxtask_exithook(tcb, status, false);
|
||||
|
||||
/* Then "really" exit. Only the lower 8 bits of the exit status are
|
||||
* used.
|
||||
*/
|
||||
|
||||
_exit(status);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
* sched/task/spawn.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __SCHED_TASK_SPAWN_H
|
||||
#define __SCHED_TASK_SPAWN_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.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;
|
||||
|
||||
/* 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 sem_t g_spawn_parmsem;
|
||||
#ifndef CONFIG_SCHED_WAITPID
|
||||
extern sem_t g_spawn_execsem;
|
||||
#endif
|
||||
extern struct spawn_parms_s g_spawn_parms;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_semtake and spawn_semgive
|
||||
*
|
||||
* Description:
|
||||
* Give and take semaphores
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* sem - The semaphore to act on.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int spawn_semtake(FAR sem_t *sem);
|
||||
#define spawn_semgive(sem) nxsem_post(sem)
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_execattrs
|
||||
*
|
||||
* Description:
|
||||
* Set attributes of the new child task after it has been spawned.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pid - The pid of the new task.
|
||||
* attr - The attributes to use
|
||||
*
|
||||
* Returned Value:
|
||||
* Errors are not reported by this function. This is because errors
|
||||
* cannot occur, but ratther that the new task has already been started
|
||||
* so there is no graceful way to handle errors detected in this context
|
||||
* (unless we delete the new task and recover).
|
||||
*
|
||||
* Assumptions:
|
||||
* That task has been started but has not yet executed because pre-
|
||||
* emption is disabled.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_proxyattrs
|
||||
*
|
||||
* Description:
|
||||
* Set attributes of the proxy task before it has started the new child
|
||||
* task.
|
||||
*
|
||||
* 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);
|
||||
|
||||
#endif /* __SCHED_TASK_SPAWN_H */
|
||||
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __SCHED_TASK_TASK_H
|
||||
#define __SCHED_TASK_TASK_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/compiler.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
struct tcb_s; /* Forward reference */
|
||||
|
||||
/* Task start-up */
|
||||
|
||||
void nxtask_start(void);
|
||||
int nxtask_setup_scheduler(FAR struct task_tcb_s *tcb, int priority,
|
||||
start_t start, main_t main, uint8_t ttype);
|
||||
int nxtask_setup_arguments(FAR struct task_tcb_s *tcb, FAR const char *name,
|
||||
FAR char * const argv[]);
|
||||
|
||||
/* 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);
|
||||
void nxtask_recover(FAR struct tcb_s *tcb);
|
||||
|
||||
/* Cancellation points */
|
||||
|
||||
bool nxnotify_cancellation(FAR struct tcb_s *tcb);
|
||||
|
||||
#endif /* __SCHED_TASK_TASK_H */
|
||||
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_activate.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_activate
|
||||
*
|
||||
* Description:
|
||||
* This function activates tasks initialized by nxtask_setup_scheduler().
|
||||
* Without activation, a task is ineligible for execution by the
|
||||
* scheduler.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB for the task for the task (same as the nxtask_init
|
||||
* argument).
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_activate(FAR struct tcb_s *tcb)
|
||||
{
|
||||
irqstate_t flags = enter_critical_section();
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION
|
||||
|
||||
/* Check if this is really a re-start */
|
||||
|
||||
if (tcb->task_state != TSTATE_TASK_INACTIVE)
|
||||
{
|
||||
/* Inform the instrumentation layer that the task
|
||||
* has stopped
|
||||
*/
|
||||
|
||||
sched_note_stop(tcb);
|
||||
}
|
||||
|
||||
/* Inform the instrumentation layer that the task
|
||||
* has started
|
||||
*/
|
||||
|
||||
sched_note_start(tcb);
|
||||
#endif
|
||||
|
||||
up_unblock_task(tcb);
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_atexit.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef CONFIG_SCHED_ATEXIT
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_ONEXIT
|
||||
static void exitfunc(int exitcode, FAR void *arg)
|
||||
{
|
||||
(*(atexitfunc_t)arg)();
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: atexit
|
||||
*
|
||||
* Description:
|
||||
* Registers a function to be called at program exit.
|
||||
* The atexit() function registers the given function to be called
|
||||
* at normal process termination, whether via exit or via return from
|
||||
* the program's main().
|
||||
*
|
||||
* NOTE: CONFIG_SCHED_ATEXIT must be defined to enable this function
|
||||
*
|
||||
* Limitations in the current implementation:
|
||||
*
|
||||
* 1. Only a single atexit function can be registered unless
|
||||
* CONFIG_SCHED_ATEXIT_MAX defines a larger number.
|
||||
* 2. atexit functions are not inherited when a new task is
|
||||
* created.
|
||||
* 3. If both SCHED_ONEXIT and SCHED_ATEXIT are selected, then atexit()
|
||||
* is built on top of the on_exit() implementation. In that case,
|
||||
* CONFIG_SCHED_ONEXIT_MAX determines the size of the combined
|
||||
* number of atexit() and on_exit() calls and SCHED_ATEXIT_MAX is
|
||||
* not used.
|
||||
*
|
||||
* Input Parameters:
|
||||
* func - A pointer to the function to be called when the task exits.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success. Non-zero on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int atexit(void (*func)(void))
|
||||
{
|
||||
#if defined(CONFIG_SCHED_ONEXIT)
|
||||
/* atexit is equivalent to on_exit() with no argument (Assuming that the
|
||||
* ABI can handle a callback function that receives more parameters than
|
||||
* it expects).
|
||||
*/
|
||||
|
||||
return on_exit(exitfunc, func);
|
||||
|
||||
#else
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
int index;
|
||||
int ret = ERROR;
|
||||
|
||||
DEBUGASSERT(group);
|
||||
|
||||
/* The following must be atomic */
|
||||
|
||||
if (func)
|
||||
{
|
||||
sched_lock();
|
||||
|
||||
/* Search for the first available slot. atexit() functions are
|
||||
* registered from lower to higher array indices; they must be called
|
||||
* in the reverse order of registration when task exists, i.e., from
|
||||
* higher to lower indices.
|
||||
*/
|
||||
|
||||
for (index = 0; index < CONFIG_SCHED_EXIT_MAX; index++)
|
||||
{
|
||||
if (!group->tg_exit[index].func.at)
|
||||
{
|
||||
group->tg_exit[index].func.at = func;
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_ATEXIT */
|
||||
@@ -0,0 +1,414 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_cancelpt.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Cancellation Points.
|
||||
*
|
||||
* Cancellation points shall occur when a thread is executing the following
|
||||
* functions:
|
||||
*
|
||||
* accept() mq_timedsend() putpmsg() sigtimedwait()
|
||||
* aio_suspend() msgrcv() pwrite() sigwait()
|
||||
* clock_nanosleep() msgsnd() read() sigwaitinfo()
|
||||
* close() msync() readv() sleep()
|
||||
* connect() nanosleep() recv() system()
|
||||
* creat() open() recvfrom() tcdrain()
|
||||
* fcntl() pause() recvmsg() usleep()
|
||||
* fdatasync() poll() select() wait()
|
||||
* fsync() pread() sem_timedwait() waitid()
|
||||
* getmsg() pselect() sem_wait() waitpid()
|
||||
* getpmsg() pthread_cond_timedwait() send() write()
|
||||
* lockf() pthread_cond_wait() sendmsg() writev()
|
||||
* mq_receive() pthread_join() sendto()
|
||||
* mq_send() pthread_testcancel() sigpause()
|
||||
* mq_timedreceive() putmsg() sigsuspend()
|
||||
*
|
||||
* Each of the above function must call enter_cancellation_point() on entry
|
||||
* in order to establish the cancellation point and
|
||||
* leave_cancellation_point() on exit. These functions are described below.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/cancelpt.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "semaphore/semaphore.h"
|
||||
#include "signal/signal.h"
|
||||
#include "mqueue/mqueue.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: enter_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Called at the beginning of the cancellation point to establish the
|
||||
* cancellation point. This function does the following:
|
||||
*
|
||||
* 1. If deferred cancellation does not apply to this thread, nothing is
|
||||
* done, otherwise, it
|
||||
* 2. Sets state information in the caller's TCB and increments a nesting
|
||||
* count.
|
||||
* 3. If this is the outermost nesting level, it checks if there is a
|
||||
* pending cancellation and, if so, calls either exit() or
|
||||
* pthread_exit(), depending upon the type of the thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* true is returned if a cancellation is pending but cannot be performed
|
||||
* now due to the nesting level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool enter_cancellation_point(void)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
bool ret = false;
|
||||
|
||||
/* Disabling pre-emption should provide sufficient protection. We only
|
||||
* need the TCB to be stationary (no interrupt level modification is
|
||||
* anticipated).
|
||||
*
|
||||
* REVISIT: is locking the scheduler sufficient in SMP mode?
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* If cancellation is disabled on this thread or if this thread is using
|
||||
* asynchronous cancellation, then do nothing.
|
||||
*
|
||||
* Special case: if the cpcount count is greater than zero, then we are
|
||||
* nested and the above condition was certainly true at the outermost
|
||||
* nesting level.
|
||||
*/
|
||||
|
||||
if (((tcb->flags & TCB_FLAG_NONCANCELABLE) == 0 &&
|
||||
(tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) ||
|
||||
tcb->cpcount > 0)
|
||||
{
|
||||
/* Check if there is a pending cancellation */
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0)
|
||||
{
|
||||
/* Yes... return true (if we don't exit here) */
|
||||
|
||||
ret = true;
|
||||
|
||||
/* If there is a pending cancellation and we are at the outermost
|
||||
* nesting level of cancellation function calls, then exit
|
||||
* according to the type of the thread.
|
||||
*/
|
||||
|
||||
if (tcb->cpcount == 0)
|
||||
{
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) ==
|
||||
TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, indicate that we are at a cancellation point by
|
||||
* incrementing the nesting level of the cancellation point
|
||||
* functions.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(tcb->cpcount < INT16_MAX);
|
||||
tcb->cpcount++;
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: leave_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Called at the end of the cancellation point. This function does the
|
||||
* following:
|
||||
*
|
||||
* 1. If deferred cancellation does not apply to this thread, nothing is
|
||||
* done, otherwise, it
|
||||
* 2. Clears state information in the caller's TCB and decrements a
|
||||
* nesting count.
|
||||
* 3. If this is the outermost nesting level, it checks if there is a
|
||||
* pending cancellation and, if so, calls either exit() or
|
||||
* pthread_exit(), depending upon the type of the thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void leave_cancellation_point(void)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
|
||||
/* Disabling pre-emption should provide sufficient protection. We only
|
||||
* need the TCB to be stationary (no interrupt level modification is
|
||||
* anticipated).
|
||||
*
|
||||
* REVISIT: is locking the scheduler sufficient in SMP mode?
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* If cancellation is disabled on this thread or if this thread is using
|
||||
* asynchronous cancellation, then do nothing. Here we check only the
|
||||
* nesting level: if the cpcount count is greater than zero, then the
|
||||
* required condition was certainly true at the outermost nesting level.
|
||||
*/
|
||||
|
||||
if (tcb->cpcount > 0)
|
||||
{
|
||||
/* Decrement the nesting level. If if would decrement to zero, then
|
||||
* we are at the outermost nesting level and may need to do more.
|
||||
*/
|
||||
|
||||
if (tcb->cpcount == 1)
|
||||
{
|
||||
/* We are no longer at the cancellation point */
|
||||
|
||||
tcb->cpcount = 0;
|
||||
|
||||
/* If there is a pending cancellation then just exit according to
|
||||
* the type of the thread.
|
||||
*/
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0)
|
||||
{
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) ==
|
||||
TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We are not at the outermost nesting level. Just decrment the
|
||||
* nesting level count.
|
||||
*/
|
||||
|
||||
tcb->cpcount--;
|
||||
}
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: check_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Returns true if:
|
||||
*
|
||||
* 1. Deferred cancellation does applies to this thread,
|
||||
* 2. We are within a cancellation point (i.e., the nesting level in the
|
||||
* TCB is greater than zero).
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* true is returned if a cancellation is pending but cannot be performed
|
||||
* now due to the nesting level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool check_cancellation_point(void)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
bool ret = false;
|
||||
|
||||
/* Disabling pre-emption should provide sufficient protection. We only
|
||||
* need the TCB to be stationary (no interrupt level modification is
|
||||
* anticipated).
|
||||
*
|
||||
* REVISIT: is locking the scheduler sufficient in SMP mode?
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* If cancellation is disabled on this thread or if this thread is using
|
||||
* asynchronous cancellation, then return false.
|
||||
*
|
||||
* If the cpcount count is greater than zero, then we within a
|
||||
* cancellation and will true if there is a pending cancellation.
|
||||
*/
|
||||
|
||||
if (((tcb->flags & TCB_FLAG_NONCANCELABLE) == 0 &&
|
||||
(tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) ||
|
||||
tcb->cpcount > 0)
|
||||
{
|
||||
/* Check if there is a pending cancellation. If so, return true. */
|
||||
|
||||
ret = ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0);
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CANCELLATION_POINTS */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxnotify_cancellation
|
||||
*
|
||||
* Description:
|
||||
* Called by task_delete() or pthread_cancel() if the cancellation occurs
|
||||
* while we the thread is within the cancellation point. This logic
|
||||
* behaves much like sending a signal: It will cause waiting threads
|
||||
* to wake up and terminated with ECANCELED. A call to
|
||||
* leave_cancellation_point() would then follow, causing the thread to
|
||||
* exit.
|
||||
*
|
||||
* Returned Value:
|
||||
* Indicate whether the notification delivery to the target
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool nxnotify_cancellation(FAR struct tcb_s *tcb)
|
||||
{
|
||||
irqstate_t flags;
|
||||
|
||||
/* We need perform the following operations from within a critical section
|
||||
* because it can compete with interrupt level activity.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* We only notify the cancellation if (1) the thread has not disabled
|
||||
* cancellation, (2) the thread uses the deferred cancellation mode,
|
||||
* (3) the thread is waiting within a cancellation point.
|
||||
*/
|
||||
|
||||
/* Check to see if this task has the non-cancelable bit set. */
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0)
|
||||
{
|
||||
/* Then we cannot cancel the thread now. Here is how this is
|
||||
* supposed to work:
|
||||
*
|
||||
* "When cancellability is disabled, all cancels are held pending
|
||||
* in the target thread until the thread changes the cancellability.
|
||||
* When cancellability is deferred, all cancels are held pending in
|
||||
* the target thread until the thread changes the cancellability,
|
||||
* calls a function which is a cancellation point or calls
|
||||
* pthread_testcancel(), thus creating a cancellation point. When
|
||||
* cancellability is asynchronous, all cancels are acted upon
|
||||
* immediately, interrupting the thread with its processing."
|
||||
*/
|
||||
|
||||
tcb->flags |= TCB_FLAG_CANCEL_PENDING;
|
||||
leave_critical_section(flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* Check if this task supports deferred cancellation */
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
|
||||
{
|
||||
/* Then we cannot cancel the task asynchronously.
|
||||
* Mark the cancellation as pending.
|
||||
*/
|
||||
|
||||
tcb->flags |= TCB_FLAG_CANCEL_PENDING;
|
||||
|
||||
/* If the task is waiting at a cancellation point, then notify of the
|
||||
* cancellation thereby waking the task up with an ECANCELED error.
|
||||
*/
|
||||
|
||||
if (tcb->cpcount > 0)
|
||||
{
|
||||
/* If the thread is blocked waiting for a semaphore, then the
|
||||
* thread must be unblocked to handle the cancellation.
|
||||
*/
|
||||
|
||||
if (tcb->task_state == TSTATE_WAIT_SEM)
|
||||
{
|
||||
nxsem_wait_irq(tcb, ECANCELED);
|
||||
}
|
||||
|
||||
/* If the thread is blocked waiting on a signal, then the
|
||||
* thread must be unblocked to handle the cancellation.
|
||||
*/
|
||||
|
||||
else if (tcb->task_state == TSTATE_WAIT_SIG)
|
||||
{
|
||||
nxsig_wait_irq(tcb, ECANCELED);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DISABLE_MQUEUE
|
||||
/* If the thread is blocked waiting on a message queue, then
|
||||
* the thread must be unblocked to handle the cancellation.
|
||||
*/
|
||||
|
||||
else if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY ||
|
||||
tcb->task_state == TSTATE_WAIT_MQNOTFULL)
|
||||
{
|
||||
nxmq_wait_irq(tcb, ECANCELED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
leave_critical_section(flags);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_create.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/kthread.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxthread_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_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.
|
||||
*
|
||||
* 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 nxthread_create(FAR const char *name, uint8_t ttype,
|
||||
int priority, int stack_size, main_t entry,
|
||||
FAR char * const argv[])
|
||||
{
|
||||
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)
|
||||
{
|
||||
serr("ERROR: Failed to allocate TCB\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Setup the task type */
|
||||
|
||||
tcb->cmn.flags = ttype;
|
||||
|
||||
/* Initialize the task */
|
||||
|
||||
ret = nxtask_init(tcb, name, priority, NULL, stack_size, entry, argv);
|
||||
if (ret < OK)
|
||||
{
|
||||
kmm_free(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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_create
|
||||
*
|
||||
* Description:
|
||||
* This function creates and activates a new task with a specified
|
||||
* priority and returns its system-assigned ID.
|
||||
*
|
||||
* The entry address entry is the address of the "main" function of the
|
||||
* task. This function will be called once the C environment has been
|
||||
* set up. The specified function will be called with four arguments.
|
||||
* Should the specified routine return, a call to exit() will
|
||||
* automatically be made.
|
||||
*
|
||||
* Note that four (and only four) arguments must be passed for the spawned
|
||||
* functions.
|
||||
*
|
||||
* nxtask_create() is identical to the function task_create(), differing
|
||||
* only in its return value: This function does not modify the errno
|
||||
* variable. This is a non-standard, internal OS function and is not
|
||||
* intended for use by application logic. Applications should use
|
||||
* task_create().
|
||||
*
|
||||
* Input Parameters:
|
||||
* name - Name of the new task
|
||||
* priority - Priority of the new task
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxtask_create(FAR const char *name, int priority,
|
||||
int stack_size, main_t entry, FAR char * const argv[])
|
||||
{
|
||||
return nxthread_create(name, TCB_FLAG_TTYPE_TASK, priority, stack_size,
|
||||
entry, argv);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_create
|
||||
*
|
||||
* Description:
|
||||
* This function creates and activates a new task with a specified
|
||||
* priority and returns its system-assigned ID.
|
||||
*
|
||||
* The entry address entry is the address of the "main" function of the
|
||||
* task. This function will be called once the C environment has been
|
||||
* set up. The specified function will be called with four arguments.
|
||||
* Should the specified routine return, a call to exit() will
|
||||
* automatically be made.
|
||||
*
|
||||
* Note that four (and only four) arguments must be passed for the spawned
|
||||
* functions.
|
||||
*
|
||||
* Input Parameters:
|
||||
* name - Name of the new task
|
||||
* priority - Priority of the new task
|
||||
* 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.
|
||||
*
|
||||
* Returned Value:
|
||||
* Returns the non-zero process ID of the new task or ERROR if memory is
|
||||
* insufficient or the task cannot be created. The errno will be set in
|
||||
* the failure case to indicate the nature of the error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_BUILD_KERNEL
|
||||
int task_create(FAR const char *name, int priority,
|
||||
int stack_size, main_t entry, FAR char * const argv[])
|
||||
{
|
||||
int ret = nxtask_create(name, priority, stack_size, entry, argv);
|
||||
if (ret < 0)
|
||||
{
|
||||
set_errno(-ret);
|
||||
ret = ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kthread_create
|
||||
*
|
||||
* Description:
|
||||
* This function creates and activates a kernel thread task with kernel-
|
||||
* mode privileges. It is identical to task_create() except that it
|
||||
* configures the newly started thread to run in kernel model.
|
||||
*
|
||||
* Input Parameters:
|
||||
* name - Name of the new task
|
||||
* priority - Priority of the new task
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kthread_create(FAR const char *name, int priority,
|
||||
int stack_size, main_t entry, FAR char * const argv[])
|
||||
{
|
||||
return nxthread_create(name, TCB_FLAG_TTYPE_KERNEL, priority, stack_size,
|
||||
entry, argv);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_delete.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_delete
|
||||
*
|
||||
* Description:
|
||||
* This function causes a specified task to cease to exist. Its stack and
|
||||
* TCB will be deallocated. This function is the companion to
|
||||
* task_create(). This is the version of the function exposed to the
|
||||
* user; it is simply a wrapper around the internal, nxtask_terminate
|
||||
* function.
|
||||
*
|
||||
* The logic in this function only deletes non-running tasks. If the
|
||||
* 'pid' parameter refers to the currently running task, then processing
|
||||
* is redirected to exit(). This can only happen if a task calls
|
||||
* task_delete()in order to delete itself.
|
||||
*
|
||||
* This function obeys the semantics of pthread cancellation: task
|
||||
* deletion is deferred if cancellation is disabled or if deferred
|
||||
* cancellation is supported (with cancellation points enabled).
|
||||
*
|
||||
* Input Parameters:
|
||||
* pid - The task ID of the task to delete. A pid of zero
|
||||
* signifies the calling task.
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; or ERROR on failure with the errno variable set
|
||||
* appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_delete(pid_t pid)
|
||||
{
|
||||
FAR struct tcb_s *dtcb;
|
||||
FAR struct tcb_s *rtcb;
|
||||
int errcode;
|
||||
int ret;
|
||||
|
||||
/* Check if the task to delete is the calling task: PID=0 means to delete
|
||||
* the calling task. In this case, task_delete() is much like exit()
|
||||
* except that it obeys the cancellation semantics.
|
||||
*/
|
||||
|
||||
rtcb = this_task();
|
||||
if (pid == 0)
|
||||
{
|
||||
pid = rtcb->pid;
|
||||
}
|
||||
|
||||
/* Get the TCB of the task to be deleted */
|
||||
|
||||
dtcb = (FAR struct tcb_s *)nxsched_get_tcb(pid);
|
||||
if (dtcb == NULL)
|
||||
{
|
||||
/* The pid does not correspond to any known thread. The task
|
||||
* has probably already exited.
|
||||
*/
|
||||
|
||||
errcode = ESRCH;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Only tasks and kernel threads can be deleted with this interface
|
||||
* (The semantics of the call should be sufficient to prohibit this).
|
||||
*/
|
||||
|
||||
DEBUGASSERT((dtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD);
|
||||
|
||||
/* Non-privileged tasks and pthreads may not delete privileged kernel
|
||||
* threads.
|
||||
*
|
||||
* REVISIT: We will need to look at this again in the future if/when
|
||||
* permissions are supported and a user task might also be privileged.
|
||||
*/
|
||||
|
||||
if (((rtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL) &&
|
||||
((dtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL))
|
||||
{
|
||||
errcode = EACCES;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Check if the task to delete is the calling task */
|
||||
|
||||
if (pid == rtcb->pid)
|
||||
{
|
||||
/* If it is, then what we really wanted to do was exit. Note that we
|
||||
* don't bother to unlock the TCB since it will be going away.
|
||||
*/
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Notify the target if the non-cancelable or deferred cancellation set */
|
||||
|
||||
if (nxnotify_cancellation(dtcb))
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Otherwise, perform the asynchronous cancellation, letting
|
||||
* nxtask_terminate() do all of the heavy lifting.
|
||||
*/
|
||||
|
||||
ret = nxtask_terminate(pid, false);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
||||
errout:
|
||||
set_errno(errcode);
|
||||
return ERROR;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_execv.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/binfmt/symtab.h>
|
||||
|
||||
#ifdef CONFIG_LIBC_EXECFUNCS
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: execv
|
||||
*
|
||||
* Description:
|
||||
* The standard 'exec' family of functions will replace the current process
|
||||
* image with a new process image. The new image will be constructed from a
|
||||
* regular, executable file called the new process image file. There will
|
||||
* be no return from a successful exec, because the calling process image
|
||||
* is overlaid by the new process image.
|
||||
*
|
||||
* Simplified 'execl()' and 'execv()' functions are provided by NuttX for
|
||||
* compatibility. NuttX is a tiny embedded RTOS that does not support
|
||||
* processes and hence the concept of overlaying a tasks process image with
|
||||
* a new process image does not make any sense. In NuttX, these functions
|
||||
* are wrapper functions that:
|
||||
*
|
||||
* 1. Call the non-standard binfmt function 'exec', and then
|
||||
* 2. exit(0).
|
||||
*
|
||||
* Note the inefficiency when 'exec[l|v]()' is called in the normal, two-
|
||||
* step process: (1) first call vfork() to create a new thread, then (2)
|
||||
* call 'exec[l|v]()' to replace the new thread with a program from the
|
||||
* file system. Since the new thread will be terminated by the
|
||||
* 'exec[l|v]()' call, it really served no purpose other than to support
|
||||
* Unix compatility.
|
||||
*
|
||||
* The non-standard binfmt function 'exec()' needs to have (1) a symbol
|
||||
* table that provides the list of symbols exported by the base code, and
|
||||
* (2) the number of symbols in that table. This information is currently
|
||||
* provided to 'exec()' from 'exec[l|v]()' via NuttX configuration
|
||||
* settings:
|
||||
*
|
||||
* CONFIG_LIBC_EXECFUNCS : Enable exec[l|v] support
|
||||
* CONFIG_EXECFUNCS_HAVE_SYMTAB : Defined if there is a pre-defined
|
||||
* symbol table
|
||||
* CONFIG_EXECFUNCS_SYMTAB_ARRAY : Symbol table name used by exec[l|v]
|
||||
* CONFIG_EXECFUNCS_NSYMBOLS_VAR : Variable holding number of symbols
|
||||
* in the table
|
||||
*
|
||||
* As a result of the above, the current implementations of 'execl()' and
|
||||
* 'execv()' suffer from some incompatibilities that may or may not be
|
||||
* addressed in a future version of NuttX. Other than just being an
|
||||
* inefficient use of MCU resource, the most serious of these is that
|
||||
* the exec'ed task will not have the same task ID as the vfork'ed
|
||||
* function. So the parent function cannot know the ID of the exec'ed
|
||||
* task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* path - The path to the program to be executed. If CONFIG_LIB_ENVPATH
|
||||
* is defined in the configuration, then this may be a relative path
|
||||
* from the current working directory. Otherwise, path must be the
|
||||
* absolute path to the program.
|
||||
* argv - A pointer to an array of string arguments. The end of the
|
||||
* array is indicated with a NULL entry.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function does not return on success. On failure, it will return
|
||||
* -1 (ERROR) and will set the 'errno' value appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int execv(FAR const char *path, FAR char * const argv[])
|
||||
{
|
||||
FAR const struct symtab_s *symtab;
|
||||
int nsymbols;
|
||||
int ret;
|
||||
|
||||
/* Get the current symbol table selection */
|
||||
|
||||
exec_getsymtab(&symtab, &nsymbols);
|
||||
|
||||
/* Start the task */
|
||||
|
||||
ret = exec(path, (FAR char * const *)argv, symtab, nsymbols);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: exec failed: %d\n", get_errno());
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Then exit */
|
||||
|
||||
exit(0);
|
||||
|
||||
/* We should not get here, but might be needed by some compilers. Other,
|
||||
* smarter compilers might complain that this code is unreachable. You
|
||||
* just can't win.
|
||||
*/
|
||||
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LIBC_EXECFUNCS */
|
||||
@@ -0,0 +1,191 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_exit.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
# include "irq/irq.h"
|
||||
#endif
|
||||
|
||||
#include "signal/signal.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_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 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.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; or ERROR on failure
|
||||
*
|
||||
* Assumptions:
|
||||
* Executing within a critical section established by the caller.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxtask_exit(void)
|
||||
{
|
||||
FAR struct tcb_s *dtcb;
|
||||
FAR struct tcb_s *rtcb;
|
||||
int ret;
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu;
|
||||
|
||||
/* Get the current CPU. By assumption, we are within a critical section
|
||||
* and, hence, the CPU index will remain stable.
|
||||
*
|
||||
* Avoid using this_task() because it may assume a state that is not
|
||||
* appropriate for an exiting task.
|
||||
*/
|
||||
|
||||
cpu = this_cpu();
|
||||
dtcb = current_task(cpu);
|
||||
#else
|
||||
dtcb = this_task();
|
||||
#endif
|
||||
|
||||
/* Update scheduler parameters */
|
||||
|
||||
nxsched_suspend_scheduler(dtcb);
|
||||
|
||||
/* Remove the TCB of the current task from the ready-to-run list. A
|
||||
* context switch will definitely be necessary -- that must be done
|
||||
* by the architecture-specific logic.
|
||||
*
|
||||
* nxsched_remove_readytorun will mark the task at the head of the
|
||||
* ready-to-run with state == TSTATE_TASK_RUNNING
|
||||
*/
|
||||
|
||||
nxsched_remove_readytorun(dtcb);
|
||||
|
||||
/* If there are any pending tasks, then add them to the ready-to-run
|
||||
* task list now
|
||||
*/
|
||||
|
||||
if (g_pendingtasks.head != NULL)
|
||||
{
|
||||
nxsched_merge_pending();
|
||||
}
|
||||
|
||||
/* Get the new task at the head of the ready to run list */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
rtcb = current_task(cpu);
|
||||
#else
|
||||
rtcb = this_task();
|
||||
#endif
|
||||
|
||||
/* NOTE: nxsched_resume_scheduler() was moved to up_exit()
|
||||
* because the global IRQ control for SMP should be deferred until
|
||||
* context switching, otherwise, the context switching would be done
|
||||
* without a critical section
|
||||
*/
|
||||
|
||||
/* 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
|
||||
* running.
|
||||
*
|
||||
* We disable pre-emption here by directly incrementing the lockcount
|
||||
* (vs. calling sched_lock()).
|
||||
*/
|
||||
|
||||
rtcb->lockcount++;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Make sure that the system knows about the locked state */
|
||||
|
||||
spin_setbit(&g_cpu_lockset, this_cpu(), &g_cpu_locksetlock,
|
||||
&g_cpu_schedlock);
|
||||
#endif
|
||||
|
||||
rtcb->task_state = TSTATE_TASK_READYTORUN;
|
||||
|
||||
/* Move the TCB to the specified blocked task list and delete it. Calling
|
||||
* nxtask_terminate with non-blocking true will suppress atexit() and
|
||||
* on-exit() calls and will cause buffered I/O to fail to be flushed. The
|
||||
* former is required _exit() behavior; the latter is optional _exit()
|
||||
* behavior.
|
||||
*/
|
||||
|
||||
nxsched_add_blocked(dtcb, TSTATE_TASK_INACTIVE);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* NOTE:
|
||||
* During nxtask_terminate(), enter_critical_section() will be called
|
||||
* to deallocate tcb. However, this would acquire g_cpu_irqlock if
|
||||
* rtcb->irqcount = 0, event though we are in critical section.
|
||||
* To prevent from acquiring, increment rtcb->irqcount here.
|
||||
*/
|
||||
|
||||
rtcb->irqcount++;
|
||||
#endif
|
||||
|
||||
ret = nxtask_terminate(dtcb->pid, true);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
rtcb->irqcount--;
|
||||
#endif
|
||||
|
||||
rtcb->task_state = TSTATE_TASK_RUNNING;
|
||||
|
||||
/* Decrement the lockcount on rctb. */
|
||||
|
||||
rtcb->lockcount--;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (rtcb->lockcount == 0)
|
||||
{
|
||||
/* Make sure that the system knows about the unlocked state */
|
||||
|
||||
spin_clrbit(&g_cpu_lockset, this_cpu(), &g_cpu_locksetlock,
|
||||
&g_cpu_schedlock);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,660 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_exithook.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "signal/signal.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_atexit
|
||||
*
|
||||
* Description:
|
||||
* Call any registered atexit function(s)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT)
|
||||
static inline void nxtask_atexit(FAR struct tcb_s *tcb)
|
||||
{
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
|
||||
/* Make sure that we have not already left the group. Only the final
|
||||
* exiting thread in the task group should trigger the atexit()
|
||||
* callbacks.
|
||||
*
|
||||
* REVISIT: This is a security problem In the PROTECTED and KERNEL builds:
|
||||
* We must not call the registered function in supervisor mode! See also
|
||||
* on_exit() and pthread_cleanup_pop() callbacks.
|
||||
*
|
||||
* REVISIT: In the case of task_delete(), the callback would execute in
|
||||
* the context the caller of task_delete() cancel, not in the context of
|
||||
* the exiting task (or process).
|
||||
*/
|
||||
|
||||
if (group && group->tg_nmembers == 1)
|
||||
{
|
||||
int index;
|
||||
|
||||
/* Call each atexit function in reverse order of registration atexit()
|
||||
* functions are registered from lower to higher array 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_EXIT_MAX - 1; index >= 0; index--)
|
||||
{
|
||||
if (group->tg_exit[index].func.at)
|
||||
{
|
||||
atexitfunc_t func;
|
||||
|
||||
/* Nullify the atexit function to prevent its reuse. */
|
||||
|
||||
func = group->tg_exit[index].func.at;
|
||||
group->tg_exit[index].func.at = NULL;
|
||||
|
||||
/* Call the atexit function */
|
||||
|
||||
(*func)();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define nxtask_atexit(tcb)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_onexit
|
||||
*
|
||||
* Description:
|
||||
* Call any registered on_exit function(s)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_ONEXIT
|
||||
static inline void nxtask_onexit(FAR struct tcb_s *tcb, int status)
|
||||
{
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
|
||||
/* Make sure that we have not already left the group. Only the final
|
||||
* exiting thread in the task group should trigger the atexit()
|
||||
* callbacks.
|
||||
*
|
||||
* REVISIT: This is a security problem In the PROTECTED and KERNEL builds:
|
||||
* We must not call the registered function in supervisor mode! See also
|
||||
* atexit() and pthread_cleanup_pop() callbacks.
|
||||
*
|
||||
* REVISIT: In the case of task_delete(), the callback would execute in
|
||||
* he context the caller of task_delete() cancel, not in the context of
|
||||
* the exiting task (or process).
|
||||
*/
|
||||
|
||||
if (group && group->tg_nmembers == 1)
|
||||
{
|
||||
int index;
|
||||
|
||||
/* Call each on_exit function in reverse order of registration.
|
||||
* on_exit() functions are registered from lower to higher array
|
||||
* 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_EXIT_MAX - 1; index >= 0; index--)
|
||||
{
|
||||
if (group->tg_exit[index].func.on)
|
||||
{
|
||||
onexitfunc_t func;
|
||||
FAR void *arg;
|
||||
|
||||
/* Nullify the on_exit function to prevent its reuse. */
|
||||
|
||||
func = group->tg_exit[index].func.on;
|
||||
arg = group->tg_exit[index].arg;
|
||||
|
||||
group->tg_exit[index].func.on = NULL;
|
||||
group->tg_exit[index].arg = NULL;
|
||||
|
||||
/* Call the on_exit function */
|
||||
|
||||
(*func)(status, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define nxtask_onexit(tcb,status)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_exitstatus
|
||||
*
|
||||
* Description:
|
||||
* Report exit status when main task of a task group exits
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
static inline void nxtask_exitstatus(FAR struct task_group_s *group,
|
||||
int status)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* Check if the parent task group has suppressed retention of
|
||||
* child exit status information.
|
||||
*/
|
||||
|
||||
if ((group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
/* No.. Find the exit status entry for this task in the parent TCB */
|
||||
|
||||
child = group_find_child(group, getpid());
|
||||
if (child)
|
||||
{
|
||||
/* Save the exit status.. For the case of HAVE_GROUP_MEMBERS,
|
||||
* the child status will be as exited until the last member
|
||||
* of the task group exits.
|
||||
*/
|
||||
|
||||
child->ch_status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
# define nxtask_exitstatus(group,status)
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_groupexit
|
||||
*
|
||||
* Description:
|
||||
* Mark that the final thread of a child task group as exited.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
static inline void nxtask_groupexit(FAR struct task_group_s *group)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* Check if the parent task group has suppressed retention of child exit
|
||||
* status information.
|
||||
*/
|
||||
|
||||
if ((group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
/* No.. Find the exit status entry for this task in the parent TCB */
|
||||
|
||||
child = group_find_child(group, getpid());
|
||||
if (child)
|
||||
{
|
||||
/* Mark that all members of the child task group has exited */
|
||||
|
||||
child->ch_flags |= CHILD_FLAG_EXITED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define nxtask_groupexit(group)
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_sigchild
|
||||
*
|
||||
* Description:
|
||||
* Send the SIGCHLD signal to the parent thread
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_HAVE_PARENT
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
static inline void nxtask_sigchild(pid_t ppid, FAR struct tcb_s *ctcb,
|
||||
int status)
|
||||
{
|
||||
FAR struct task_group_s *chgrp = ctcb->group;
|
||||
FAR struct task_group_s *pgrp;
|
||||
siginfo_t info;
|
||||
|
||||
DEBUGASSERT(chgrp);
|
||||
|
||||
/* Get the parent task group. It is possible that all of the members of
|
||||
* the parent task group have exited. This would not be an error. In
|
||||
* this case, the child task group has been orphaned.
|
||||
*/
|
||||
|
||||
pgrp = group_findbypid(ppid);
|
||||
if (!pgrp)
|
||||
{
|
||||
/* Set the task group ID to an invalid group ID. The dead parent
|
||||
* task group ID could get reused some time in the future.
|
||||
*/
|
||||
|
||||
chgrp->tg_ppid = INVALID_PROCESS_ID;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the exit status now if this is the main thread of the task group
|
||||
* that is exiting. Only the exiting main task of a task group carries
|
||||
* interpretable exit Check if this is the main task that is exiting.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
#endif
|
||||
{
|
||||
nxtask_exitstatus(pgrp, status);
|
||||
}
|
||||
|
||||
/* But only the final exiting thread in a task group, whatever it is,
|
||||
* should generate SIGCHLD.
|
||||
*/
|
||||
|
||||
if (chgrp->tg_nmembers == 1)
|
||||
{
|
||||
/* Mark that all of the threads in the task group have exited */
|
||||
|
||||
nxtask_groupexit(pgrp);
|
||||
|
||||
/* Create the siginfo structure. We don't actually know the cause.
|
||||
* That is a bug. Let's just say that the child task just exited
|
||||
* for now.
|
||||
*/
|
||||
|
||||
info.si_signo = SIGCHLD;
|
||||
info.si_code = CLD_EXITED;
|
||||
info.si_errno = OK;
|
||||
info.si_value.sival_ptr = NULL;
|
||||
info.si_pid = chgrp->tg_pid;
|
||||
info.si_status = status;
|
||||
|
||||
/* Send the signal to one thread in the group */
|
||||
|
||||
group_signal(pgrp, &info);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* HAVE_GROUP_MEMBERS */
|
||||
|
||||
static inline void nxtask_sigchild(FAR struct tcb_s *ptcb,
|
||||
FAR struct tcb_s *ctcb, int status)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
/* If task groups are not supported then we will report SIGCHLD when the
|
||||
* task exits. Unfortunately, there could still be threads in the group
|
||||
* that are still running.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
#endif
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Save the exit status now of the main thread */
|
||||
|
||||
nxtask_exitstatus(ptcb->group, status);
|
||||
|
||||
#else /* CONFIG_SCHED_CHILD_STATUS */
|
||||
/* Exit status is not retained. Just decrement the number of
|
||||
* children from this parent.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(ptcb->group != NULL && ptcb->group->tg_nchildren > 0);
|
||||
ptcb->group->tg_nchildren--;
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
|
||||
/* Create the siginfo structure. We don't actually know the cause.
|
||||
* That is a bug. Let's just say that the child task just exited
|
||||
* for now.
|
||||
*/
|
||||
|
||||
info.si_signo = SIGCHLD;
|
||||
info.si_code = CLD_EXITED;
|
||||
info.si_errno = OK;
|
||||
info.si_value.sival_ptr = NULL;
|
||||
info.si_pid = ctcb->group->tg_pid;
|
||||
info.si_status = status;
|
||||
|
||||
/* Send the signal. We need to use this internal interface so that we
|
||||
* can provide the correct si_code value with the signal.
|
||||
*/
|
||||
|
||||
nxsig_tcbdispatch(ptcb, &info);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_GROUP_MEMBERS */
|
||||
#else /* CONFIG_SCHED_HAVE_PARENT */
|
||||
|
||||
# define nxtask_sigchild(x,ctcb,status)
|
||||
|
||||
#endif /* CONFIG_SCHED_HAVE_PARENT */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_signalparent
|
||||
*
|
||||
* Description:
|
||||
* Send the SIGCHLD signal to the parent task group
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_HAVE_PARENT
|
||||
static inline void nxtask_signalparent(FAR struct tcb_s *ctcb, int status)
|
||||
{
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
DEBUGASSERT(ctcb && ctcb->group);
|
||||
|
||||
/* Keep things stationary throughout the following */
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Send SIGCHLD to all members of the parent's task group */
|
||||
|
||||
nxtask_sigchild(ctcb->group->tg_ppid, ctcb, status);
|
||||
sched_unlock();
|
||||
#else
|
||||
FAR struct tcb_s *ptcb;
|
||||
|
||||
/* Keep things stationary throughout the following */
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Get the TCB of the receiving, parent task. We do this early to
|
||||
* handle multiple calls to nxtask_signalparent.
|
||||
*/
|
||||
|
||||
ptcb = nxsched_get_tcb(ctcb->group->tg_ppid);
|
||||
if (ptcb == NULL)
|
||||
{
|
||||
/* The parent no longer exists... bail */
|
||||
|
||||
sched_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send SIGCHLD to all members of the parent's task group. NOTE that the
|
||||
* SIGCHLD signal is only sent once either (1) if this is the final thread
|
||||
* of the task group that is exiting (HAVE_GROUP_MEMBERS) or (2) if the
|
||||
* main thread of the group is exiting (!HAVE_GROUP_MEMBERS).
|
||||
*/
|
||||
|
||||
nxtask_sigchild(ptcb, ctcb, status);
|
||||
sched_unlock();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
# define nxtask_signalparent(ctcb,status)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_exitwakeup
|
||||
*
|
||||
* Description:
|
||||
* Wakeup any tasks waiting for this task to exit
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_SCHED_HAVE_PARENT)
|
||||
static inline void nxtask_exitwakeup(FAR struct tcb_s *tcb, int status)
|
||||
{
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
|
||||
/* Have we already left the group? */
|
||||
|
||||
if (group)
|
||||
{
|
||||
/* Only tasks (and kernel threads) return valid status. Record the
|
||||
* exit status when the task exists. The group, however, may still
|
||||
* be executing.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
#endif
|
||||
{
|
||||
/* Report the exit status. We do not nullify tg_statloc here
|
||||
* because we want to prevent other tasks from registering for
|
||||
* the return status. There is only one task per task group,
|
||||
* there for, this logic should execute exactly once in the
|
||||
* lifetime of the task group.
|
||||
*
|
||||
* "If more than one thread is suspended in waitpid() awaiting
|
||||
* termination of the same process, exactly one thread will
|
||||
* return the process status at the time of the target process
|
||||
* termination."
|
||||
*
|
||||
* Hmmm.. what do we return to the others?
|
||||
*/
|
||||
|
||||
if (group->tg_statloc)
|
||||
{
|
||||
*group->tg_statloc = status << 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this the last thread in the group? */
|
||||
|
||||
if (group->tg_nmembers == 1)
|
||||
{
|
||||
/* Yes.. Wakeup any tasks waiting for this task to exit */
|
||||
|
||||
group->tg_statloc = NULL;
|
||||
group->tg_waitflags = 0;
|
||||
|
||||
while (group->tg_exitsem.semcount < 0)
|
||||
{
|
||||
/* Wake up the thread */
|
||||
|
||||
nxsem_post(&group->tg_exitsem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# 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
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_exithook
|
||||
*
|
||||
* Description:
|
||||
* This function implements some of the internal logic of exit() and
|
||||
* task_delete(). This function performs some clean-up and other actions
|
||||
* required when a task exits:
|
||||
*
|
||||
* - All open streams are flushed and closed.
|
||||
* - All functions registered with atexit() and on_exit() are called, in
|
||||
* the reverse order of their registration.
|
||||
*
|
||||
* When called from exit(), the tcb still resides at the head of the ready-
|
||||
* to-run list. The following logic is safe because we will not be
|
||||
* returning from the exit() call.
|
||||
*
|
||||
* When called from nxtask_terminate() we are operating on a different
|
||||
* thread; on the thread that called task_delete(). In this case,
|
||||
* 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)
|
||||
{
|
||||
/* Under certain conditions, nxtask_exithook() can be called multiple
|
||||
* times. A bit in the TCB was set the first time this function was
|
||||
* called. If that bit is set, then just exit doing nothing more..
|
||||
*/
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_EXIT_PROCESSING) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* Mark the task as non-cancelable to avoid additional calls to exit()
|
||||
* due to any cancellation point logic that might get kicked off by
|
||||
* actions taken during exit processing.
|
||||
*/
|
||||
|
||||
tcb->flags |= TCB_FLAG_NONCANCELABLE;
|
||||
tcb->flags &= ~TCB_FLAG_CANCEL_PENDING;
|
||||
tcb->cpcount = 0;
|
||||
#endif
|
||||
|
||||
/* If exit function(s) were registered, call them now before we do any un-
|
||||
* initialization.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1. In the case of task_delete(), the exit function will *not* be called
|
||||
* on the thread execution of the task being deleted! That is probably
|
||||
* a bug.
|
||||
* 2. We cannot call the exit functions if nonblocking is requested: These
|
||||
* functions might block.
|
||||
* 3. This function will only be called with non-blocking == true
|
||||
* only when called through _exit(). _exit() behaviors requires that
|
||||
* the exit functions *not* be called.
|
||||
*/
|
||||
|
||||
if (!nonblocking)
|
||||
{
|
||||
#if defined(CONFIG_SCHED_ATEXIT) || defined(CONFIG_SCHED_ONEXIT)
|
||||
nxtask_atexit(tcb);
|
||||
|
||||
/* Call any registered on_exit function(s) */
|
||||
|
||||
nxtask_onexit(tcb, status);
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
nxtask_recover(tcb);
|
||||
|
||||
/* NOTE: signal handling needs to be done in a criticl section */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
irqstate_t flags = enter_critical_section();
|
||||
#endif
|
||||
|
||||
/* Send the SIGCHLD signal to the parent task group */
|
||||
|
||||
nxtask_signalparent(tcb, status);
|
||||
|
||||
/* Wakeup any tasks waiting for this task to exit */
|
||||
|
||||
nxtask_exitwakeup(tcb, status);
|
||||
|
||||
/* Leave the task group. Perhaps discarding any un-reaped child
|
||||
* status (no zombies here!)
|
||||
*/
|
||||
|
||||
group_leave(tcb);
|
||||
|
||||
/* Deallocate anything left in the TCB's queues */
|
||||
|
||||
nxsig_cleanup(tcb); /* Deallocate Signal lists */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
leave_critical_section(flags);
|
||||
#endif
|
||||
|
||||
/* This function can be re-entered in certain cases. Set a flag
|
||||
* bit in the TCB to not that we have already completed this exit
|
||||
* processing.
|
||||
*/
|
||||
|
||||
tcb->flags |= TCB_FLAG_EXIT_PROCESSING;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_getgroup.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_getgroup
|
||||
*
|
||||
* Description:
|
||||
* Given a task ID, return the group structure of this task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* pid - The task ID to use in the lookup.
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, a pointer to the group task structure is returned. This
|
||||
* function can fail only if there is no group that corresponds to the
|
||||
* grouped ID.
|
||||
*
|
||||
* Assumptions:
|
||||
* Called during when signally tasks in a safe context. No special
|
||||
* precautions should be required here. However, extra care is taken when
|
||||
* accessing the global g_grouphead list.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct task_group_s *task_getgroup(pid_t pid)
|
||||
{
|
||||
FAR struct tcb_s *tcb = nxsched_get_tcb(pid);
|
||||
if (tcb)
|
||||
{
|
||||
return tcb->group;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_getpid.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: getpid
|
||||
*
|
||||
* Description:
|
||||
* Get the task ID of the currently executing task.
|
||||
*
|
||||
* Input parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* Normally when called from user applications, getpid() will return the
|
||||
* task ID of the currently executing task, that is, the task at the head
|
||||
* of the ready-to-run list. There is no specification for any errors
|
||||
* returned from getpid().
|
||||
*
|
||||
* getpid(), however, may be called from within the OS in some cases.
|
||||
* There are certain situations during context switching when the OS data
|
||||
* structures are in flux and where the current task at the head of the
|
||||
* ready-to-run task list is not actually running. In that case,
|
||||
* getpid() will return the error: -ESRCH
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pid_t getpid(void)
|
||||
{
|
||||
FAR struct tcb_s *rtcb;
|
||||
|
||||
/* Get the TCB at the head of the ready-to-run task list. That
|
||||
* will usually be the currently executing task. There is are two
|
||||
* exceptions to this:
|
||||
*
|
||||
* 1. Early in the start-up sequence, the ready-to-run list may be
|
||||
* empty! In this case, of course, the CPU0 start-up/IDLE thread with
|
||||
* pid == 0 must be running, and
|
||||
* 2. As described above, during certain context-switching conditions the
|
||||
* task at the head of the ready-to-run list may not actually be
|
||||
* running.
|
||||
*/
|
||||
|
||||
rtcb = this_task();
|
||||
if (rtcb != NULL)
|
||||
{
|
||||
/* Check if the task is actually running */
|
||||
|
||||
if (rtcb->task_state == TSTATE_TASK_RUNNING)
|
||||
{
|
||||
/* Yes.. Return the task ID from the TCB at the head of the
|
||||
* ready-to-run task list
|
||||
*/
|
||||
|
||||
return rtcb->pid;
|
||||
}
|
||||
|
||||
/* No.. return -ESRCH to indicate this condition */
|
||||
|
||||
return (pid_t)-ESRCH;
|
||||
}
|
||||
|
||||
/* We must have been called earlier in the start up sequence from the
|
||||
* start-up/IDLE thread before the ready-to-run list has been initialized.
|
||||
*/
|
||||
|
||||
return (pid_t)0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_getppid.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: getppid
|
||||
*
|
||||
* Description:
|
||||
* Get the parent task ID of the currently executing task.
|
||||
*
|
||||
* Input parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* Normally when called from user applications, getppid() will return the
|
||||
* parent task ID of the currently executing task, that is, the task at the
|
||||
* head of the ready-to-run list. There is no specification for any errors
|
||||
* returned from getppid().
|
||||
*
|
||||
* getppid(), however, may be called from within the OS in some cases.
|
||||
* There are certain situations during context switching when the OS data
|
||||
* structures are in flux and where the current task at the head of the
|
||||
* ready-to-run task list is not actually running. In that case,
|
||||
* getppid() will return the error: -ESRCH
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pid_t getppid(void)
|
||||
{
|
||||
FAR struct tcb_s *rtcb;
|
||||
|
||||
/* Get the TCB at the head of the ready-to-run task list. That
|
||||
* will usually be the currently executing task. There is are two
|
||||
* exceptions to this:
|
||||
*
|
||||
* 1. Early in the start-up sequence, the ready-to-run list may be
|
||||
* empty! In this case, of course, the CPU0 start-up/IDLE thread with
|
||||
* pid == 0 must be running, and
|
||||
* 2. As described above, during certain context-switching conditions the
|
||||
* task at the head of the ready-to-run list may not actually be
|
||||
* running.
|
||||
*/
|
||||
|
||||
rtcb = this_task();
|
||||
if (rtcb != NULL)
|
||||
{
|
||||
/* Check if the task is actually running */
|
||||
|
||||
if (rtcb->task_state == TSTATE_TASK_RUNNING)
|
||||
{
|
||||
/* Yes.. Return the parent task ID from the TCB at the head of the
|
||||
* ready-to-run task list
|
||||
*/
|
||||
|
||||
return rtcb->group->tg_ppid;
|
||||
}
|
||||
|
||||
/* No.. return -ESRCH to indicate this condition */
|
||||
|
||||
return (pid_t)-ESRCH;
|
||||
}
|
||||
|
||||
/* We must have been called earlier in the start up sequence from the
|
||||
* start-up/IDLE thread before the ready-to-run list has been initialized.
|
||||
*/
|
||||
|
||||
return (pid_t)0;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_gettid.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: gettid
|
||||
*
|
||||
* Description:
|
||||
* Get the thread ID of the currently executing thread.
|
||||
*
|
||||
* Input parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, returns the thread ID of the calling process.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pid_t gettid(void)
|
||||
{
|
||||
FAR struct tcb_s *rtcb;
|
||||
|
||||
/* Get the TCB at the head of the ready-to-run task list. That
|
||||
* will usually be the currently executing task. There are two
|
||||
* exceptions to this:
|
||||
*
|
||||
* 1. Early in the start-up sequence, the ready-to-run list may be
|
||||
* empty! In this case, of course, the CPU0 start-up/IDLE thread with
|
||||
* pid == 0 must be running, and
|
||||
* 2. As described above, during certain context-switching conditions the
|
||||
* task at the head of the ready-to-run list may not actually be
|
||||
* running.
|
||||
*/
|
||||
|
||||
rtcb = this_task();
|
||||
if (rtcb != NULL)
|
||||
{
|
||||
/* Check if the task is actually running */
|
||||
|
||||
if (rtcb->task_state == TSTATE_TASK_RUNNING)
|
||||
{
|
||||
/* Yes.. Return the task ID from the TCB at the head of the
|
||||
* ready-to-run task list
|
||||
*/
|
||||
|
||||
return rtcb->pid;
|
||||
}
|
||||
|
||||
/* No.. return -ESRCH to indicate this condition */
|
||||
|
||||
return (pid_t)-ESRCH;
|
||||
}
|
||||
|
||||
/* We must have been called earlier in the start up sequence from the
|
||||
* start-up/IDLE thread before the ready-to-run list has been initialized.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_init.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <sched.h>
|
||||
#include <queue.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/lib/libvars.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_init
|
||||
*
|
||||
* Description:
|
||||
* This function initializes a Task Control Block (TCB) in preparation for
|
||||
* starting a new thread. It performs a subset of the functionality of
|
||||
* task_create()
|
||||
*
|
||||
* Unlike task_create():
|
||||
* 1. Allocate the TCB. The pre-allocated TCB is passed in argv.
|
||||
* 2. Allocate the stack. The pre-allocated stack is passed in argv.
|
||||
* 3. Activate the task. This must be done by calling nxtask_activate().
|
||||
*
|
||||
* Certain fields of the pre-allocated TCB may be set to change the
|
||||
* nature of the created task. For example:
|
||||
*
|
||||
* - Task type may be set in the TCB flags to create kernel thread
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* name - Name of the new task (not used)
|
||||
* priority - Priority of the new task
|
||||
* stack - Start of the pre-allocated stack
|
||||
* stack_size - Size (in bytes) of the stack allocated
|
||||
* entry - Application start point of the new task
|
||||
* argv - 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.
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; negative error value on failure appropriately. (See
|
||||
* nxtask_setup_scheduler() for possible failure conditions). On failure,
|
||||
* the caller is responsible for freeing the stack memory and for calling
|
||||
* nxsched_release_tcb() to free the TCB (which could be in most any
|
||||
* state).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxtask_init(FAR struct task_tcb_s *tcb, const char *name, int priority,
|
||||
FAR void *stack, uint32_t stack_size,
|
||||
main_t entry, FAR char * const argv[])
|
||||
{
|
||||
uint8_t ttype = tcb->cmn.flags & TCB_FLAG_TTYPE_MASK;
|
||||
#ifndef CONFIG_BUILD_KERNEL
|
||||
FAR struct task_group_s *group;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
/* Only tasks and kernel threads can be initialized in this way */
|
||||
|
||||
DEBUGASSERT(tcb && ttype != TCB_FLAG_TTYPE_PTHREAD);
|
||||
#endif
|
||||
|
||||
/* Create a new task group */
|
||||
|
||||
ret = group_allocate(tcb, tcb->cmn.flags);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Associate file descriptors with the new task */
|
||||
|
||||
ret = group_setuptaskfiles(tcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_group;
|
||||
}
|
||||
|
||||
if (stack)
|
||||
{
|
||||
/* Use pre-allocated stack */
|
||||
|
||||
ret = up_use_stack(&tcb->cmn, stack, stack_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Allocate the stack for the TCB */
|
||||
|
||||
ret = up_create_stack(&tcb->cmn, stack_size, ttype);
|
||||
}
|
||||
|
||||
if (ret < OK)
|
||||
{
|
||||
goto errout_with_group;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_BUILD_KERNEL
|
||||
/* Allocate a stack frame to hold task-specific data */
|
||||
|
||||
group = tcb->cmn.group;
|
||||
group->tg_libvars = up_stack_frame(&tcb->cmn, sizeof(struct libvars_s));
|
||||
DEBUGASSERT(group->tg_libvars != NULL);
|
||||
|
||||
/* Initialize the task-specific data */
|
||||
|
||||
memset(group->tg_libvars, 0, sizeof(struct libvars_s));
|
||||
|
||||
/* Save the allocated task data in TLS */
|
||||
|
||||
tls_set_taskdata(&tcb->cmn);
|
||||
#endif
|
||||
|
||||
/* Initialize the task control block */
|
||||
|
||||
ret = nxtask_setup_scheduler(tcb, priority, nxtask_start,
|
||||
entry, ttype);
|
||||
if (ret < OK)
|
||||
{
|
||||
goto errout_with_group;
|
||||
}
|
||||
|
||||
/* Setup to pass parameters to the new task */
|
||||
|
||||
nxtask_setup_arguments(tcb, name, argv);
|
||||
|
||||
/* Now we have enough in place that we can join the group */
|
||||
|
||||
ret = group_initialize(tcb);
|
||||
if (ret == OK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The TCB was added to the inactive task list by
|
||||
* nxtask_setup_scheduler().
|
||||
*/
|
||||
|
||||
dq_rem((FAR dq_entry_t *)tcb, (FAR dq_queue_t *)&g_inactivetasks);
|
||||
|
||||
errout_with_group:
|
||||
|
||||
if (!stack && tcb->cmn.stack_alloc_ptr)
|
||||
{
|
||||
#ifdef CONFIG_BUILD_KERNEL
|
||||
/* If the exiting thread is not a kernel thread, then it has an
|
||||
* address environment. Don't bother to release the stack memory
|
||||
* in this case... There is no point since the memory lies in the
|
||||
* user memory region that will be destroyed anyway (and the
|
||||
* address environment has probably already been destroyed at
|
||||
* this point.. so we would crash if we even tried it). But if
|
||||
* this is a privileged group, when we still have to release the
|
||||
* memory using the kernel allocator.
|
||||
*/
|
||||
|
||||
if (ttype == TCB_FLAG_TTYPE_KERNEL)
|
||||
#endif
|
||||
{
|
||||
up_release_stack(&tcb->cmn, ttype);
|
||||
}
|
||||
}
|
||||
|
||||
group_leave(&tcb->cmn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_uninit
|
||||
*
|
||||
* Description:
|
||||
* Undo all operations on a TCB performed by task_init() and release the
|
||||
* TCB by calling kmm_free(). This is intended primarily to support
|
||||
* error recovery operations after a successful call to task_init() such
|
||||
* was when a subsequent call to task_activate fails.
|
||||
*
|
||||
* Caution: Freeing of the TCB itself might be an unexpected side-effect.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the TCB initialized by task_init()
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; negative error value on failure appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_uninit(FAR struct task_tcb_s *tcb)
|
||||
{
|
||||
/* The TCB was added to the inactive task list by
|
||||
* nxtask_setup_scheduler().
|
||||
*/
|
||||
|
||||
dq_rem((FAR dq_entry_t *)tcb, (FAR dq_queue_t *)&g_inactivetasks);
|
||||
|
||||
/* Release all resources associated with the TCB... Including the TCB
|
||||
* itself.
|
||||
*/
|
||||
|
||||
nxsched_release_tcb((FAR struct tcb_s *)tcb,
|
||||
tcb->cmn.flags & TCB_FLAG_TTYPE_MASK);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_onexit.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef CONFIG_SCHED_ONEXIT
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: on_exit
|
||||
*
|
||||
* Description:
|
||||
* Registers a function to be called at program exit.
|
||||
* The on_exit() function registers the given function to be called
|
||||
* at normal process termination, whether via exit or via return from
|
||||
* the program's main(). The function is passed the status argument
|
||||
* given to the last call to exit and the arg argument from on_exit().
|
||||
*
|
||||
* NOTE 1: This function comes from SunOS 4, but is also present in
|
||||
* libc4, libc5 and glibc. It no longer occurs in Solaris (SunOS 5).
|
||||
* Avoid this function, and use the standard atexit() instead.
|
||||
*
|
||||
* NOTE 2: CONFIG_SCHED_ONEXIT must be defined to enable this function
|
||||
*
|
||||
* Limitations in the current implementation:
|
||||
*
|
||||
* 1. Only a single on_exit function can be registered unless
|
||||
* CONFIG_SCHED_ONEXIT_MAX defines a larger number.
|
||||
* 2. on_exit functions are not inherited when a new task is
|
||||
* created.
|
||||
*
|
||||
* Input Parameters:
|
||||
* func - A pointer to the function to be called when the task exits.
|
||||
* arg - An argument that will be provided to the on_exit() function when
|
||||
* the task exits.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero on success. Non-zero on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
FAR struct task_group_s *group = tcb->group;
|
||||
int index;
|
||||
int ret = ENOSPC;
|
||||
|
||||
DEBUGASSERT(group);
|
||||
|
||||
/* The following must be atomic */
|
||||
|
||||
if (func)
|
||||
{
|
||||
sched_lock();
|
||||
|
||||
/* Search for the first available slot. on_exit() functions are
|
||||
* registered from lower to higher array indices; they must be called
|
||||
* in the reverse order of registration when task exists, i.e.,
|
||||
* from higher to lower indices.
|
||||
*/
|
||||
|
||||
for (index = 0; index < CONFIG_SCHED_EXIT_MAX; index++)
|
||||
{
|
||||
if (!group->tg_exit[index].func.on)
|
||||
{
|
||||
group->tg_exit[index].func.on = func;
|
||||
group->tg_exit[index].arg = arg;
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_ONEXIT */
|
||||
@@ -0,0 +1,443 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_posixspawn.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <spawn.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/binfmt/symtab.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/spawn.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxposix_spawn_exec
|
||||
*
|
||||
* Description:
|
||||
* Execute the task from the file system.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pidp - Upon successful completion, this will return the task ID of the
|
||||
* child task in the variable pointed to by a non-NULL 'pid' argument.|
|
||||
*
|
||||
* path - The 'path' argument identifies the file to execute. If
|
||||
* CONFIG_LIB_ENVPATH is defined, this may be either a relative or
|
||||
* or an absolute path. Otherwise, it must be an absolute path.
|
||||
*
|
||||
* 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
|
||||
* following spawn flags are supported:
|
||||
*
|
||||
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
||||
* value.
|
||||
* - POSIX_SPAWN_SETSCHEDULER: Set the new tasks scheduler priority to
|
||||
* the sched_policy value.
|
||||
*
|
||||
* NOTE: POSIX_SPAWN_SETSIGMASK is handled in ps_proxy().
|
||||
*
|
||||
* argv - argv[] is the argument list for the new task. argv[] is an
|
||||
* array of pointers to null-terminated strings. The list is terminated
|
||||
* with a null pointer.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function will return zero on success. Otherwise, an error number
|
||||
* will be returned as the function return value to indicate the error.
|
||||
* This errno value may be that set by execv(), sched_setpolicy(), or
|
||||
* sched_setparam().
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nxposix_spawn_exec(FAR pid_t *pidp, FAR const char *path,
|
||||
FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const argv[])
|
||||
{
|
||||
FAR const struct symtab_s *symtab;
|
||||
int nsymbols;
|
||||
int pid;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(path);
|
||||
|
||||
/* Get the current symbol table selection */
|
||||
|
||||
exec_getsymtab(&symtab, &nsymbols);
|
||||
|
||||
/* Disable pre-emption so that we can modify the task parameters after
|
||||
* we start the new task; the new task will not actually begin execution
|
||||
* until we re-enable pre-emption.
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Start the task */
|
||||
|
||||
pid = exec_spawn(path, (FAR char * const *)argv, symtab, nsymbols, attr);
|
||||
if (pid < 0)
|
||||
{
|
||||
ret = -pid;
|
||||
serr("ERROR: exec failed: %d\n", ret);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Return the task ID to the caller */
|
||||
|
||||
if (pid)
|
||||
{
|
||||
*pidp = pid;
|
||||
}
|
||||
|
||||
/* Now set the attributes. Note that we ignore all of the return values
|
||||
* here because we have already successfully started the task. If we
|
||||
* return an error value, then we would also have to stop the task.
|
||||
*/
|
||||
|
||||
if (attr)
|
||||
{
|
||||
spawn_execattrs(pid, attr);
|
||||
}
|
||||
|
||||
/* Re-enable pre-emption and return */
|
||||
|
||||
errout:
|
||||
sched_unlock();
|
||||
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);
|
||||
|
||||
#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
|
||||
spawn_semgive(&g_spawn_execsem);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: posix_spawn
|
||||
*
|
||||
* Description:
|
||||
* The posix_spawn() and posix_spawnp() functions will create a new,
|
||||
* child task, constructed from a regular executable file.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pid - Upon successful completion, posix_spawn() and posix_spawnp() will
|
||||
* return the task ID of the child task to the parent task, in the
|
||||
* variable pointed to by a non-NULL 'pid' argument. If the 'pid'
|
||||
* argument is a null pointer, the process ID of the child is not
|
||||
* returned to the caller.
|
||||
*
|
||||
* path - The 'path' argument to posix_spawn() is the absolute path that
|
||||
* identifies the file to execute. The 'path' argument to posix_spawnp()
|
||||
* may also be a relative path and will be used to construct a pathname
|
||||
* that identifies the file to execute. In the case of a relative path,
|
||||
* the path prefix for the file will be obtained by a search of the
|
||||
* directories passed as the environment variable PATH.
|
||||
*
|
||||
* NOTE: NuttX provides only one implementation: If
|
||||
* CONFIG_LIB_ENVPATH is defined, then only posix_spawnp() behavior
|
||||
* is supported; otherwise, only posix_spawn behavior is supported.
|
||||
*
|
||||
* file_actions - If 'file_actions' is a null pointer, then file
|
||||
* descriptors open in the calling process will remain open in the
|
||||
* child process (unless CONFIG_FDCLONE_STDIO is defined). If
|
||||
* 'file_actions' is not NULL, then the file descriptors open in the
|
||||
* child process will be those open in the calling process as modified
|
||||
* by the spawn file actions object pointed to by 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
|
||||
* posix_spawnattr_t spawn attributes object type is defined in spawn.h.
|
||||
* It will contains these attributes, not all of which are supported by
|
||||
* NuttX:
|
||||
*
|
||||
* - POSIX_SPAWN_SETPGROUP: Setting of the new task's process group is
|
||||
* not supported. NuttX does not support process groups.
|
||||
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
||||
* value.
|
||||
* - POSIX_SPAWN_SETSCHEDULER: Set the new task's scheduler policy to
|
||||
* the sched_policy value.
|
||||
* - POSIX_SPAWN_RESETIDS: Resetting of the effective user ID of the
|
||||
* child process is not supported. NuttX does not support effective
|
||||
* user IDs.
|
||||
* - POSIX_SPAWN_SETSIGMASK: Set the new task's signal mask.
|
||||
* - POSIX_SPAWN_SETSIGDEF: Resetting signal default actions is not
|
||||
* supported. NuttX does not support default signal actions.
|
||||
*
|
||||
* argv - argv[] is the argument list for the new task. argv[] is an
|
||||
* array of pointers to null-terminated strings. The list is terminated
|
||||
* with a null pointer.
|
||||
*
|
||||
* envp - The envp[] argument is not used by NuttX and may be NULL. In
|
||||
* standard implementations, envp[] is an array of character pointers to
|
||||
* null-terminated strings that provide the environment for the new
|
||||
* process image. The environment array is terminated by a null pointer.
|
||||
* In NuttX, the envp[] argument is ignored and the new task will simply
|
||||
* inherit the environment of the parent task.
|
||||
*
|
||||
* Returned Value:
|
||||
* posix_spawn() and posix_spawnp() will return zero on success.
|
||||
* Otherwise, an error number will be returned as the function return
|
||||
* value to indicate the error:
|
||||
*
|
||||
* - EINVAL: The value specified by 'file_actions' or 'attr' is invalid.
|
||||
* - Any errors that might have been return if vfork() and excec[l|v]()
|
||||
* had been called.
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* - NuttX provides only posix_spawn() or posix_spawnp() behavior
|
||||
* depending upon the setting of CONFIG_LIB_ENVPATH: If
|
||||
* CONFIG_LIB_ENVPATH is defined, then only posix_spawnp() behavior
|
||||
* is supported; otherwise, only posix_spawn behavior is supported.
|
||||
* - The 'envp' argument is not used and the 'environ' variable is not
|
||||
* altered (NuttX does not support the 'environ' variable).
|
||||
* - Process groups are not supported (POSIX_SPAWN_SETPGROUP).
|
||||
* - Effective user IDs are not supported (POSIX_SPAWN_RESETIDS).
|
||||
* - Signal default actions cannot be modified in the newly task executed
|
||||
* because NuttX does not support default signal actions
|
||||
* (POSIX_SPAWN_SETSIGDEF).
|
||||
*
|
||||
* POSIX Compatibility
|
||||
* - The value of the argv[0] received by the child task is assigned by
|
||||
* NuttX. For the caller of posix_spawn(), the provided argv[0] will
|
||||
* correspond to argv[1] received by the new task.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_LIB_ENVPATH
|
||||
int posix_spawnp(FAR pid_t *pid, FAR const char *path,
|
||||
FAR const posix_spawn_file_actions_t *file_actions,
|
||||
FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const argv[], FAR char * const envp[])
|
||||
#else
|
||||
int posix_spawn(FAR pid_t *pid, FAR const char *path,
|
||||
FAR const posix_spawn_file_actions_t *file_actions,
|
||||
FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const argv[], FAR char * const envp[])
|
||||
#endif
|
||||
{
|
||||
struct sched_param param;
|
||||
pid_t 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))
|
||||
{
|
||||
return nxposix_spawn_exec(pid, path, attr, argv);
|
||||
}
|
||||
|
||||
/* 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 = spawn_semtake(&g_spawn_parmsem);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: spawn_semtake 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.u.posix.path = path;
|
||||
|
||||
/* Get the priority of this (parent) task */
|
||||
|
||||
ret = nxsched_get_param(0, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: nxsched_get_param failed: %d\n", ret);
|
||||
spawn_semgive(&g_spawn_parmsem);
|
||||
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 = kthread_create("nxposix_spawn_proxy", param.sched_priority,
|
||||
CONFIG_POSIX_SPAWN_PROXY_STACKSIZE,
|
||||
(main_t)nxposix_spawn_proxy,
|
||||
(FAR char * const *)NULL);
|
||||
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 = nx_waitpid(proxy, &status, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: waitpid() failed: %d\n", ret);
|
||||
goto errout_with_lock;
|
||||
}
|
||||
#else
|
||||
ret = spawn_semtake(&g_spawn_execsem);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: spawn_semtake() 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
|
||||
spawn_semgive(&g_spawn_parmsem);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_prctl.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: prctl
|
||||
*
|
||||
* Description:
|
||||
* prctl() is called with a first argument describing what to do (with
|
||||
* values PR_* defined above) and with additional arguments depending on
|
||||
* the specific command.
|
||||
*
|
||||
* Returned Value:
|
||||
* The returned value may depend on the specific command. For PR_SET_NAME
|
||||
* and PR_GET_NAME, the returned value of 0 indicates successful operation.
|
||||
* On any failure, -1 is retruend and the errno value is set appropriately.
|
||||
*
|
||||
* EINVAL The value of 'option' is not recognized.
|
||||
* EFAULT optional arg1 is not a valid address.
|
||||
* ESRCH No task/thread can be found corresponding to that specified
|
||||
* by optional arg1.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int prctl(int option, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int errcode;
|
||||
|
||||
va_start(ap, option);
|
||||
switch (option)
|
||||
{
|
||||
case PR_SET_NAME:
|
||||
case PR_GET_NAME:
|
||||
case PR_SET_NAME_EXT:
|
||||
case PR_GET_NAME_EXT:
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
{
|
||||
/* Get the prctl arguments */
|
||||
|
||||
FAR char *name = va_arg(ap, FAR char *);
|
||||
FAR struct tcb_s *tcb;
|
||||
int pid = 0;
|
||||
|
||||
if (option == PR_SET_NAME_EXT ||
|
||||
option == PR_GET_NAME_EXT)
|
||||
{
|
||||
pid = va_arg(ap, int);
|
||||
}
|
||||
|
||||
/* Get the TCB associated with the PID (handling the special case
|
||||
* of pid==0 meaning "this thread")
|
||||
*/
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
tcb = this_task();
|
||||
}
|
||||
else
|
||||
{
|
||||
tcb = nxsched_get_tcb(pid);
|
||||
}
|
||||
|
||||
/* An invalid pid will be indicated by a NULL TCB returned from
|
||||
* nxsched_get_tcb()
|
||||
*/
|
||||
|
||||
if (tcb == NULL)
|
||||
{
|
||||
serr("ERROR: Pid does not correspond to a task: %d\n", pid);
|
||||
errcode = ESRCH;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* A pointer to the task name storage must also be provided */
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
serr("ERROR: No name provide\n");
|
||||
errcode = EFAULT;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Now get or set the task name */
|
||||
|
||||
if (option == PR_SET_NAME || option == PR_SET_NAME_EXT)
|
||||
{
|
||||
/* Ensure that tcb->name will be null-terminated, truncating if
|
||||
* necessary.
|
||||
*/
|
||||
|
||||
strncpy(tcb->name, name, CONFIG_TASK_NAME_SIZE);
|
||||
tcb->name[CONFIG_TASK_NAME_SIZE] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The returned value will be null-terminated, truncating if
|
||||
* necessary.
|
||||
*/
|
||||
|
||||
strncpy(name, tcb->name, CONFIG_TASK_NAME_SIZE - 1);
|
||||
name[CONFIG_TASK_NAME_SIZE - 1] = '\0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
#else
|
||||
serr("ERROR: Option not enabled: %d\n", option);
|
||||
errcode = ENOSYS;
|
||||
goto errout;
|
||||
#endif
|
||||
|
||||
default:
|
||||
serr("ERROR: Unrecognized option: %d\n", option);
|
||||
errcode = EINVAL;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Not reachable unless CONFIG_TASK_NAME_SIZE is > 0. NOTE: This might
|
||||
* change if additional commands are supported.
|
||||
*/
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
va_end(ap);
|
||||
return OK;
|
||||
#endif
|
||||
|
||||
errout:
|
||||
va_end(ap);
|
||||
set_errno(errcode);
|
||||
return ERROR;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_recover.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/wdog.h>
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "semaphore/semaphore.h"
|
||||
#include "wdog/wdog.h"
|
||||
#include "mqueue/mqueue.h"
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_recover
|
||||
*
|
||||
* Description:
|
||||
* This function is called when a task is deleted via task_delete() or
|
||||
* via pthread_cancel. I checks checks for semaphores, message queue, and
|
||||
* watchdog timer resources stranded in bad conditions.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the terminated task or thread
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* This function is called from task deletion logic in a safe context.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_recover(FAR struct tcb_s *tcb)
|
||||
{
|
||||
/* The task is being deleted. Cancel in pending timeout events. */
|
||||
|
||||
wd_recover(tcb);
|
||||
|
||||
/* If the thread holds semaphore counts or is waiting for a semaphore
|
||||
* count, then release the counts.
|
||||
*/
|
||||
|
||||
nxsem_recover(tcb);
|
||||
|
||||
#ifndef CONFIG_DISABLE_MQUEUE
|
||||
/* Handle cases where the thread was waiting for a message queue event */
|
||||
|
||||
nxmq_recover(tcb);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_SPORADIC
|
||||
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
|
||||
{
|
||||
/* Stop current sporadic scheduling */
|
||||
|
||||
DEBUGVERIFY(nxsched_stop_sporadic(tcb));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_reparent.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef CONFIG_SCHED_HAVE_PARENT
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_reparent
|
||||
*
|
||||
* Description:
|
||||
* Change the parent of a task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ppid - PID of the new parent task (0 for grandparent, i.e. the parent
|
||||
* of the current parent task)
|
||||
* chpid - PID of the child to be reparented.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 (OK) on success; A negated errno value on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
int task_reparent(pid_t ppid, pid_t chpid)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
FAR struct child_status_s *child;
|
||||
#endif
|
||||
FAR struct task_group_s *chgrp;
|
||||
FAR struct task_group_s *ogrp;
|
||||
FAR struct task_group_s *pgrp;
|
||||
FAR struct tcb_s *tcb;
|
||||
irqstate_t flags;
|
||||
pid_t opid;
|
||||
int ret;
|
||||
|
||||
/* Disable interrupts so that nothing can change in the relationship of
|
||||
* the three task: Child, current parent, and new parent.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Get the child tasks task group */
|
||||
|
||||
tcb = nxsched_get_tcb(chpid);
|
||||
if (!tcb)
|
||||
{
|
||||
ret = -ECHILD;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
DEBUGASSERT(tcb->group);
|
||||
chgrp = tcb->group;
|
||||
|
||||
/* Get the PID of the old parent task's task group (opid) */
|
||||
|
||||
opid = chgrp->tg_ppid;
|
||||
|
||||
/* Get the old parent task's task group (ogrp) */
|
||||
|
||||
ogrp = group_findbypid(opid);
|
||||
if (!ogrp)
|
||||
{
|
||||
ret = -ESRCH;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
/* If new parent task's PID (ppid) is zero, then new parent is the
|
||||
* grandparent will be the new parent, i.e., the parent of the current
|
||||
* parent task.
|
||||
*/
|
||||
|
||||
if (ppid == 0)
|
||||
{
|
||||
/* Get the grandparent task's task group (pgrp) */
|
||||
|
||||
ppid = ogrp->tg_ppid;
|
||||
pgrp = group_findbypid(ppid);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the new parent task's task group (pgrp) */
|
||||
|
||||
tcb = nxsched_get_tcb(ppid);
|
||||
if (!tcb)
|
||||
{
|
||||
ret = -ESRCH;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
pgrp = tcb->group;
|
||||
ppid = pgrp->tg_pid;
|
||||
}
|
||||
|
||||
if (!pgrp)
|
||||
{
|
||||
ret = -ESRCH;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
/* Then reparent the child. Notice that we don't actually change the
|
||||
* parent of the task. Rather, we change the parent task group for
|
||||
* all members of the child's task group.
|
||||
*/
|
||||
|
||||
chgrp->tg_ppid = ppid;
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Remove the child status entry from old parent task group */
|
||||
|
||||
child = group_remove_child(ogrp, chpid);
|
||||
if (child)
|
||||
{
|
||||
/* Has the new parent's task group suppressed child exit status? */
|
||||
|
||||
if ((pgrp->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
/* No.. Add the child status entry to the new parent's task group */
|
||||
|
||||
group_add_child(pgrp, child);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes.. Discard the child status entry */
|
||||
|
||||
group_free_child(child);
|
||||
}
|
||||
|
||||
/* Either case is a success */
|
||||
|
||||
ret = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This would not be an error if the original parent's task group has
|
||||
* suppressed child exit status.
|
||||
*/
|
||||
|
||||
ret = ((ogrp->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK;
|
||||
}
|
||||
|
||||
#else /* CONFIG_SCHED_CHILD_STATUS */
|
||||
/* Child task exit status is not retained */
|
||||
|
||||
DEBUGASSERT(ogrp->tg_nchildren > 0);
|
||||
|
||||
ogrp->tg_nchildren--; /* The original parent now has one few children */
|
||||
pgrp->tg_nchildren++; /* The new parent has one additional child */
|
||||
ret = OK;
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
|
||||
errout_with_ints:
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int task_reparent(pid_t ppid, pid_t chpid)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
FAR struct child_status_s *child;
|
||||
#endif
|
||||
FAR struct tcb_s *ptcb;
|
||||
FAR struct tcb_s *chtcb;
|
||||
FAR struct tcb_s *otcb;
|
||||
pid_t opid;
|
||||
irqstate_t flags;
|
||||
int ret;
|
||||
|
||||
/* Disable interrupts so that nothing can change in the relationship of
|
||||
* the three task: Child, current parent, and new parent.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Get the child tasks TCB (chtcb) */
|
||||
|
||||
chtcb = nxsched_get_tcb(chpid);
|
||||
if (!chtcb)
|
||||
{
|
||||
ret = -ECHILD;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
/* Get the PID of the child task's parent (opid) */
|
||||
|
||||
opid = chtcb->group->tg_ppid;
|
||||
|
||||
/* Get the TCB of the child task's parent (otcb) */
|
||||
|
||||
otcb = nxsched_get_tcb(opid);
|
||||
if (!otcb)
|
||||
{
|
||||
ret = -ESRCH;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
/* If new parent task's PID (tg_ppid) is zero, then new parent is the
|
||||
* grandparent will be the new parent, i.e., the parent of the current
|
||||
* parent task.
|
||||
*/
|
||||
|
||||
if (ppid == 0)
|
||||
{
|
||||
ppid = otcb->group->tg_ppid;
|
||||
}
|
||||
|
||||
/* Get the new parent task's TCB (ptcb) */
|
||||
|
||||
ptcb = nxsched_get_tcb(ppid);
|
||||
if (!ptcb)
|
||||
{
|
||||
ret = -ESRCH;
|
||||
goto errout_with_ints;
|
||||
}
|
||||
|
||||
/* Then reparent the child. The task specified by ppid is the new
|
||||
* parent.
|
||||
*/
|
||||
|
||||
chtcb->group->tg_ppid = ppid;
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Remove the child status entry from old parent TCB */
|
||||
|
||||
child = group_remove_child(otcb->group, chpid);
|
||||
if (child)
|
||||
{
|
||||
/* Has the new parent's task group suppressed child exit status? */
|
||||
|
||||
if ((ptcb->group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
/* No.. Add the child status entry to the new parent's task group */
|
||||
|
||||
group_add_child(ptcb->group, child);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes.. Discard the child status entry */
|
||||
|
||||
group_free_child(child);
|
||||
}
|
||||
|
||||
/* Either case is a success */
|
||||
|
||||
ret = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This would not be an error if the original parent's task group has
|
||||
* suppressed child exit status.
|
||||
*/
|
||||
|
||||
ret = ((otcb->group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) ?
|
||||
-ENOENT : OK;
|
||||
}
|
||||
|
||||
#else /* CONFIG_SCHED_CHILD_STATUS */
|
||||
/* Child task exit status is not retained */
|
||||
|
||||
DEBUGASSERT(otcb->group != NULL && otcb->group->tg_nchildren > 0);
|
||||
|
||||
otcb->group->tg_nchildren--; /* The original parent now has one few children */
|
||||
ptcb->group->tg_nchildren++; /* The new parent has one additional child */
|
||||
ret = OK;
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
|
||||
errout_with_ints:
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_SCHED_HAVE_PARENT */
|
||||
@@ -0,0 +1,211 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_restart.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/arch.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "signal/signal.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_restart
|
||||
*
|
||||
* Description:
|
||||
* This function "restarts" a task. The task is first terminated and then
|
||||
* reinitialized with same ID, priority, original entry point, stack size,
|
||||
* and parameters it had when it was first started.
|
||||
*
|
||||
* Input Parameters:
|
||||
* pid - The task ID of the task to delete. An ID of zero signifies the
|
||||
* calling task.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; -1 (ERROR) on failure with the errno variable set
|
||||
* appropriately.
|
||||
*
|
||||
* This function can fail if:
|
||||
* (1) A pid of zero or the pid of the calling task is provided
|
||||
* (functionality not implemented)
|
||||
* (2) The pid is not associated with any task known to the system.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_restart(pid_t pid)
|
||||
{
|
||||
FAR struct tcb_s *rtcb;
|
||||
FAR struct task_tcb_s *tcb;
|
||||
FAR dq_queue_t *tasklist;
|
||||
irqstate_t flags;
|
||||
int errcode;
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu;
|
||||
#endif
|
||||
|
||||
/* Check if the task to restart is the calling task */
|
||||
|
||||
rtcb = this_task();
|
||||
if ((pid == 0) || (pid == rtcb->pid))
|
||||
{
|
||||
/* Not implemented */
|
||||
|
||||
errcode = ENOSYS;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* We are restarting some other task than ourselves. Make sure that the
|
||||
* task does not change its state while we are executing. In the single
|
||||
* CPU state this could be done by disabling pre-emption. But we will
|
||||
* a little stronger medicine on the SMP case: The task make be running
|
||||
* on another CPU.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Find for the TCB associated with matching pid */
|
||||
|
||||
tcb = (FAR struct task_tcb_s *)nxsched_get_tcb(pid);
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if (!tcb || (tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) ==
|
||||
TCB_FLAG_TTYPE_PTHREAD)
|
||||
#else
|
||||
if (!tcb)
|
||||
#endif
|
||||
{
|
||||
/* There is no TCB with this pid or, if there is, it is not a task. */
|
||||
|
||||
errcode = ESRCH;
|
||||
goto errout_with_lock;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* If the task is running on another CPU, then pause that CPU. We can
|
||||
* then manipulate the TCB of the restarted task and when we resume the
|
||||
* that CPU, the restart take effect.
|
||||
*/
|
||||
|
||||
cpu = nxsched_pause_cpu(&tcb->cmn);
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/* Try to recover from any bad states */
|
||||
|
||||
nxtask_recover((FAR struct tcb_s *)tcb);
|
||||
|
||||
/* Kill any children of this thread */
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
group_kill_children((FAR struct tcb_s *)tcb);
|
||||
#endif
|
||||
|
||||
/* Remove the TCB from whatever list it is in. After this point, the TCB
|
||||
* should no longer be accessible to the system
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
tasklist = TLIST_HEAD(tcb->cmn.task_state, tcb->cmn.cpu);
|
||||
#else
|
||||
tasklist = TLIST_HEAD(tcb->cmn.task_state);
|
||||
#endif
|
||||
|
||||
dq_rem((FAR dq_entry_t *)tcb, tasklist);
|
||||
tcb->cmn.task_state = TSTATE_TASK_INVALID;
|
||||
|
||||
/* Deallocate anything left in the TCB's signal queues */
|
||||
|
||||
nxsig_cleanup((FAR struct tcb_s *)tcb); /* Deallocate Signal lists */
|
||||
tcb->cmn.sigprocmask = NULL_SIGNAL_SET; /* Reset sigprocmask */
|
||||
|
||||
/* Reset the current task priority */
|
||||
|
||||
tcb->cmn.sched_priority = tcb->cmn.init_priority;
|
||||
|
||||
/* The task should restart with pre-emption disabled and not in a critical
|
||||
* section.
|
||||
*/
|
||||
|
||||
tcb->cmn.lockcount = 0;
|
||||
#ifdef CONFIG_SMP
|
||||
tcb->cmn.irqcount = 0;
|
||||
#endif
|
||||
|
||||
/* Reset the base task priority and the number of pending
|
||||
* reprioritizations.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PRIORITY_INHERITANCE
|
||||
tcb->cmn.base_priority = tcb->cmn.init_priority;
|
||||
# if CONFIG_SEM_NNESTPRIO > 0
|
||||
tcb->cmn.npend_reprio = 0;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Re-initialize the processor-specific portion of the TCB. This will
|
||||
* reset the entry point and the start-up parameters
|
||||
*/
|
||||
|
||||
up_initial_state((FAR struct tcb_s *)tcb);
|
||||
|
||||
/* Add the task to the inactive task list */
|
||||
|
||||
dq_addfirst((FAR dq_entry_t *)tcb, (FAR dq_queue_t *)&g_inactivetasks);
|
||||
tcb->cmn.task_state = TSTATE_TASK_INACTIVE;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Resume the paused CPU (if any) */
|
||||
|
||||
if (cpu >= 0)
|
||||
{
|
||||
int ret = up_cpu_resume(cpu);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
goto errout_with_lock;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
/* Activate the task. */
|
||||
|
||||
nxtask_activate((FAR struct tcb_s *)tcb);
|
||||
return OK;
|
||||
|
||||
errout_with_lock:
|
||||
leave_critical_section(flags);
|
||||
errout:
|
||||
set_errno(errcode);
|
||||
return ERROR;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_setcancelstate.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_setcancelstate
|
||||
*
|
||||
* Description:
|
||||
* The task_setcancelstate() function atomically both sets the calling
|
||||
* task's cancellability state to the indicated state and returns the
|
||||
* previous cancellability state at the location referenced by oldstate.
|
||||
* Legal values for state are TASK_CANCEL_ENABLE and TASK_CANCEL_DISABLE.
|
||||
*
|
||||
* The cancellability state and type of any newly created tasks are
|
||||
* TASK_CANCEL_ENABLE and TASK_CANCEL_DEFERRED respectively.
|
||||
*
|
||||
* Input Parameters:
|
||||
* state - the new cancellability state, either TASK_CANCEL_ENABLE or
|
||||
* TASK_CANCEL_DISABLE
|
||||
* oldstate - The location to return the old cancellability state.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; ERROR is returned on any failure with the
|
||||
* errno value set appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_setcancelstate(int state, FAR int *oldstate)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
int ret = OK;
|
||||
|
||||
/* Suppress context changes for a bit so that the flags are stable. (the
|
||||
* flags should not change in interrupt handling).
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Return the current state if so requested */
|
||||
|
||||
if (oldstate != NULL)
|
||||
{
|
||||
if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0)
|
||||
{
|
||||
*oldstate = TASK_CANCEL_DISABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*oldstate = TASK_CANCEL_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the new cancellation state */
|
||||
|
||||
if (state == TASK_CANCEL_ENABLE)
|
||||
{
|
||||
/* Clear the non-cancelable flag */
|
||||
|
||||
tcb->flags &= ~TCB_FLAG_NONCANCELABLE;
|
||||
|
||||
/* Check if a cancellation was pending */
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0)
|
||||
{
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* If we are using deferred cancellation? */
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) == 0)
|
||||
#endif
|
||||
{
|
||||
/* No.. We are using asynchronous cancellation. If the
|
||||
* cancellation was pending in this case, then just exit.
|
||||
*/
|
||||
|
||||
tcb->flags &= ~TCB_FLAG_CANCEL_PENDING;
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) ==
|
||||
TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (state == TASK_CANCEL_DISABLE)
|
||||
{
|
||||
/* Set the non-cancelable state */
|
||||
|
||||
tcb->flags |= TCB_FLAG_NONCANCELABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_errno(EINVAL);
|
||||
ret = ERROR;
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_setcanceltype.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_setcanceltype
|
||||
*
|
||||
* Description:
|
||||
* The task_setcanceltype() function atomically both sets the calling
|
||||
* thread's cancellability type to the indicated type and returns the
|
||||
* previous cancellability type at the location referenced by oldtype
|
||||
* Legal values for type are TASK_CANCEL_DEFERRED and
|
||||
* TASK_CANCEL_ASYNCHRONOUS.
|
||||
*
|
||||
* The cancellability state and type of any newly created threads,
|
||||
* including the thread in which main() was first invoked, are
|
||||
* TASK_CANCEL_ENABLE and TASK_CANCEL_DEFERRED respectively.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_setcanceltype(int type, FAR int *oldtype)
|
||||
{
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
int ret = OK;
|
||||
|
||||
/* Suppress context changes for a bit so that the flags are stable. (the
|
||||
* flags should not change in interrupt handling).
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Return the current type if so requested */
|
||||
|
||||
if (oldtype != NULL)
|
||||
{
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
|
||||
{
|
||||
*oldtype = TASK_CANCEL_DEFERRED;
|
||||
}
|
||||
else
|
||||
{
|
||||
*oldtype = TASK_CANCEL_ASYNCHRONOUS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the new cancellation type */
|
||||
|
||||
if (type == TASK_CANCEL_ASYNCHRONOUS)
|
||||
{
|
||||
/* Clear the deferred cancellation bit */
|
||||
|
||||
tcb->flags &= ~TCB_FLAG_CANCEL_DEFERRED;
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* If we just switched from deferred to asynchronous type and if a
|
||||
* cancellation is pending, then exit now.
|
||||
*/
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0 &&
|
||||
(tcb->flags & TCB_FLAG_NONCANCELABLE) == 0)
|
||||
{
|
||||
tcb->flags &= ~TCB_FLAG_CANCEL_PENDING;
|
||||
|
||||
/* Exit according to the type of the thread */
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
else if (type == TASK_CANCEL_DEFERRED)
|
||||
{
|
||||
/* Set the deferred cancellation type */
|
||||
|
||||
tcb->flags |= TCB_FLAG_CANCEL_DEFERRED;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_setup.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/signal.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "pthread/pthread.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* This is an artificial limit to detect error conditions where an argv[]
|
||||
* list is not properly terminated.
|
||||
*/
|
||||
|
||||
#define MAX_STACK_ARGS 256
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/* This is the name for un-named tasks */
|
||||
|
||||
static const char g_noname[] = "<noname>";
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_assign_pid
|
||||
*
|
||||
* Description:
|
||||
* This function assigns the next unique task ID to a task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - TCB of task
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; ERROR on failure (errno is not set)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nxtask_assign_pid(FAR struct tcb_s *tcb)
|
||||
{
|
||||
pid_t next_pid;
|
||||
int hash_ndx;
|
||||
int tries;
|
||||
int ret = ERROR;
|
||||
|
||||
/* NOTE:
|
||||
* ERROR means that the g_pidhash[] table is completely full.
|
||||
* We cannot allow another task to be started.
|
||||
*/
|
||||
|
||||
/* Protect the following operation with a critical section
|
||||
* because g_pidhash is accessed from an interrupt context
|
||||
*/
|
||||
|
||||
irqstate_t flags = enter_critical_section();
|
||||
|
||||
/* We'll try every allowable pid */
|
||||
|
||||
for (tries = 0; tries < CONFIG_MAX_TASKS; tries++)
|
||||
{
|
||||
/* Get the next process ID candidate */
|
||||
|
||||
next_pid = ++g_lastpid;
|
||||
|
||||
/* Verify that the next_pid is in the valid range */
|
||||
|
||||
if (next_pid <= 0)
|
||||
{
|
||||
g_lastpid = 1;
|
||||
next_pid = 1;
|
||||
}
|
||||
|
||||
/* Get the hash_ndx associated with the next_pid */
|
||||
|
||||
hash_ndx = PIDHASH(next_pid);
|
||||
|
||||
/* Check if there is a (potential) duplicate of this pid */
|
||||
|
||||
if (!g_pidhash[hash_ndx].tcb)
|
||||
{
|
||||
/* Assign this PID to the task */
|
||||
|
||||
g_pidhash[hash_ndx].tcb = tcb;
|
||||
g_pidhash[hash_ndx].pid = next_pid;
|
||||
#ifdef CONFIG_SCHED_CPULOAD
|
||||
g_pidhash[hash_ndx].ticks = 0;
|
||||
#endif
|
||||
tcb->pid = next_pid;
|
||||
|
||||
ret = OK;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_inherit_affinity
|
||||
*
|
||||
* Description:
|
||||
* exec(), task_create(), and vfork() all inherit the affinity mask from
|
||||
* the parent thread. This is the default for pthread_create() as well
|
||||
* but the affinity mask can be specified in the pthread attributes as
|
||||
* well. pthread_setup() will have to fix up the affinity mask in this
|
||||
* case.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the new task.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* The parent of the new task is the task at the head of the assigned task
|
||||
* list for the current CPU.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline void nxtask_inherit_affinity(FAR struct tcb_s *tcb)
|
||||
{
|
||||
FAR struct tcb_s *rtcb = this_task();
|
||||
tcb->affinity = rtcb->affinity;
|
||||
}
|
||||
#else
|
||||
# define nxtask_inherit_affinity(tcb)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_save_parent
|
||||
*
|
||||
* Description:
|
||||
* Save the task ID of the parent task in the child task's group and
|
||||
* allocate a child status structure to catch the child task's exit
|
||||
* status.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the new, child task.
|
||||
* ttype - Type of the new thread: task, pthread, or kernel thread
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* The parent of the new task is the task at the head of the ready-to-run
|
||||
* list.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_HAVE_PARENT
|
||||
static inline void nxtask_save_parent(FAR struct tcb_s *tcb, uint8_t ttype)
|
||||
{
|
||||
DEBUGASSERT(tcb != NULL && tcb->group != NULL);
|
||||
|
||||
/* Only newly created tasks (and kernel threads) have parents. None of
|
||||
* this logic applies to pthreads with reside in the same group as the
|
||||
* parent and share that same child/parent relationships.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
#endif
|
||||
{
|
||||
/* Get the TCB of the parent task. In this case, the calling task. */
|
||||
|
||||
FAR struct tcb_s *rtcb = this_task();
|
||||
|
||||
DEBUGASSERT(rtcb != NULL && rtcb->group != NULL);
|
||||
|
||||
/* Save the PID of the parent tasks' task group in the child's task
|
||||
* group. Copy the ID from the parent's task group structure to
|
||||
* child's task group.
|
||||
*/
|
||||
|
||||
tcb->group->tg_ppid = rtcb->group->tg_pid;
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Tasks can also suppress retention of their child status by applying
|
||||
* the SA_NOCLDWAIT flag with sigaction().
|
||||
*/
|
||||
|
||||
if ((rtcb->group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* Make sure that there is not already a structure for this PID in
|
||||
* the parent TCB. There should not be.
|
||||
*/
|
||||
|
||||
child = group_find_child(rtcb->group, tcb->pid);
|
||||
DEBUGASSERT(child == NULL);
|
||||
if (child == NULL)
|
||||
{
|
||||
/* Allocate a new status structure */
|
||||
|
||||
child = group_alloc_child();
|
||||
}
|
||||
|
||||
/* Did we successfully find/allocate the child status structure? */
|
||||
|
||||
DEBUGASSERT(child != NULL);
|
||||
if (child != NULL)
|
||||
{
|
||||
/* Yes.. Initialize the structure */
|
||||
|
||||
child->ch_flags = ttype;
|
||||
child->ch_pid = tcb->pid;
|
||||
child->ch_status = 0;
|
||||
|
||||
/* Add the entry into the group's list of children */
|
||||
|
||||
group_add_child(rtcb->group, child);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_SCHED_CHILD_STATUS */
|
||||
/* Child status is not retained. Simply keep track of the number
|
||||
* child tasks created.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(rtcb->group->tg_nchildren < UINT16_MAX);
|
||||
rtcb->group->tg_nchildren++;
|
||||
|
||||
#endif /* CONFIG_SCHED_CHILD_STATUS */
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define nxtask_save_parent(tcb,ttype)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_dup_dspace
|
||||
*
|
||||
* Description:
|
||||
* When a new task or thread is created from a PIC module, then that
|
||||
* module (probably) intends the task or thread to execute in the same
|
||||
* D-Space. This function will duplicate the D-Space for that purpose.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the new task.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* The parent of the new task is the task at the head of the ready-to-run
|
||||
* list.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_PIC
|
||||
static inline void nxtask_dup_dspace(FAR struct tcb_s *tcb)
|
||||
{
|
||||
FAR struct tcb_s *rtcb = this_task();
|
||||
if (rtcb->dspace != NULL)
|
||||
{
|
||||
/* Copy the D-Space structure reference and increment the reference
|
||||
* count on the memory. The D-Space memory will persist until the
|
||||
* last thread exits (see nxsched_release_tcb()).
|
||||
*/
|
||||
|
||||
tcb->dspace = rtcb->dspace;
|
||||
tcb->dspace->crefs++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define nxtask_dup_dspace(tcb)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxthread_setup_scheduler
|
||||
*
|
||||
* Description:
|
||||
* This functions initializes the common portions of the Task Control Block
|
||||
* (TCB) in preparation for starting a new thread.
|
||||
*
|
||||
* nxthread_setup_scheduler() is called from nxtask_setup_scheduler() and
|
||||
* pthread_setup_scheduler().
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* priority - Priority of the new task
|
||||
* start - Thread startup routine
|
||||
* entry - Thread user entry point
|
||||
* ttype - Type of the new thread: task, pthread, or kernel thread
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; ERROR on failure.
|
||||
*
|
||||
* This function can only failure is it is unable to assign a new, unique
|
||||
* task ID to the TCB (errno is not set).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nxthread_setup_scheduler(FAR struct tcb_s *tcb, int priority,
|
||||
start_t start, CODE void *entry,
|
||||
uint8_t ttype)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Assign a unique task ID to the task. */
|
||||
|
||||
ret = nxtask_assign_pid(tcb);
|
||||
if (ret == OK)
|
||||
{
|
||||
/* Save task priority and entry point in the TCB */
|
||||
|
||||
tcb->sched_priority = (uint8_t)priority;
|
||||
tcb->init_priority = (uint8_t)priority;
|
||||
#ifdef CONFIG_PRIORITY_INHERITANCE
|
||||
tcb->base_priority = (uint8_t)priority;
|
||||
#endif
|
||||
tcb->start = start;
|
||||
tcb->entry.main = (main_t)entry;
|
||||
|
||||
/* Save the thread type. This setting will be needed in
|
||||
* up_initial_state() is called.
|
||||
*/
|
||||
|
||||
ttype &= TCB_FLAG_TTYPE_MASK;
|
||||
tcb->flags &= ~TCB_FLAG_TTYPE_MASK;
|
||||
tcb->flags |= ttype;
|
||||
|
||||
/* Set the appropriate scheduling policy in the TCB */
|
||||
|
||||
tcb->flags &= ~TCB_FLAG_POLICY_MASK;
|
||||
#if CONFIG_RR_INTERVAL > 0
|
||||
tcb->flags |= TCB_FLAG_SCHED_RR;
|
||||
tcb->timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
|
||||
#else
|
||||
tcb->flags |= TCB_FLAG_SCHED_FIFO;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* Set the deferred cancellation type */
|
||||
|
||||
tcb->flags |= TCB_FLAG_CANCEL_DEFERRED;
|
||||
#endif
|
||||
|
||||
/* Save the task ID of the parent task in the TCB and allocate
|
||||
* a child status structure.
|
||||
*/
|
||||
|
||||
nxtask_save_parent(tcb, ttype);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* exec(), task_create(), and vfork() all inherit the affinity mask
|
||||
* from the parent thread. This is the default for pthread_create()
|
||||
* as well but the affinity mask can be specified in the pthread
|
||||
* attributes as well. pthread_create() will have to fix up the
|
||||
* affinity mask in this case.
|
||||
*/
|
||||
|
||||
nxtask_inherit_affinity(tcb);
|
||||
#endif
|
||||
|
||||
/* exec(), pthread_create(), task_create(), and vfork() all
|
||||
* inherit the signal mask of the parent thread.
|
||||
*/
|
||||
|
||||
nxsig_procmask(SIG_SETMASK, NULL, &tcb->sigprocmask);
|
||||
|
||||
/* Initialize the task state. It does not get a valid state
|
||||
* until it is activated.
|
||||
*/
|
||||
|
||||
tcb->task_state = TSTATE_TASK_INVALID;
|
||||
|
||||
/* Clone the parent tasks D-Space (if it was running PIC). This
|
||||
* must be done before calling up_initial_state() so that the
|
||||
* state setup will take the PIC address base into account.
|
||||
*/
|
||||
|
||||
nxtask_dup_dspace(tcb);
|
||||
|
||||
/* Initialize the processor-specific portion of the TCB */
|
||||
|
||||
up_initial_state(tcb);
|
||||
|
||||
/* Add the task to the inactive task list */
|
||||
|
||||
sched_lock();
|
||||
dq_addfirst((FAR dq_entry_t *)tcb, (FAR dq_queue_t *)&g_inactivetasks);
|
||||
tcb->task_state = TSTATE_TASK_INACTIVE;
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_setup_name
|
||||
*
|
||||
* Description:
|
||||
* Assign the task name.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* name - Name of the new task
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
static void nxtask_setup_name(FAR struct task_tcb_s *tcb,
|
||||
FAR const char *name)
|
||||
{
|
||||
/* Give a name to the unnamed tasks */
|
||||
|
||||
if (!name)
|
||||
{
|
||||
name = (FAR char *)g_noname;
|
||||
}
|
||||
|
||||
/* Copy the name into the TCB */
|
||||
|
||||
strncpy(tcb->cmn.name, name, CONFIG_TASK_NAME_SIZE);
|
||||
tcb->cmn.name[CONFIG_TASK_NAME_SIZE] = '\0';
|
||||
}
|
||||
#else
|
||||
# define nxtask_setup_name(t,n)
|
||||
#endif /* CONFIG_TASK_NAME_SIZE */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_setup_stackargs
|
||||
*
|
||||
* Description:
|
||||
* This functions is called only from nxtask_setup_arguments() It will
|
||||
* allocate space on the new task's stack and will copy the argv[] array
|
||||
* and all strings to the task's stack where it is readily accessible to
|
||||
* the task. Data on the stack, on the other hand, is guaranteed to be
|
||||
* accessible no matter what privilege mode the task runs in.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* argv - A pointer to an array of input parameters. The arrau should be
|
||||
* terminated with a NULL argv[] value. If no parameters are
|
||||
* required, argv may be NULL.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; a negated errno on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline int nxtask_setup_stackargs(FAR struct task_tcb_s *tcb,
|
||||
FAR char * const argv[])
|
||||
{
|
||||
FAR char **stackargv;
|
||||
FAR const char *name;
|
||||
FAR char *str;
|
||||
size_t strtablen;
|
||||
size_t argvlen;
|
||||
int nbytes;
|
||||
int argc;
|
||||
int i;
|
||||
|
||||
/* Get the name string that we will use as the first argument */
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
name = tcb->cmn.name;
|
||||
#else
|
||||
name = (FAR const char *)g_noname;
|
||||
#endif /* CONFIG_TASK_NAME_SIZE */
|
||||
|
||||
/* Get the size of the task name (including the NUL terminator) */
|
||||
|
||||
strtablen = (strlen(name) + 1);
|
||||
|
||||
/* Count the number of arguments and get the accumulated size of the
|
||||
* argument strings (including the null terminators). The argument count
|
||||
* does not include the task name in that will be in argv[0].
|
||||
*/
|
||||
|
||||
argc = 0;
|
||||
if (argv != NULL)
|
||||
{
|
||||
/* A NULL argument terminates the list */
|
||||
|
||||
while (argv[argc])
|
||||
{
|
||||
/* Add the size of this argument (with NUL terminator).
|
||||
* Check each time if the accumulated size exceeds the
|
||||
* size of the allocated stack.
|
||||
*/
|
||||
|
||||
strtablen += (strlen(argv[argc]) + 1);
|
||||
if (strtablen >= tcb->cmn.adj_stack_size)
|
||||
{
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
/* Increment the number of args. Here is a sanity check to
|
||||
* prevent running away with an unterminated argv[] list.
|
||||
* MAX_STACK_ARGS should be sufficiently large that this never
|
||||
* happens in normal usage.
|
||||
*/
|
||||
|
||||
if (++argc > MAX_STACK_ARGS)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a stack frame to hold argv[] array and the strings. NOTE
|
||||
* that argc + 2 entries are needed: The number of arguments plus the
|
||||
* task name plus a NULL argv[] entry to terminate the list.
|
||||
*/
|
||||
|
||||
argvlen = (argc + 2) * sizeof(FAR char *);
|
||||
stackargv = (FAR char **)up_stack_frame(&tcb->cmn, argvlen + strtablen);
|
||||
|
||||
DEBUGASSERT(stackargv != NULL);
|
||||
if (stackargv == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get the address of the string table that will lie immediately after
|
||||
* the argv[] array and mark it as a null string.
|
||||
*/
|
||||
|
||||
str = (FAR char *)stackargv + argvlen;
|
||||
|
||||
/* Copy the task name. Increment str to skip over the task name and its
|
||||
* NUL terminator in the string buffer.
|
||||
*/
|
||||
|
||||
stackargv[0] = str;
|
||||
nbytes = strlen(name) + 1;
|
||||
strcpy(str, name);
|
||||
str += nbytes;
|
||||
|
||||
/* Copy each argument */
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
/* Save the pointer to the location in the string buffer and copy
|
||||
* the argument into the buffer. Increment str to skip over the
|
||||
* argument and its NUL terminator in the string buffer.
|
||||
*/
|
||||
|
||||
stackargv[i + 1] = str;
|
||||
nbytes = strlen(argv[i]) + 1;
|
||||
strcpy(str, argv[i]);
|
||||
str += nbytes;
|
||||
}
|
||||
|
||||
/* Put a terminator entry at the end of the argv[] array. Then save the
|
||||
* argv[] array pointer in the TCB where it will be recovered later by
|
||||
* nxtask_start().
|
||||
*/
|
||||
|
||||
stackargv[argc + 1] = NULL;
|
||||
tcb->argv = stackargv;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_setup_scheduler
|
||||
*
|
||||
* Description:
|
||||
* This functions initializes a Task Control Block (TCB) in preparation
|
||||
* for starting a new task.
|
||||
*
|
||||
* nxtask_setup_scheduler() is called from nxtask_init() and
|
||||
* nxtask_start().
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* priority - Priority of the new task
|
||||
* start - Start-up function (probably nxtask_start())
|
||||
* main - Application start point of the new task
|
||||
* ttype - Type of the new thread: task or kernel thread
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; ERROR on failure.
|
||||
*
|
||||
* This function can only failure is it is unable to assign a new, unique
|
||||
* task ID to the TCB (errno is not set).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxtask_setup_scheduler(FAR struct task_tcb_s *tcb, int priority,
|
||||
start_t start, main_t main, uint8_t ttype)
|
||||
{
|
||||
/* Perform common thread setup */
|
||||
|
||||
return nxthread_setup_scheduler((FAR struct tcb_s *)tcb, priority,
|
||||
start, (CODE void *)main, ttype);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pthread_setup_scheduler
|
||||
*
|
||||
* Description:
|
||||
* This functions initializes a Task Control Block (TCB) in preparation
|
||||
* for starting a new pthread.
|
||||
*
|
||||
* pthread_setup_scheduler() is called from pthread_create(),
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* priority - Priority of the new task
|
||||
* start - Start-up function (probably pthread_start())
|
||||
* entry - Entry point of the new pthread
|
||||
* ttype - Type of the new thread: task, pthread, or kernel thread
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; ERROR on failure.
|
||||
*
|
||||
* This function can only failure is it is unable to assign a new, unique
|
||||
* task ID to the TCB (errno is not set).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
int pthread_setup_scheduler(FAR struct pthread_tcb_s *tcb, int priority,
|
||||
start_t start, pthread_startroutine_t entry)
|
||||
{
|
||||
/* Perform common thread setup */
|
||||
|
||||
return nxthread_setup_scheduler((FAR struct tcb_s *)tcb, priority,
|
||||
start, (CODE void *)entry,
|
||||
TCB_FLAG_TTYPE_PTHREAD);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_setup_arguments
|
||||
*
|
||||
* Description:
|
||||
* This functions sets up parameters in the Task Control Block (TCB) in
|
||||
* preparation for starting a new thread.
|
||||
*
|
||||
* nxtask_setup_arguments() is called only from nxtask_init() and
|
||||
* nxtask_start() to create a new task. In the "normal" case, the argv[]
|
||||
* array is a structure in the TCB, the arguments are cloned via strdup.
|
||||
*
|
||||
* In the kernel build case, the argv[] array and all strings are copied
|
||||
* to the task's stack. This is done because the TCB (and kernel allocated
|
||||
* strings) are only accessible in kernel-mode. Data on the stack, on the
|
||||
* other hand, is guaranteed to be accessible no matter what mode the
|
||||
* task runs in.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* name - Name of the new task (not used)
|
||||
* argv - 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.
|
||||
*
|
||||
* Returned Value:
|
||||
* OK
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxtask_setup_arguments(FAR struct task_tcb_s *tcb, FAR const char *name,
|
||||
FAR char * const argv[])
|
||||
{
|
||||
/* Setup the task name */
|
||||
|
||||
nxtask_setup_name(tcb, name);
|
||||
|
||||
/* Copy the argv[] array and all strings are to the task's stack. Data on
|
||||
* the stack is guaranteed to be accessible by the ask no matter what
|
||||
* privilege mode the task runs in.
|
||||
*/
|
||||
|
||||
return nxtask_setup_stackargs(tcb, argv);
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_spawn.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sched.h>
|
||||
#include <spawn.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/spawn.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/spawn.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifndef CONFIG_BUILD_KERNEL
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_spawn_exec
|
||||
*
|
||||
* Description:
|
||||
* Execute the task from the file system.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pidp - Upon successful completion, this will return the task ID of the
|
||||
* child task in the variable pointed to by a non-NULL 'pid' argument.|
|
||||
*
|
||||
* name - The name to assign to the child task.
|
||||
*
|
||||
* entry - The child task's entry point (an address in memory)
|
||||
*
|
||||
* 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
|
||||
* following spawn flags are supported:
|
||||
*
|
||||
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
||||
* value.
|
||||
* - POSIX_SPAWN_SETSCHEDULER: Set the new tasks scheduler priority to
|
||||
* the sched_policy value.
|
||||
*
|
||||
* NOTE: POSIX_SPAWN_SETSIGMASK is handled in nxtask_spawn_proxy().
|
||||
*
|
||||
* argv - argv[] is the argument list for the new task. argv[] is an
|
||||
* array of pointers to null-terminated strings. The list is terminated
|
||||
* with a null pointer.
|
||||
*
|
||||
* Returned Value:
|
||||
* This function will return zero on success. Otherwise, an error number
|
||||
* will be returned as the function return value to indicate the error.
|
||||
* This errno value may be that set by execv(), sched_setpolicy(), or
|
||||
* sched_setparam().
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nxtask_spawn_exec(FAR pid_t *pidp, FAR const char *name,
|
||||
main_t entry, FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const *argv)
|
||||
{
|
||||
size_t stacksize;
|
||||
int priority;
|
||||
int pid;
|
||||
int ret = OK;
|
||||
|
||||
/* Disable pre-emption so that we can modify the task parameters after
|
||||
* we start the new task; the new task will not actually begin execution
|
||||
* until we re-enable pre-emption.
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Use the default priority and stack size if no attributes are provided */
|
||||
|
||||
if (attr)
|
||||
{
|
||||
priority = attr->priority;
|
||||
stacksize = attr->stacksize;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct sched_param param;
|
||||
|
||||
/* Set the default priority to the same priority as this task */
|
||||
|
||||
ret = nxsched_get_param(0, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = -ret;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
priority = param.sched_priority;
|
||||
stacksize = CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE;
|
||||
}
|
||||
|
||||
/* Start the task */
|
||||
|
||||
pid = nxtask_create(name, priority, stacksize, entry, argv);
|
||||
if (pid < 0)
|
||||
{
|
||||
ret = -pid;
|
||||
serr("ERROR: nxtask_create failed: %d\n", ret);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Return the task ID to the caller */
|
||||
|
||||
if (pid)
|
||||
{
|
||||
*pidp = pid;
|
||||
}
|
||||
|
||||
/* Now set the attributes. Note that we ignore all of the return values
|
||||
* here because we have already successfully started the task. If we
|
||||
* return an error value, then we would also have to stop the task.
|
||||
*/
|
||||
|
||||
if (attr)
|
||||
{
|
||||
spawn_execattrs(pid, attr);
|
||||
}
|
||||
|
||||
/* Re-enable pre-emption and return */
|
||||
|
||||
errout:
|
||||
sched_unlock();
|
||||
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:
|
||||
* Standard task start-up parameters
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
#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
|
||||
spawn_semgive(&g_spawn_execsem);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_spawn/_task_spawn
|
||||
*
|
||||
* Description:
|
||||
* The task_spawn() function will create a new, child task, where the
|
||||
* entry point to the task is an address in memory.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pid - Upon successful completion, task_spawn() will return the task ID
|
||||
* of the child task to the parent task, in the variable pointed to by
|
||||
* a non-NULL 'pid' argument. If the 'pid' argument is a null pointer,
|
||||
* the process ID of the child is not returned to the caller.
|
||||
*
|
||||
* name - The name to assign to the child task.
|
||||
*
|
||||
* entry - The child task's entry point (an address in memory)
|
||||
*
|
||||
* file_actions - If 'file_actions' is a null pointer, then file
|
||||
* descriptors open in the calling process will remain open in the
|
||||
* child process (unless CONFIG_FDCLONE_STDIO is defined). If
|
||||
* 'file_actions' is not NULL, then the file descriptors open in the
|
||||
* child process will be those open in the calling process as modified
|
||||
* by the spawn file actions object pointed to by 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
|
||||
* task_spawnattr_t spawn attributes object type is defined in spawn.h.
|
||||
* It will contains these attributes, not all of which are supported by
|
||||
* NuttX:
|
||||
*
|
||||
* - POSIX_SPAWN_SETPGROUP: Setting of the new task's process group is
|
||||
* not supported. NuttX does not support process groups.
|
||||
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
||||
* value.
|
||||
* - POSIX_SPAWN_SETSCHEDULER: Set the new task's scheduler policy to
|
||||
* the sched_policy value.
|
||||
* - POSIX_SPAWN_RESETIDS: Resetting of the effective user ID of the
|
||||
* child process is not supported. NuttX does not support effective
|
||||
* user IDs.
|
||||
* - POSIX_SPAWN_SETSIGMASK: Set the new task's signal mask.
|
||||
* - POSIX_SPAWN_SETSIGDEF: Resetting signal default actions is not
|
||||
* supported. NuttX does not support default signal actions.
|
||||
*
|
||||
* And the non-standard:
|
||||
*
|
||||
* - TASK_SPAWN_SETSTACKSIZE: Set the stack size for the new task.
|
||||
*
|
||||
* argv - argv[] is the argument list for the new task. argv[] is an
|
||||
* array of pointers to null-terminated strings. The list is terminated
|
||||
* with a null pointer.
|
||||
*
|
||||
* envp - The envp[] argument is not used by NuttX and may be NULL.
|
||||
*
|
||||
* Returned Value:
|
||||
* task_spawn() will return zero on success. Otherwise, an error number
|
||||
* will be returned as the function return value to indicate the error:
|
||||
*
|
||||
* - EINVAL: The value specified by 'file_actions' or 'attr' is invalid.
|
||||
* - Any errors that might have been return if vfork() and excec[l|v]()
|
||||
* had been called.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_LIB_SYSCALL
|
||||
static int _task_spawn(FAR pid_t *pid, FAR const char *name, main_t entry,
|
||||
FAR const posix_spawn_file_actions_t *file_actions,
|
||||
FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const argv[], FAR char * const envp[])
|
||||
#else
|
||||
int task_spawn(FAR pid_t *pid, FAR const char *name, main_t entry,
|
||||
FAR const posix_spawn_file_actions_t *file_actions,
|
||||
FAR const posix_spawnattr_t *attr,
|
||||
FAR char * const argv[], FAR char * const envp[])
|
||||
#endif
|
||||
{
|
||||
struct sched_param param;
|
||||
pid_t proxy;
|
||||
#ifdef CONFIG_SCHED_WAITPID
|
||||
int status;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
sinfo("pid=%p name=%s entry=%p file_actions=%p attr=%p argv=%p\n",
|
||||
pid, 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))
|
||||
{
|
||||
return nxtask_spawn_exec(pid, name, entry, attr, argv);
|
||||
}
|
||||
|
||||
/* 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 = spawn_semtake(&g_spawn_parmsem);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: spawn_semtake 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.u.task.name = name;
|
||||
g_spawn_parms.u.task.entry = entry;
|
||||
|
||||
/* Get the priority of this (parent) task */
|
||||
|
||||
ret = nxsched_get_param(0, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: nxsched_get_param failed: %d\n", ret);
|
||||
spawn_semgive(&g_spawn_parmsem);
|
||||
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,
|
||||
CONFIG_POSIX_SPAWN_PROXY_STACKSIZE,
|
||||
(main_t)nxtask_spawn_proxy,
|
||||
(FAR char * const *)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 = nx_waitpid(proxy, &status, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: waitpid() failed: %d\n", ret);
|
||||
goto errout_with_lock;
|
||||
}
|
||||
#else
|
||||
ret = spawn_semtake(&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;
|
||||
|
||||
errout_with_lock:
|
||||
#ifdef CONFIG_SCHED_WAITPID
|
||||
sched_unlock();
|
||||
#endif
|
||||
spawn_semgive(&g_spawn_parmsem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nx_task_spawn
|
||||
*
|
||||
* Description:
|
||||
* This function de-marshals parameters and invokes _task_spawn().
|
||||
*
|
||||
* task_spawn() and posix_spawn() are NuttX OS interfaces. In PROTECTED
|
||||
* and KERNEL build modes, then can be reached from applications only via
|
||||
* a system call. Currently, the number of parameters in a system call
|
||||
* is limited to six; these spawn function have seven parameters. Rather
|
||||
* than extend the maximum number of parameters across all architectures,
|
||||
* I opted instead to marshal the seven parameters into a structure.
|
||||
*
|
||||
* Input Parameters:
|
||||
* parms - The marshaled task_spawn() parameters.
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, these functions return 0; on failure they return an error
|
||||
* number from <errno.h> (see the comments associated with task_spawn()).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_LIB_SYSCALL
|
||||
int nx_task_spawn(FAR const struct spawn_syscall_parms_s *parms)
|
||||
{
|
||||
DEBUGASSERT(parms != NULL);
|
||||
return _task_spawn(parms->pid, parms->name, parms->entry,
|
||||
parms->file_actions, parms->attr,
|
||||
parms->argv, parms->envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_BUILD_KERNEL */
|
||||
@@ -0,0 +1,342 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_spawnparms.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <spawn.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/signal.h>
|
||||
#include <nuttx/spawn.h>
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include "task/spawn.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
sem_t g_spawn_parmsem = SEM_INITIALIZER(1);
|
||||
#ifndef CONFIG_SCHED_WAITPID
|
||||
sem_t g_spawn_execsem = SEM_INITIALIZER(0);
|
||||
#endif
|
||||
struct spawn_parms_s g_spawn_parms;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxspawn_close, nxspawn_dup2, and nxspawn_open
|
||||
*
|
||||
* Description:
|
||||
* Implement individual file actions
|
||||
*
|
||||
* Input Parameters:
|
||||
* action - describes the action to be performed
|
||||
*
|
||||
* Returned Value:
|
||||
* posix_spawn() and posix_spawnp() will return zero on success.
|
||||
* Otherwise, an error number will be returned as the function return
|
||||
* value to indicate the error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline int nxspawn_close(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;
|
||||
}
|
||||
|
||||
static inline int nxspawn_dup2(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;
|
||||
}
|
||||
|
||||
static inline int nxspawn_open(FAR struct spawn_open_file_action_s *action)
|
||||
{
|
||||
int fd;
|
||||
int ret = OK;
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
}
|
||||
|
||||
sinfo("Closing fd=%d\n", fd);
|
||||
nx_close(fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_semtake and spawn_semgive
|
||||
*
|
||||
* Description:
|
||||
* Give and take semaphores
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* sem - The semaphore to act on.
|
||||
*
|
||||
* Returned Value:
|
||||
* See nxsem_wait_uninterruptible
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int spawn_semtake(FAR sem_t *sem)
|
||||
{
|
||||
return nxsem_wait_uninterruptible(sem);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_execattrs
|
||||
*
|
||||
* Description:
|
||||
* Set attributes of the new child task after it has been spawned.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* pid - The pid of the new task.
|
||||
* attr - The attributes to use
|
||||
*
|
||||
* Returned Value:
|
||||
* Errors are not reported by this function. This is not because errors
|
||||
* cannot occur, but rather that the new task has already been started
|
||||
* so there is no graceful way to handle errors detected in this context
|
||||
* (unless we delete the new task and recover).
|
||||
*
|
||||
* Assumptions:
|
||||
* That task has been started but has not yet executed because pre-
|
||||
* emption is disabled.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr)
|
||||
{
|
||||
struct sched_param param;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(attr);
|
||||
|
||||
/* Now set the attributes. Note that we ignore all of the return values
|
||||
* here because we have already successfully started the task. If we
|
||||
* return an error value, then we would also have to stop the task.
|
||||
*/
|
||||
|
||||
/* If we are only setting the priority, then call sched_setparm()
|
||||
* to set the priority of the of the new task.
|
||||
*/
|
||||
|
||||
if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_SPORADIC
|
||||
/* Get the current sporadic scheduling parameters. Those will not be
|
||||
* modified.
|
||||
*/
|
||||
|
||||
ret = nxsched_get_param(pid, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get the priority from the attributes */
|
||||
|
||||
param.sched_priority = attr->priority;
|
||||
|
||||
/* If we are setting *both* the priority and the scheduler,
|
||||
* then we will call nxsched_set_scheduler() below.
|
||||
*/
|
||||
|
||||
if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0)
|
||||
{
|
||||
sinfo("Setting priority=%d for pid=%d\n",
|
||||
param.sched_priority, pid);
|
||||
|
||||
ret = nxsched_set_param(pid, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are only changing the scheduling policy, then reset
|
||||
* the priority to the default value (the same as this thread) in
|
||||
* preparation for the nxsched_set_scheduler() call below.
|
||||
*/
|
||||
|
||||
else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
|
||||
{
|
||||
ret = nxsched_get_param(0, ¶m);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Are we setting the scheduling policy? If so, use the priority
|
||||
* setting determined above.
|
||||
*/
|
||||
|
||||
if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0)
|
||||
{
|
||||
sinfo("Setting policy=%d priority=%d for pid=%d\n",
|
||||
attr->policy, param.sched_priority, pid);
|
||||
|
||||
#ifdef CONFIG_SCHED_SPORADIC
|
||||
/* But take the sporadic scheduler parameters from the attributes */
|
||||
|
||||
param.sched_ss_low_priority = (int)attr->low_priority;
|
||||
param.sched_ss_max_repl = (int)attr->max_repl;
|
||||
param.sched_ss_repl_period.tv_sec = attr->repl_period.tv_sec;
|
||||
param.sched_ss_repl_period.tv_nsec = attr->repl_period.tv_nsec;
|
||||
param.sched_ss_init_budget.tv_sec = attr->budget.tv_sec;
|
||||
param.sched_ss_init_budget.tv_nsec = attr->budget.tv_nsec;
|
||||
#endif
|
||||
nxsched_set_scheduler(pid, attr->policy, ¶m);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spawn_proxyattrs
|
||||
*
|
||||
* Description:
|
||||
* Set attributes of the proxy task before it has started the new child
|
||||
* task.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
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? */
|
||||
|
||||
if (file_actions != NULL)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
case SPAWN_FILE_ACTION_CLOSE:
|
||||
ret = nxspawn_close((FAR struct spawn_close_file_action_s *)
|
||||
entry);
|
||||
break;
|
||||
|
||||
case SPAWN_FILE_ACTION_DUP2:
|
||||
ret = nxspawn_dup2((FAR struct spawn_dup2_file_action_s *)
|
||||
entry);
|
||||
break;
|
||||
|
||||
case SPAWN_FILE_ACTION_OPEN:
|
||||
ret = nxspawn_open((FAR struct spawn_open_file_action_s *)
|
||||
entry);
|
||||
break;
|
||||
|
||||
case SPAWN_FILE_ACTION_NONE:
|
||||
default:
|
||||
serr("ERROR: Unknown action: %d\n", entry->action);
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_start.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "group/group.h"
|
||||
#include "sched/sched.h"
|
||||
#include "signal/signal.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* This is an artificial limit to detect error conditions where an argv[]
|
||||
* list is not properly terminated.
|
||||
*/
|
||||
|
||||
#define MAX_START_ARGS 256
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_start
|
||||
*
|
||||
* Description:
|
||||
* This function is the low level entry point into the main thread of
|
||||
* execution of a task. It receives initial control when the task is
|
||||
* started and calls main entry point of the newly started task.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_start(void)
|
||||
{
|
||||
FAR struct task_tcb_s *tcb = (FAR struct task_tcb_s *)this_task();
|
||||
int exitcode = EXIT_FAILURE;
|
||||
int argc;
|
||||
|
||||
DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) != \
|
||||
TCB_FLAG_TTYPE_PTHREAD);
|
||||
|
||||
#ifdef CONFIG_SIG_DEFAULT
|
||||
/* Set up default signal actions */
|
||||
|
||||
nxsig_default_initialize(&tcb->cmn);
|
||||
#endif
|
||||
|
||||
/* Execute the start hook if one has been registered */
|
||||
|
||||
#ifdef CONFIG_SCHED_STARTHOOK
|
||||
if (tcb->starthook != NULL)
|
||||
{
|
||||
tcb->starthook(tcb->starthookarg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Count how many non-null arguments we are passing. The first non-null
|
||||
* argument terminates the list .
|
||||
*/
|
||||
|
||||
argc = 1;
|
||||
while (tcb->argv[argc])
|
||||
{
|
||||
/* Increment the number of args. Here is a sanity check to
|
||||
* prevent running away with an unterminated argv[] list.
|
||||
* MAX_START_ARGS should be sufficiently large that this never
|
||||
* happens in normal usage.
|
||||
*/
|
||||
|
||||
if (++argc > MAX_START_ARGS)
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the 'main' entry point passing argc and argv. In the kernel build
|
||||
* this has to be handled differently if we are starting a user-space task;
|
||||
* we have to switch to user-mode before calling the task.
|
||||
*/
|
||||
|
||||
if ((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
|
||||
{
|
||||
exitcode = tcb->cmn.entry.main(argc, tcb->argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CONFIG_BUILD_FLAT
|
||||
nxtask_startup(tcb->cmn.entry.main, argc, tcb->argv);
|
||||
#else
|
||||
up_task_start(tcb->cmn.entry.main, argc, tcb->argv);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Call exit() if/when the task returns */
|
||||
|
||||
exit(exitcode);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_starthook.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef CONFIG_SCHED_STARTHOOK
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_starthook
|
||||
*
|
||||
* Description:
|
||||
* Configure a start hook... a function that will be called on the thread
|
||||
* of the new task before the new task's main entry point is called.
|
||||
* The start hook is useful, for example, for setting up automatic
|
||||
* configuration of C++ constructors.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The new, unstarted task task that needs the start hook
|
||||
* starthook - The pointer to the start hook function
|
||||
* arg - The argument to pass to the start hook function.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_starthook(FAR struct task_tcb_s *tcb, starthook_t starthook,
|
||||
FAR void *arg)
|
||||
{
|
||||
/* Only tasks can have starthooks. The starthook will be called when the
|
||||
* task is started (or restarted).
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
DEBUGASSERT(tcb &&
|
||||
(tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) !=
|
||||
TCB_FLAG_TTYPE_PTHREAD);
|
||||
#endif
|
||||
|
||||
/* Set up the start hook */
|
||||
|
||||
tcb->starthook = starthook;
|
||||
tcb->starthookarg = arg;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_STARTHOOK */
|
||||
@@ -0,0 +1,189 @@
|
||||
/*******************************************************************************
|
||||
* sched/task/task_terminate.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Included Files
|
||||
*******************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <queue.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "signal/signal.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* Public Functions
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Name: nxtask_terminate
|
||||
*
|
||||
* Description:
|
||||
* This function causes a specified task to cease to exist. Its stack and
|
||||
* TCB will be deallocated. This function is the internal implementation
|
||||
* of the task_delete() function. It includes and additional parameter
|
||||
* 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).
|
||||
*
|
||||
* The path through nxtask_exit() supports the final stops of the exit(),
|
||||
* _exit(), and pthread_exit
|
||||
*
|
||||
* - 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;
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This function can fail if the provided pid does not correspond to a
|
||||
* task (errno is not set)
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
int nxtask_terminate(pid_t pid, bool nonblocking)
|
||||
{
|
||||
FAR struct tcb_s *dtcb;
|
||||
FAR dq_queue_t *tasklist;
|
||||
irqstate_t flags;
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
/* Make sure the task does not become ready-to-run while we are futzing
|
||||
* with its TCB. Within the critical section, no new task may be started
|
||||
* or terminated (even in the SMP case).
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Find for the TCB associated with matching PID */
|
||||
|
||||
dtcb = nxsched_get_tcb(pid);
|
||||
if (!dtcb)
|
||||
{
|
||||
/* This PID does not correspond to any known task */
|
||||
|
||||
ret = -ESRCH;
|
||||
goto errout_with_lock;
|
||||
}
|
||||
|
||||
/* Verify our internal sanity */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
DEBUGASSERT(dtcb->task_state < NUM_TASK_STATES);
|
||||
#else
|
||||
DEBUGASSERT(dtcb->task_state != TSTATE_TASK_RUNNING &&
|
||||
dtcb->task_state < NUM_TASK_STATES);
|
||||
#endif
|
||||
|
||||
/* Remove the task from the OS's task lists. We must be in a critical
|
||||
* section and the must must not be running to do this.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* In the SMP case, the thread may be running on another CPU. If that is
|
||||
* the case, then we will pause the CPU that the thread is running on.
|
||||
*/
|
||||
|
||||
cpu = nxsched_pause_cpu(dtcb);
|
||||
|
||||
/* Get the task list associated with the thread's state and CPU */
|
||||
|
||||
tasklist = TLIST_HEAD(dtcb->task_state, cpu);
|
||||
#else
|
||||
/* In the non-SMP case, we can be assured that the task to be terminated
|
||||
* is not running. get the task list associated with the task state.
|
||||
*/
|
||||
|
||||
tasklist = TLIST_HEAD(dtcb->task_state);
|
||||
#endif
|
||||
|
||||
/* Remove the task from the task list */
|
||||
|
||||
dq_rem((FAR dq_entry_t *)dtcb, tasklist);
|
||||
|
||||
/* At this point, the TCB should no longer be accessible to the system */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Resume the paused CPU (if any) */
|
||||
|
||||
if (cpu >= 0)
|
||||
{
|
||||
/* I am not yet sure how to handle a failure here. */
|
||||
|
||||
DEBUGVERIFY(up_cpu_resume(cpu));
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
/* Perform common task termination logic (flushing streams, calling
|
||||
* functions registered by at_exit/on_exit, etc.). 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);
|
||||
|
||||
/* Since all tasks pass through this function as the final step in their
|
||||
* exit sequence, this is an appropriate place to inform any instrumentation
|
||||
* layer that the task no longer exists.
|
||||
*/
|
||||
|
||||
sched_note_stop(dtcb);
|
||||
|
||||
/* Deallocate its TCB */
|
||||
|
||||
return nxsched_release_tcb(dtcb, dtcb->flags & TCB_FLAG_TTYPE_MASK);
|
||||
|
||||
errout_with_lock:
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_testcancel.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/cancelpt.h>
|
||||
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_testcancel
|
||||
*
|
||||
* Description:
|
||||
* The task_testcancel() function creates a cancellation point in the
|
||||
* calling thread. The task_testcancel() function has no effect if
|
||||
* cancellability is disabled
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void task_testcancel(void)
|
||||
{
|
||||
enter_cancellation_point();
|
||||
leave_cancellation_point();
|
||||
}
|
||||
@@ -0,0 +1,496 @@
|
||||
/****************************************************************************
|
||||
* sched/task/task_vfork.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <stdint.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <queue.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#include "group/group.h"
|
||||
#include "task/task.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* vfork() requires architecture-specific support as well as waipid(). */
|
||||
|
||||
#if defined(CONFIG_ARCH_HAVE_VFORK) && defined(CONFIG_SCHED_WAITPID)
|
||||
|
||||
/* This is an artificial limit to detect error conditions where an argv[]
|
||||
* list is not properly terminated.
|
||||
*/
|
||||
|
||||
#define MAX_VFORK_ARGS 256
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxvfork_setup_name
|
||||
*
|
||||
* Description:
|
||||
* Copy the task name.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - Address of the new task's TCB
|
||||
* name - Name of the new task
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
static inline void nxvfork_setup_name(FAR struct tcb_s *parent,
|
||||
FAR struct task_tcb_s *child)
|
||||
{
|
||||
/* Copy the name from the parent into the child TCB */
|
||||
|
||||
strncpy(child->cmn.name, parent->name, CONFIG_TASK_NAME_SIZE);
|
||||
}
|
||||
#else
|
||||
# define nxvfork_setup_name(p,c)
|
||||
#endif /* CONFIG_TASK_NAME_SIZE */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxvfork_setup_stackargs
|
||||
*
|
||||
* Description:
|
||||
* Clone the task arguments in the same relative positions on the child's
|
||||
* stack.
|
||||
*
|
||||
* Input Parameters:
|
||||
* parent - Address of the parent task's TCB
|
||||
* child - Address of the child task's TCB
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; a negated errno on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline int nxvfork_setup_stackargs(FAR struct tcb_s *parent,
|
||||
FAR struct task_tcb_s *child)
|
||||
{
|
||||
/* Is the parent a task? or a pthread? Only tasks (and kernel threads)
|
||||
* have command line arguments.
|
||||
*/
|
||||
|
||||
child->argv = NULL;
|
||||
if ((parent->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
FAR struct task_tcb_s *ptcb = (FAR struct task_tcb_s *)parent;
|
||||
uintptr_t offset;
|
||||
int argc;
|
||||
|
||||
/* Get the address correction */
|
||||
|
||||
offset = (uintptr_t)child->cmn.adj_stack_ptr -
|
||||
(uintptr_t)parent->adj_stack_ptr;
|
||||
|
||||
/* Change the child argv[] to point into its stack (instead of its
|
||||
* parent's stack).
|
||||
*/
|
||||
|
||||
child->argv = (FAR char **)((uintptr_t)ptcb->argv + offset);
|
||||
|
||||
/* Copy the adjusted address for each argument */
|
||||
|
||||
argc = 0;
|
||||
while (ptcb->argv[argc])
|
||||
{
|
||||
uintptr_t newaddr = (uintptr_t)ptcb->argv[argc] + offset;
|
||||
child->argv[argc] = (FAR char *)newaddr;
|
||||
|
||||
/* Increment the number of args. Here is a sanity check to
|
||||
* prevent running away with an unterminated argv[] list.
|
||||
* MAX_VFORK_ARGS should be sufficiently large that this never
|
||||
* happens in normal usage.
|
||||
*/
|
||||
|
||||
if (++argc > MAX_VFORK_ARGS)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put a terminator entry at the end of the child argv[] array. */
|
||||
|
||||
child->argv[argc] = NULL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxvfork_setup_arguments
|
||||
*
|
||||
* Description:
|
||||
* Clone the argument list from the parent to the child.
|
||||
*
|
||||
* Input Parameters:
|
||||
* parent - Address of the parent task's TCB
|
||||
* child - Address of the child task's TCB
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; a negated errno on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline int nxvfork_setup_arguments(FAR struct tcb_s *parent,
|
||||
FAR struct task_tcb_s *child)
|
||||
{
|
||||
/* Clone the task name */
|
||||
|
||||
nxvfork_setup_name(parent, child);
|
||||
|
||||
/* Adjust and copy the argv[] array. */
|
||||
|
||||
return nxvfork_setup_stackargs(parent, child);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxvfork_sizeof_arguments
|
||||
*
|
||||
* Description:
|
||||
* Get the parent's argument size.
|
||||
*
|
||||
* Input Parameters:
|
||||
* parent - Address of the parent task's TCB
|
||||
*
|
||||
* Return Value:
|
||||
* The parent's argument size.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline size_t nxvfork_sizeof_arguments(FAR struct tcb_s *parent)
|
||||
{
|
||||
if ((parent->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
||||
{
|
||||
FAR struct task_tcb_s *ptcb = (FAR struct task_tcb_s *)parent;
|
||||
size_t strtablen = 0;
|
||||
int argc = 0;
|
||||
|
||||
while (ptcb->argv[argc])
|
||||
{
|
||||
/* Add the size of this argument (with NUL terminator) */
|
||||
|
||||
strtablen += strlen(ptcb->argv[argc++]) + 1;
|
||||
}
|
||||
|
||||
/* Return the size to hold argv[] array and the strings. */
|
||||
|
||||
return (argc + 1) * sizeof(FAR char *) + strtablen;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_setup_vfork
|
||||
*
|
||||
* Description:
|
||||
* The vfork() function has the same effect as fork(), except that the
|
||||
* behavior is undefined if the process created by vfork() either modifies
|
||||
* any data other than a variable of type pid_t used to store the return
|
||||
* value from vfork(), or returns from the function in which vfork() was
|
||||
* called, or calls any other function before successfully calling _exit()
|
||||
* or one of the exec family of functions.
|
||||
*
|
||||
* This function provides one step in the overall vfork() sequence: It
|
||||
* Allocates and initializes the child task's TCB. The overall sequence
|
||||
* is:
|
||||
*
|
||||
* 1) User code calls vfork(). vfork() is provided in
|
||||
* architecture-specific code.
|
||||
* 2) vfork()and calls nxtask_setup_vfork().
|
||||
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB.
|
||||
* This consists of:
|
||||
* - Allocation of the child task's TCB.
|
||||
* - Initialization of file descriptors and streams
|
||||
* - Configuration of environment variables
|
||||
* - Setup the input parameters for the task.
|
||||
* - Initialization of the TCB (including call to up_initial_state()
|
||||
* 4) up_vfork() provides any additional operating context. up_vfork must:
|
||||
* - Allocate and initialize the stack
|
||||
* - Initialize special values in any CPU registers that were not
|
||||
* already configured by up_initial_state()
|
||||
* 5) up_vfork() then calls nxtask_start_vfork()
|
||||
* 6) nxtask_start_vfork() then executes the child thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* retaddr - Return address
|
||||
* argsize - Location to return the argument size
|
||||
*
|
||||
* Returned Value:
|
||||
* Upon successful completion, nxtask_setup_vfork() returns a pointer to
|
||||
* newly allocated and initialized child task's TCB. NULL is returned
|
||||
* on any failure and the errno is set appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct task_tcb_s *nxtask_setup_vfork(start_t retaddr, size_t *argsize)
|
||||
{
|
||||
FAR struct tcb_s *parent = this_task();
|
||||
FAR struct task_tcb_s *child;
|
||||
uint8_t ttype;
|
||||
int priority;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(retaddr != NULL && argsize != NULL);
|
||||
|
||||
/* Get the type of the fork'ed task (kernel or user) */
|
||||
|
||||
if ((parent->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
|
||||
{
|
||||
/* Fork'ed from a kernel thread */
|
||||
|
||||
ttype = TCB_FLAG_TTYPE_KERNEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fork'ed from a user task or pthread */
|
||||
|
||||
ttype = TCB_FLAG_TTYPE_TASK;
|
||||
}
|
||||
|
||||
/* Allocate a TCB for the child task. */
|
||||
|
||||
child = (FAR struct task_tcb_s *)kmm_zalloc(sizeof(struct task_tcb_s));
|
||||
if (!child)
|
||||
{
|
||||
serr("ERROR: Failed to allocate TCB\n");
|
||||
set_errno(ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate a new task group with the same privileges as the parent */
|
||||
|
||||
ret = group_allocate(child, parent->flags);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_tcb;
|
||||
}
|
||||
|
||||
/* Associate file descriptors with the new task */
|
||||
|
||||
ret = group_setuptaskfiles(child);
|
||||
if (ret < OK)
|
||||
{
|
||||
goto errout_with_tcb;
|
||||
}
|
||||
|
||||
/* Get the priority of the parent task */
|
||||
|
||||
#ifdef CONFIG_PRIORITY_INHERITANCE
|
||||
priority = parent->base_priority; /* "Normal," unboosted priority */
|
||||
#else
|
||||
priority = parent->sched_priority; /* Current priority */
|
||||
#endif
|
||||
|
||||
/* Initialize the task control block. This calls up_initial_state() */
|
||||
|
||||
sinfo("Child priority=%d start=%p\n", priority, retaddr);
|
||||
ret = nxtask_setup_scheduler(child, priority, retaddr, parent->entry.main,
|
||||
ttype);
|
||||
if (ret < OK)
|
||||
{
|
||||
goto errout_with_tcb;
|
||||
}
|
||||
|
||||
/* Return the argument size */
|
||||
|
||||
*argsize = nxvfork_sizeof_arguments(parent);
|
||||
|
||||
sinfo("parent=%p, returning child=%p\n", parent, child);
|
||||
return child;
|
||||
|
||||
errout_with_tcb:
|
||||
nxsched_release_tcb((FAR struct tcb_s *)child, ttype);
|
||||
set_errno(-ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_start_vfork
|
||||
*
|
||||
* Description:
|
||||
* The vfork() function has the same effect as fork(), except that the
|
||||
* behavior is undefined if the process created by vfork() either modifies
|
||||
* any data other than a variable of type pid_t used to store the return
|
||||
* value from vfork(), or returns from the function in which vfork() was
|
||||
* called, or calls any other function before successfully calling _exit()
|
||||
* or one of the exec family of functions.
|
||||
*
|
||||
* This function provides one step in the overall vfork() sequence: It
|
||||
* starts execution of the previously initialized TCB. The overall
|
||||
* sequence is:
|
||||
*
|
||||
* 1) User code calls vfork()
|
||||
* 2) Architecture-specific code provides vfork()and calls
|
||||
* nxtask_setup_vfork().
|
||||
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB.
|
||||
* This consists of:
|
||||
* - Allocation of the child task's TCB.
|
||||
* - Initialization of file descriptors and streams
|
||||
* - Configuration of environment variables
|
||||
* - Setup the input parameters for the task.
|
||||
* - Initialization of the TCB (including call to up_initial_state()
|
||||
* 4) vfork() provides any additional operating context. vfork must:
|
||||
* - Allocate and initialize the stack
|
||||
* - Initialize special values in any CPU registers that were not
|
||||
* already configured by up_initial_state()
|
||||
* 5) vfork() then calls nxtask_start_vfork()
|
||||
* 6) nxtask_start_vfork() then executes the child thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* retaddr - The return address from vfork() where the child task
|
||||
* will be started.
|
||||
*
|
||||
* Returned Value:
|
||||
* Upon successful completion, vfork() returns 0 to the child process and
|
||||
* returns the process ID of the child process to the parent process.
|
||||
* Otherwise, -1 is returned to the parent, no child process is created,
|
||||
* and errno is set to indicate the error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pid_t nxtask_start_vfork(FAR struct task_tcb_s *child)
|
||||
{
|
||||
FAR struct tcb_s *parent = this_task();
|
||||
pid_t pid;
|
||||
int rc;
|
||||
int ret;
|
||||
|
||||
sinfo("Starting Child TCB=%p, parent=%p\n", child, this_task());
|
||||
DEBUGASSERT(child);
|
||||
|
||||
/* Duplicate the original argument list in the forked child TCB */
|
||||
|
||||
ret = nxvfork_setup_arguments(parent, child);
|
||||
if (ret < 0)
|
||||
{
|
||||
nxtask_abort_vfork(child, -ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Now we have enough in place that we can join the group */
|
||||
|
||||
ret = group_initialize(child);
|
||||
if (ret < 0)
|
||||
{
|
||||
nxtask_abort_vfork(child, -ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Get the assigned pid before we start the task */
|
||||
|
||||
pid = (int)child->cmn.pid;
|
||||
|
||||
/* Eliminate a race condition by disabling pre-emption. The child task
|
||||
* can be instantiated, but cannot run until we call waitpid(). This
|
||||
* assures us that we cannot miss the death-of-child signal (only
|
||||
* needed in the SMP case).
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Activate the task */
|
||||
|
||||
nxtask_activate((FAR struct tcb_s *)child);
|
||||
|
||||
/* The child task has not yet ran because pre-emption is disabled.
|
||||
* The child task has the same priority as the parent task, so that
|
||||
* would typically be the case anyway. However, in the SMP
|
||||
* configuration, the child thread might have already ran on
|
||||
* another CPU if pre-emption were not disabled.
|
||||
*
|
||||
* It is a requirement that the parent environment be stable while
|
||||
* vfork runs; the child thread is still dependent on things in the
|
||||
* parent thread... like the pointers into parent thread's stack
|
||||
* which will still appear in the child's registers and environment.
|
||||
*
|
||||
* We assure that by waiting for the child thread to exit before
|
||||
* returning to the parent thread. NOTE that pre-emption will be
|
||||
* re-enabled while we are waiting, giving the child thread the
|
||||
* opportunity to run.
|
||||
*/
|
||||
|
||||
rc = 0;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FEATURES
|
||||
ret = waitpid(pid, &rc, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
serr("ERROR: waitpid failed: %d\n", get_errno());
|
||||
}
|
||||
#else
|
||||
waitpid(pid, &rc, 0);
|
||||
#endif
|
||||
|
||||
sched_unlock();
|
||||
return pid;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxtask_abort_vfork
|
||||
*
|
||||
* Description:
|
||||
* Recover from any errors after nxtask_setup_vfork() was called.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void nxtask_abort_vfork(FAR struct task_tcb_s *child, int errcode)
|
||||
{
|
||||
/* The TCB was added to the active task list by nxtask_setup_scheduler() */
|
||||
|
||||
dq_rem((FAR dq_entry_t *)child, (FAR dq_queue_t *)&g_inactivetasks);
|
||||
|
||||
/* Release the TCB */
|
||||
|
||||
nxsched_release_tcb((FAR struct tcb_s *)child,
|
||||
child->cmn.flags & TCB_FLAG_TTYPE_MASK);
|
||||
set_errno(errcode);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARCH_HAVE_VFORK && CONFIG_SCHED_WAITPID */
|
||||
Reference in New Issue
Block a user