Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:22

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup POSIXAPI
0007  *
0008  * @brief Function Starts a New Thread in The Calling Process
0009  */
0010 
0011 /*
0012  *  16.1.2 Thread Creation, P1003.1c/Draft 10, p. 144
0013  */
0014 
0015 /*
0016  *  COPYRIGHT (c) 1989-2014.
0017  *  On-Line Applications Research Corporation (OAR).
0018  *
0019  * Redistribution and use in source and binary forms, with or without
0020  * modification, are permitted provided that the following conditions
0021  * are met:
0022  * 1. Redistributions of source code must retain the above copyright
0023  *    notice, this list of conditions and the following disclaimer.
0024  * 2. Redistributions in binary form must reproduce the above copyright
0025  *    notice, this list of conditions and the following disclaimer in the
0026  *    documentation and/or other materials provided with the distribution.
0027  *
0028  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0029  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0030  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0031  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0032  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0033  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0034  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0035  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0036  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0037  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0038  * POSSIBILITY OF SUCH DAMAGE.
0039  */
0040 
0041 #ifdef HAVE_CONFIG_H
0042 #include "config.h"
0043 #endif
0044 
0045 #include <pthread.h>
0046 #include <errno.h>
0047 
0048 #include <rtems/posix/posixapi.h>
0049 #include <rtems/posix/priorityimpl.h>
0050 #if defined(RTEMS_POSIX_API)
0051 #include <rtems/posix/psignalimpl.h>
0052 #endif
0053 #include <rtems/posix/pthreadimpl.h>
0054 #include <rtems/posix/pthreadattrimpl.h>
0055 #include <rtems/score/assert.h>
0056 #include <rtems/score/threadcpubudget.h>
0057 #include <rtems/score/threadimpl.h>
0058 #include <rtems/score/apimutex.h>
0059 #include <rtems/score/stackimpl.h>
0060 #include <rtems/score/statesimpl.h>
0061 #include <rtems/score/schedulerimpl.h>
0062 #include <rtems/score/userextimpl.h>
0063 #include <rtems/sysinit.h>
0064 
0065 #include <string.h>
0066 
0067 static inline size_t _POSIX_Threads_Ensure_minimum_stack (
0068   size_t size
0069 )
0070 {
0071   if ( size >= PTHREAD_MINIMUM_STACK_SIZE )
0072     return size;
0073   return PTHREAD_MINIMUM_STACK_SIZE;
0074 }
0075 
0076 
0077 int pthread_create(
0078   pthread_t              *thread,
0079   const pthread_attr_t   *attr,
0080   void                 *(*start_routine)( void * ),
0081   void                   *arg
0082 )
0083 {
0084   Thread_Entry_information entry = {
0085     .adaptor = _Thread_Entry_adaptor_pointer,
0086     .Kinds = {
0087       .Pointer = {
0088         .entry = start_routine,
0089         .argument = arg
0090       }
0091     }
0092   };
0093   const pthread_attr_t               *the_attr;
0094   int                                 normal_prio;
0095   bool                                valid;
0096   Thread_Configuration                config;
0097   Status_Control                      status;
0098   Thread_Control                     *the_thread;
0099   Thread_Control                     *executing;
0100   int                                 schedpolicy = SCHED_RR;
0101   struct sched_param                  schedparam;
0102   int                                 error;
0103   ISR_lock_Context                    lock_context;
0104 #if defined(RTEMS_POSIX_API)
0105   int                                 low_prio;
0106   Priority_Control                    core_low_prio;
0107   POSIX_API_Control                  *api;
0108 #endif
0109 
0110   if ( !start_routine )
0111     return EFAULT;
0112 
0113   the_attr = (attr) ? attr : &_POSIX_Threads_Default_attributes;
0114 
0115   if ( !the_attr->is_initialized )
0116     return EINVAL;
0117 
0118   memset( &config, 0, sizeof( config ) );
0119 
0120   /*
0121    *  Currently all POSIX threads are floating point if the hardware
0122    *  supports it.
0123    */
0124   config.is_fp = true;
0125 
0126   config.is_preemptible = true;
0127 
0128   /*
0129    *  Core Thread Initialize ensures we get the minimum amount of
0130    *  stack space if it is allowed to allocate it itself.
0131    *
0132    *  NOTE: If the user provides the stack we will let it drop below
0133    *        twice the minimum.
0134    */
0135   if ( the_attr->stackaddr != NULL ) {
0136     if ( !_Stack_Is_enough( the_attr->stacksize, config.is_fp ) ) {
0137       return EINVAL;
0138     }
0139 
0140     config.stack_area = the_attr->stackaddr;
0141     config.stack_size = the_attr->stacksize;
0142   } else {
0143     config.stack_size = _POSIX_Threads_Ensure_minimum_stack(
0144       the_attr->stacksize
0145     );
0146     config.stack_size = _Stack_Extend_size( config.stack_size, config.is_fp );
0147   }
0148 
0149   #if 0
0150     int  cputime_clock_allowed;  /* see time.h */
0151     rtems_set_errno_and_return_minus_one( ENOSYS );
0152   #endif
0153 
0154   executing = _Thread_Get_executing();
0155 
0156   /*
0157    *  P1003.1c/Draft 10, p. 121.
0158    *
0159    *  If inheritsched is set to PTHREAD_INHERIT_SCHED, then this thread
0160    *  inherits scheduling attributes from the creating thread.   If it is
0161    *  PTHREAD_EXPLICIT_SCHED, then scheduling parameters come from the
0162    *  attributes structure.
0163    */
0164   switch ( the_attr->inheritsched ) {
0165     case PTHREAD_INHERIT_SCHED:
0166       error = pthread_getschedparam(
0167         pthread_self(),
0168         &schedpolicy,
0169         &schedparam
0170       );
0171       _Assert( error == 0 );
0172       (void) error; /* error only used when debug enabled */
0173       break;
0174 
0175     case PTHREAD_EXPLICIT_SCHED:
0176       schedpolicy = the_attr->schedpolicy;
0177       schedparam  = the_attr->schedparam;
0178       break;
0179 
0180     default:
0181       return EINVAL;
0182   }
0183 
0184   /*
0185    *  Check the contentionscope since rtems only supports PROCESS wide
0186    *  contention (i.e. no system wide contention).
0187    */
0188   if ( the_attr->contentionscope != PTHREAD_SCOPE_PROCESS )
0189     return ENOTSUP;
0190 
0191   error = _POSIX_Thread_Translate_sched_param(
0192     schedpolicy,
0193     &schedparam,
0194     &config
0195   );
0196   if ( error != 0 ) {
0197     return error;
0198   }
0199 
0200   normal_prio = schedparam.sched_priority;
0201 
0202   config.scheduler = _Thread_Scheduler_get_home( executing );
0203 
0204   config.priority = _POSIX_Priority_To_core(
0205     config.scheduler,
0206     normal_prio,
0207     &valid
0208   );
0209   if ( !valid ) {
0210     return EINVAL;
0211   }
0212 
0213 #if defined(RTEMS_POSIX_API)
0214   if ( schedpolicy == SCHED_SPORADIC ) {
0215     low_prio = schedparam.sched_ss_low_priority;
0216   } else {
0217     low_prio = normal_prio;
0218   }
0219 
0220   core_low_prio = _POSIX_Priority_To_core( config.scheduler, low_prio, &valid );
0221   if ( !valid ) {
0222     return EINVAL;
0223   }
0224 #endif
0225 
0226   if ( the_attr->affinityset == NULL ) {
0227     return EINVAL;
0228   }
0229 
0230   /*
0231    *  Allocate the thread control block.
0232    *
0233    *  NOTE:  Global threads are not currently supported.
0234    */
0235   the_thread = _POSIX_Threads_Allocate();
0236   if ( !the_thread ) {
0237     _Objects_Allocator_unlock();
0238     return EAGAIN;
0239   }
0240 
0241   if ( config.stack_area == NULL ) {
0242     config.stack_free = _Stack_Free;
0243     config.stack_area = _Stack_Allocate( config.stack_size );
0244   } else {
0245     config.stack_free = _Objects_Free_nothing;
0246   }
0247 
0248   if ( config.stack_area == NULL ) {
0249     _Objects_Free( &_POSIX_Threads_Information.Objects, &the_thread->Object );
0250     _Objects_Allocator_unlock();
0251     return EAGAIN;
0252   }
0253 
0254   /*
0255    *  Initialize the core thread for this task.
0256    */
0257   status = _Thread_Initialize(
0258     &_POSIX_Threads_Information,
0259     the_thread,
0260     &config
0261   );
0262   if ( status != STATUS_SUCCESSFUL ) {
0263     _Objects_Allocator_unlock();
0264     return _POSIX_Get_error( status );
0265   }
0266 
0267   if ( the_attr->detachstate == PTHREAD_CREATE_DETACHED ) {
0268     the_thread->Life.state |= THREAD_LIFE_DETACHED;
0269   }
0270 
0271   the_thread->Life.state |= THREAD_LIFE_CHANGE_DEFERRED;
0272 
0273   _ISR_lock_ISR_disable( &lock_context );
0274    status = _Scheduler_Set_affinity(
0275      the_thread,
0276      the_attr->affinitysetsize,
0277      the_attr->affinityset
0278    );
0279   _ISR_lock_ISR_enable( &lock_context );
0280    if ( status != STATUS_SUCCESSFUL ) {
0281       _Thread_Free( &_POSIX_Threads_Information, the_thread );
0282      _RTEMS_Unlock_allocator();
0283      return EINVAL;
0284    }
0285 
0286   the_thread->was_created_with_inherited_scheduler =
0287     ( the_attr->inheritsched == PTHREAD_INHERIT_SCHED );
0288 
0289 #if defined(RTEMS_POSIX_API)
0290   /*
0291    *  finish initializing the per API structure
0292    */
0293   api = the_thread->API_Extensions[ THREAD_API_POSIX ];
0294 
0295   _Priority_Node_set_priority( &api->Sporadic.Low_priority, core_low_prio );
0296   api->Sporadic.sched_ss_repl_period =
0297     the_attr->schedparam.sched_ss_repl_period;
0298   api->Sporadic.sched_ss_init_budget =
0299     the_attr->schedparam.sched_ss_init_budget;
0300   api->Sporadic.sched_ss_max_repl =
0301     the_attr->schedparam.sched_ss_max_repl;
0302 
0303   if ( schedpolicy == SCHED_SPORADIC ) {
0304 #if defined(RTEMS_SCORE_THREAD_HAS_SCHEDULER_CHANGE_INHIBITORS)
0305     the_thread->is_scheduler_change_inhibited = true;
0306 #endif
0307     _POSIX_Threads_Sporadic_timer( &api->Sporadic.Timer );
0308   }
0309 #endif
0310 
0311   /*
0312    *  POSIX threads are allocated and started in one operation.
0313    */
0314   _ISR_lock_ISR_disable( &lock_context );
0315   status = _Thread_Start( the_thread, &entry, &lock_context );
0316 
0317   #if defined(RTEMS_DEBUG)
0318     /*
0319      *  _Thread_Start only fails if the thread was in the incorrect state
0320      *
0321      *  NOTE: This can only happen if someone slips in and touches the
0322      *        thread while we are creating it.
0323      */
0324     if ( status != STATUS_SUCCESSFUL ) {
0325       _Thread_Free( &_POSIX_Threads_Information, the_thread );
0326       _Objects_Allocator_unlock();
0327       return EINVAL;
0328     }
0329   #endif
0330 
0331   /*
0332    *  Return the id and indicate we successfully created the thread
0333    */
0334   *thread = the_thread->Object.id;
0335 
0336   _Objects_Allocator_unlock();
0337   return 0;
0338 }
0339 
0340 #if defined(RTEMS_POSIX_API)
0341 void _POSIX_Threads_Sporadic_timer( Watchdog_Control *watchdog )
0342 {
0343   POSIX_API_Control    *api;
0344   Thread_Control       *the_thread;
0345   Thread_queue_Context  queue_context;
0346 
0347   api = RTEMS_CONTAINER_OF( watchdog, POSIX_API_Control, Sporadic.Timer );
0348   the_thread = api->Sporadic.thread;
0349 
0350   _Thread_queue_Context_initialize( &queue_context );
0351   _Thread_queue_Context_clear_priority_updates( &queue_context );
0352   _Thread_Wait_acquire( the_thread, &queue_context );
0353 
0354   if ( !_Priority_Node_is_active( &the_thread->Real_priority ) ) {
0355     _Thread_Priority_add(
0356       the_thread,
0357       &the_thread->Real_priority,
0358       &queue_context
0359     );
0360     _Thread_Priority_remove(
0361       the_thread,
0362       &api->Sporadic.Low_priority,
0363       &queue_context
0364     );
0365   }
0366 
0367   _Watchdog_Per_CPU_remove_ticks( &api->Sporadic.Timer );
0368   _POSIX_Threads_Sporadic_timer_insert( the_thread, api );
0369 
0370   _Thread_Wait_release( the_thread, &queue_context );
0371   _Thread_Priority_update( &queue_context );
0372 }
0373 
0374 static void _POSIX_Threads_Sporadic_budget_callout(
0375   Thread_Control *the_thread
0376 )
0377 {
0378   POSIX_API_Control    *api;
0379   Thread_queue_Context  queue_context;
0380 
0381   api = the_thread->API_Extensions[ THREAD_API_POSIX ];
0382 
0383   _Thread_queue_Context_initialize( &queue_context );
0384   _Thread_queue_Context_clear_priority_updates( &queue_context );
0385   _Thread_Wait_acquire( the_thread, &queue_context );
0386 
0387   /*
0388    *  This will prevent the thread from consuming its entire "budget"
0389    *  while at low priority.
0390    */
0391   the_thread->CPU_budget.available = UINT32_MAX;
0392 
0393   if ( _Priority_Node_is_active( &the_thread->Real_priority ) ) {
0394     _Thread_Priority_add(
0395       the_thread,
0396       &api->Sporadic.Low_priority,
0397       &queue_context
0398     );
0399     _Thread_Priority_remove(
0400       the_thread,
0401       &the_thread->Real_priority,
0402       &queue_context
0403     );
0404     _Priority_Node_set_inactive( &the_thread->Real_priority );
0405   }
0406 
0407   _Thread_Wait_release( the_thread, &queue_context );
0408   _Thread_Priority_update( &queue_context );
0409 }
0410 
0411 static void _POSIX_Threads_Sporadic_budget_at_tick( Thread_Control *the_thread )
0412 {
0413   uint32_t budget_available;
0414 
0415   if ( !the_thread->is_preemptible ) {
0416     return;
0417   }
0418 
0419   if ( !_States_Is_ready( the_thread->current_state ) ) {
0420     return;
0421   }
0422 
0423   budget_available = the_thread->CPU_budget.available;
0424 
0425   if ( budget_available == 1 ) {
0426     the_thread->CPU_budget.available = 0;
0427     _POSIX_Threads_Sporadic_budget_callout ( the_thread );
0428   } else {
0429     the_thread->CPU_budget.available = budget_available - 1;
0430   }
0431 }
0432 
0433 const Thread_CPU_budget_operations _POSIX_Threads_Sporadic_budget = {
0434   .at_tick = _POSIX_Threads_Sporadic_budget_at_tick,
0435   .at_context_switch = _Thread_CPU_budget_do_nothing,
0436   .initialize = _Thread_CPU_budget_do_nothing
0437 };
0438 
0439 static bool _POSIX_Threads_Create_extension(
0440   Thread_Control *executing,
0441   Thread_Control *created
0442 )
0443 {
0444   POSIX_API_Control *api;
0445 
0446   api = created->API_Extensions[ THREAD_API_POSIX ];
0447 
0448   api->Sporadic.thread = created;
0449   _Watchdog_Preinitialize( &api->Sporadic.Timer, _Per_CPU_Get_by_index( 0 ) );
0450   _Watchdog_Initialize( &api->Sporadic.Timer, _POSIX_Threads_Sporadic_timer );
0451   _Priority_Node_set_inactive( &api->Sporadic.Low_priority );
0452 
0453 #if defined(RTEMS_POSIX_API)
0454   /*
0455    * There are some subtle rules which need to be followed for
0456    * the value of the created thread's signal mask. Because signals
0457    * are part of C99 and enhanced by POSIX, both Classic API tasks
0458    * and POSIX threads have to have them enabled.
0459    *
0460    * + Internal system threads should have no signals enabled. They
0461    *   have no business executing user signal handlers -- especially IDLE.
0462    * + The initial signal mask for other threads needs to follow the
0463    *   implication of a pure C99 environment which only has the methods
0464    *   raise() and signal(). This implies that all signals are unmasked
0465    *   until the thread explicitly uses a POSIX methods to block some.
0466    *   This applies to both Classic tasks and POSIX threads created
0467    *   as initalization tasks/threads (e.g. before the system is up).
0468    * + After the initial threads are created, the signal mask should
0469    *   be inherited from the creator.
0470    *
0471    * NOTE: The default signal mask does not matter for any application
0472    *       that does not use POSIX signals.
0473    */
0474   if ( _Objects_Get_API(created->Object.id) == OBJECTS_INTERNAL_API ) {
0475       /*
0476        * Ensure internal (especially IDLE) is handled first.
0477        *
0478        * Block signals for all internal threads -- especially IDLE.
0479        */
0480       api->signals_unblocked = 0;
0481   } else if ( _Objects_Get_API(executing->Object.id) == OBJECTS_INTERNAL_API ) {
0482       /*
0483        * Threads being created while an internal thread is executing
0484        * should only happen for the initialization threads/tasks.
0485        *
0486        * Default state (signals unblocked) for all Initialization tasks
0487        * and POSIX threads. We should not inherit from IDLE which is
0488        * what appears to be executing during initialization.
0489        */
0490       api->signals_unblocked = SIGNAL_ALL_MASK;
0491   } else {
0492     const POSIX_API_Control            *executing_api;
0493     /*
0494      * RTEMS is running so follow the POSIX rules to inherit the signal mask.
0495      */ 
0496     executing_api = executing->API_Extensions[ THREAD_API_POSIX ];
0497     api->signals_unblocked = executing_api->signals_unblocked;
0498   }
0499 #endif
0500   return true;
0501 }
0502 
0503 static void _POSIX_Threads_Terminate_extension( Thread_Control *executing )
0504 {
0505   POSIX_API_Control *api;
0506   ISR_lock_Context   lock_context;
0507 
0508   api = executing->API_Extensions[ THREAD_API_POSIX ];
0509 
0510   _Thread_State_acquire( executing, &lock_context );
0511   _Watchdog_Per_CPU_remove_ticks( &api->Sporadic.Timer );
0512   _Thread_State_release( executing, &lock_context );
0513 }
0514 #endif
0515 
0516 static void _POSIX_Threads_Exitted_extension(
0517   Thread_Control *executing
0518 )
0519 {
0520   /*
0521    *  If the executing thread was not created with the POSIX API, then this
0522    *  API do not get to define its exit behavior.
0523    */
0524   if ( _Objects_Get_API( executing->Object.id ) == OBJECTS_POSIX_API )
0525     pthread_exit( executing->Wait.return_argument );
0526 }
0527 
0528 static User_extensions_Control _POSIX_Threads_User_extensions = {
0529   .Callouts = {
0530 #if defined(RTEMS_POSIX_API)
0531     .thread_create    = _POSIX_Threads_Create_extension,
0532     .thread_terminate = _POSIX_Threads_Terminate_extension,
0533 #endif
0534     .thread_exitted   = _POSIX_Threads_Exitted_extension
0535   }
0536 };
0537 
0538 static void _POSIX_Threads_Manager_initialization( void )
0539 {
0540   _Thread_Initialize_information( &_POSIX_Threads_Information );
0541   _User_extensions_Add_API_set( &_POSIX_Threads_User_extensions );
0542 }
0543 
0544 RTEMS_SYSINIT_ITEM(
0545   _POSIX_Threads_Manager_initialization,
0546   RTEMS_SYSINIT_POSIX_THREADS,
0547   RTEMS_SYSINIT_ORDER_MIDDLE
0548 );