123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- /****************************************************************************
- * sched/pthread/pthread_create.c
- *
- * Copyright (C) 2007-2009, 2011, 2013-2018 Gregory Nutt. All rights
- * reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <stdbool.h>
- #include <string.h>
- #include <pthread.h>
- #include <sched.h>
- #include <debug.h>
- #include <assert.h>
- #include <errno.h>
- #include <queue.h>
- #include <nuttx/sched.h>
- #include <nuttx/arch.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/pthread.h>
- #include "sched/sched.h"
- #include "group/group.h"
- #include "clock/clock.h"
- #include "pthread/pthread.h"
- /****************************************************************************
- * Public Data
- ****************************************************************************/
- /* Default pthread attributes (see include/nuttx/pthread.h). When configured
- * to build separate kernel- and user-address spaces, this global is
- * duplicated in each address spaced. This copy can only be shared within
- * the kernel address space.
- */
- const pthread_attr_t g_default_pthread_attr = PTHREAD_ATTR_INITIALIZER;
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- #if CONFIG_TASK_NAME_SIZE > 0
- /* This is the name for name-less pthreads */
- static const char g_pthreadname[] = "<pthread>";
- #endif
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: pthread_argsetup
- *
- * Description:
- * This functions sets up parameters in the Task Control Block (TCB) in
- * preparation for starting a new thread.
- *
- * pthread_argsetup() is called from task_init() and nxtask_start() to create
- * a new task (with arguments cloned via strdup) or pthread_create() which
- * has one argument passed by value (distinguished by the pthread boolean
- * argument).
- *
- * Input Parameters:
- * tcb - Address of the new task's TCB
- * arg - The argument to provide to the pthread on startup.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline void pthread_argsetup(FAR struct pthread_tcb_s *tcb, pthread_addr_t arg)
- {
- #if CONFIG_TASK_NAME_SIZE > 0
- /* Copy the pthread name into the TCB */
- strncpy(tcb->cmn.name, g_pthreadname, CONFIG_TASK_NAME_SIZE);
- tcb->cmn.name[CONFIG_TASK_NAME_SIZE] = '\0';
- #endif /* CONFIG_TASK_NAME_SIZE */
- /* For pthreads, args are strictly pass-by-value; that actual
- * type wrapped by pthread_addr_t is unknown.
- */
- tcb->arg = arg;
- }
- /****************************************************************************
- * Name: pthread_addjoininfo
- *
- * Description:
- * Add a join structure to the local data set.
- *
- * Input Parameters:
- * pjoin
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * The caller has provided protection from re-entrancy.
- *
- ****************************************************************************/
- static inline void pthread_addjoininfo(FAR struct task_group_s *group,
- FAR struct join_s *pjoin)
- {
- pjoin->next = NULL;
- if (!group->tg_jointail)
- {
- group->tg_joinhead = pjoin;
- }
- else
- {
- group->tg_jointail->next = pjoin;
- }
- group->tg_jointail = pjoin;
- }
- /****************************************************************************
- * Name: pthread_start
- *
- * Description:
- * This function is the low level entry point into the pthread
- *
- * Input Parameters:
- * None
- *
- ****************************************************************************/
- static void pthread_start(void)
- {
- FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)this_task();
- FAR struct task_group_s *group = ptcb->cmn.group;
- FAR struct join_s *pjoin = (FAR struct join_s *)ptcb->joininfo;
- pthread_addr_t exit_status;
- DEBUGASSERT(group && pjoin);
- /* Successfully spawned, add the pjoin to our data set. */
- (void)pthread_sem_take(&group->tg_joinsem, false);
- pthread_addjoininfo(group, pjoin);
- (void)pthread_sem_give(&group->tg_joinsem);
- /* Report to the spawner that we successfully started. */
- pjoin->started = true;
- (void)pthread_sem_give(&pjoin->data_sem);
- /* The priority of this thread may have been boosted to avoid priority
- * inversion problems. If that is the case, then drop to the correct
- * execution priority.
- */
- if (ptcb->cmn.sched_priority > ptcb->cmn.init_priority)
- {
- DEBUGVERIFY(nxsched_setpriority(&ptcb->cmn, ptcb->cmn.init_priority));
- }
- /* Pass control to the thread entry point. In the kernel build this has to
- * be handled differently if we are starting a user-space pthread; we have
- * to switch to user-mode before calling into the pthread.
- */
- #if defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)
- up_pthread_start(ptcb->cmn.entry.pthread, ptcb->arg);
- exit_status = NULL;
- #else
- exit_status = (*ptcb->cmn.entry.pthread)(ptcb->arg);
- #endif
- /* The thread has returned (should never happen in the kernel mode case) */
- pthread_exit(exit_status);
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: pthread_create
- *
- * Description:
- * This function creates and activates a new thread with a specified
- * attributes.
- *
- * Input Parameters:
- * thread
- * attr
- * start_routine
- * arg
- *
- * Returned Value:
- * OK (0) on success; a (non-negated) errno value on failure. The errno
- * variable is not set.
- *
- ****************************************************************************/
- int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
- pthread_startroutine_t start_routine, pthread_addr_t arg)
- {
- FAR struct pthread_tcb_s *ptcb;
- FAR struct join_s *pjoin;
- struct sched_param param;
- int policy;
- int errcode;
- pid_t pid;
- int ret;
- bool group_joined = false;
- /* If attributes were not supplied, use the default attributes */
- if (!attr)
- {
- attr = &g_default_pthread_attr;
- }
- /* Allocate a TCB for the new task. */
- ptcb = (FAR struct pthread_tcb_s *)kmm_zalloc(sizeof(struct pthread_tcb_s));
- if (!ptcb)
- {
- serr("ERROR: Failed to allocate TCB\n");
- return ENOMEM;
- }
- /* Bind the parent's group to the new TCB (we have not yet joined the
- * group).
- */
- ret = group_bind(ptcb);
- if (ret < 0)
- {
- errcode = ENOMEM;
- goto errout_with_tcb;
- }
- #ifdef CONFIG_ARCH_ADDRENV
- /* Share the address environment of the parent task group. */
- ret = up_addrenv_attach(ptcb->cmn.group, this_task());
- if (ret < 0)
- {
- errcode = -ret;
- goto errout_with_tcb;
- }
- #endif
- /* Allocate a detachable structure to support pthread_join logic */
- pjoin = (FAR struct join_s *)kmm_zalloc(sizeof(struct join_s));
- if (!pjoin)
- {
- serr("ERROR: Failed to allocate join\n");
- errcode = ENOMEM;
- goto errout_with_tcb;
- }
- if (attr->stackaddr)
- {
- /* Use pre-allocated stack */
- ret = up_use_stack((FAR struct tcb_s *)ptcb, attr->stackaddr,
- attr->stacksize);
- }
- else
- {
- /* Allocate the stack for the TCB */
- ret = up_create_stack((FAR struct tcb_s *)ptcb, attr->stacksize,
- TCB_FLAG_TTYPE_PTHREAD);
- }
- if (ret != OK)
- {
- errcode = ENOMEM;
- goto errout_with_join;
- }
- /* Should we use the priority and scheduler specified in the pthread
- * attributes? Or should we use the current thread's priority and
- * scheduler?
- */
- if (attr->inheritsched == PTHREAD_INHERIT_SCHED)
- {
- /* Get the priority (and any other scheduling parameters) for this
- * thread.
- */
- ret = nxsched_getparam(0, ¶m);
- if (ret < 0)
- {
- errcode = -ret;
- goto errout_with_join;
- }
- /* Get the scheduler policy for this thread */
- policy = nxsched_getscheduler(0);
- if (policy < 0)
- {
- errcode = -policy;
- goto errout_with_join;
- }
- }
- else
- {
- /* Use the scheduler policy and policy the attributes */
- policy = attr->policy;
- param.sched_priority = attr->priority;
- #ifdef CONFIG_SCHED_SPORADIC
- param.sched_ss_low_priority = attr->low_priority;
- param.sched_ss_max_repl = 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
- }
- #ifdef CONFIG_SCHED_SPORADIC
- if (policy == SCHED_SPORADIC)
- {
- FAR struct sporadic_s *sporadic;
- sclock_t repl_ticks;
- sclock_t budget_ticks;
- /* Convert timespec values to system clock ticks */
- (void)clock_time2ticks(¶m.sched_ss_repl_period, &repl_ticks);
- (void)clock_time2ticks(¶m.sched_ss_init_budget, &budget_ticks);
- /* The replenishment period must be greater than or equal to the
- * budget period.
- */
- if (repl_ticks < budget_ticks)
- {
- errcode = EINVAL;
- goto errout_with_join;
- }
- /* Initialize the sporadic policy */
- ret = sched_sporadic_initialize(&ptcb->cmn);
- if (ret >= 0)
- {
- sporadic = ptcb->cmn.sporadic;
- DEBUGASSERT(sporadic != NULL);
- /* Save the sporadic scheduling parameters */
- sporadic->hi_priority = param.sched_priority;
- sporadic->low_priority = param.sched_ss_low_priority;
- sporadic->max_repl = param.sched_ss_max_repl;
- sporadic->repl_period = repl_ticks;
- sporadic->budget = budget_ticks;
- /* And start the first replenishment interval */
- ret = sched_sporadic_start(&ptcb->cmn);
- }
- /* Handle any failures */
- if (ret < 0)
- {
- errcode = -ret;
- goto errout_with_join;
- }
- }
- #endif
- /* Initialize the task control block */
- ret = pthread_schedsetup(ptcb, param.sched_priority, pthread_start,
- start_routine);
- if (ret != OK)
- {
- errcode = EBUSY;
- goto errout_with_join;
- }
- #ifdef CONFIG_SMP
- /* pthread_schedsetup() will set the affinity mask by inheriting the
- * setting from the parent task. We need to override this setting
- * with the value from the pthread attributes unless that value is
- * zero: Zero is the default value and simply means to inherit the
- * parent thread's affinity mask.
- */
- if (attr->affinity != 0)
- {
- ptcb->cmn.affinity = attr->affinity;
- }
- #endif
- /* Configure the TCB for a pthread receiving on parameter
- * passed by value
- */
- pthread_argsetup(ptcb, arg);
- /* Join the parent's task group */
- ret = group_join(ptcb);
- if (ret < 0)
- {
- errcode = ENOMEM;
- goto errout_with_join;
- }
- group_joined = true;
- /* Attach the join info to the TCB. */
- ptcb->joininfo = (FAR void *)pjoin;
- /* Set the appropriate scheduling policy in the TCB */
- ptcb->cmn.flags &= ~TCB_FLAG_POLICY_MASK;
- switch (policy)
- {
- default:
- DEBUGPANIC();
- case SCHED_FIFO:
- ptcb->cmn.flags |= TCB_FLAG_SCHED_FIFO;
- break;
- #if CONFIG_RR_INTERVAL > 0
- case SCHED_RR:
- ptcb->cmn.flags |= TCB_FLAG_SCHED_RR;
- ptcb->cmn.timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
- break;
- #endif
- #ifdef CONFIG_SCHED_SPORADIC
- case SCHED_SPORADIC:
- ptcb->cmn.flags |= TCB_FLAG_SCHED_SPORADIC;
- break;
- #endif
- #if 0 /* Not supported */
- case SCHED_OTHER:
- ptcb->cmn.flags |= TCB_FLAG_SCHED_OTHER;
- break;
- #endif
- }
- #ifdef CONFIG_CANCELLATION_POINTS
- /* Set the deferred cancellation type */
- ptcb->cmn.flags |= TCB_FLAG_CANCEL_DEFERRED;
- #endif
- /* Get the assigned pid before we start the task (who knows what
- * could happen to ptcb after this!). Copy this ID into the join structure
- * as well.
- */
- pid = (int)ptcb->cmn.pid;
- pjoin->thread = (pthread_t)pid;
- /* Initialize the semaphores in the join structure to zero. */
- ret = nxsem_init(&pjoin->data_sem, 0, 0);
- if (ret == OK)
- {
- ret = nxsem_init(&pjoin->exit_sem, 0, 0);
- }
- if (ret < 0)
- {
- ret = -ret;
- }
- /* Thse semaphores are used for signaling and, hence, should not have
- * priority inheritance enabled.
- */
- if (ret == OK)
- {
- ret = nxsem_setprotocol(&pjoin->data_sem, SEM_PRIO_NONE);
- if (ret == OK)
- {
- ret = nxsem_setprotocol(&pjoin->exit_sem, SEM_PRIO_NONE);
- }
- if (ret < 0)
- {
- ret = -ret;
- }
- }
- /* If the priority of the new pthread is lower than the priority of the
- * parent thread, then starting the pthread could result in both the
- * parent and the pthread to be blocked. This is a recipe for priority
- * inversion issues.
- *
- * We avoid this here by boosting the priority of the (inactive) pthread
- * so it has the same priority as the parent thread.
- */
- if (ret == OK)
- {
- FAR struct tcb_s *parent = this_task();
- DEBUGASSERT(parent != NULL);
- if (ptcb->cmn.sched_priority < parent->sched_priority)
- {
- ret = nxsched_setpriority(&ptcb->cmn, parent->sched_priority);
- if (ret < 0)
- {
- ret = -ret;
- }
- }
- }
- /* Then activate the task */
- sched_lock();
- if (ret == OK)
- {
- ret = task_activate((FAR struct tcb_s *)ptcb);
- if (ret < 0)
- {
- ret = get_errno();
- }
- }
- if (ret == OK)
- {
- /* Wait for the task to actually get running and to register
- * its join structure.
- */
- (void)pthread_sem_take(&pjoin->data_sem, false);
- /* Return the thread information to the caller */
- if (thread)
- {
- *thread = (pthread_t)pid;
- }
- if (!pjoin->started)
- {
- ret = EINVAL;
- }
- sched_unlock();
- (void)nxsem_destroy(&pjoin->data_sem);
- }
- else
- {
- sched_unlock();
- dq_rem((FAR dq_entry_t *)ptcb, (FAR dq_queue_t *)&g_inactivetasks);
- (void)nxsem_destroy(&pjoin->data_sem);
- (void)nxsem_destroy(&pjoin->exit_sem);
- errcode = EIO;
- goto errout_with_join;
- }
- return ret;
- errout_with_join:
- sched_kfree(pjoin);
- ptcb->joininfo = NULL;
- errout_with_tcb:
- /* Clear group binding */
- if (ptcb && !group_joined)
- {
- ptcb->cmn.group = NULL;
- }
- sched_releasetcb((FAR struct tcb_s *)ptcb, TCB_FLAG_TTYPE_PTHREAD);
- return errcode;
- }
|