modify nuttx bug of lack of files

This commit is contained in:
TangYiwen123
2021-06-11 11:25:07 +08:00
parent 4a7f51a5e9
commit 6d0f2ca39a
6752 changed files with 1528133 additions and 0 deletions
@@ -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
+116
View File
@@ -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);
}
+147
View File
@@ -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 */
+61
View File
@@ -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, &param);
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, &param);
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, &param);
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, &param);
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, &param);
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, &param);
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, &param);
}
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 */