diff options
Diffstat (limited to 'c/src/exec/posix/src/pthread.c')
-rw-r--r-- | c/src/exec/posix/src/pthread.c | 1309 |
1 files changed, 1309 insertions, 0 deletions
diff --git a/c/src/exec/posix/src/pthread.c b/c/src/exec/posix/src/pthread.c new file mode 100644 index 0000000000..95213389cb --- /dev/null +++ b/c/src/exec/posix/src/pthread.c @@ -0,0 +1,1309 @@ +/* + * $Id$ + */ + +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <limits.h> + +#include <rtems/system.h> +#include <rtems/score/apiext.h> +#include <rtems/score/stack.h> +#include <rtems/score/thread.h> +#include <rtems/score/userext.h> +#include <rtems/score/wkspace.h> +#include <rtems/posix/pthread.h> +#include <rtems/posix/priority.h> +#include <rtems/posix/psignal.h> +#include <rtems/posix/config.h> +#include <rtems/posix/key.h> +#include <rtems/posix/time.h> + +/*PAGE + * + * The default pthreads attributes structure. + * + * NOTE: Be careful .. if the default attribute set changes, + * _POSIX_Threads_Initialize_user_threads will need to be examined. + * + */ + +const pthread_attr_t _POSIX_Threads_Default_attributes = { + TRUE, /* is_initialized */ + NULL, /* stackaddr */ + PTHREAD_MINIMUM_STACK_SIZE, /* stacksize */ + PTHREAD_SCOPE_PROCESS, /* contentionscope */ + PTHREAD_INHERIT_SCHED, /* inheritsched */ + SCHED_FIFO, /* schedpolicy */ + { /* schedparam */ + 2, /* sched_priority */ + 0, /* ss_low_priority */ + { 0L, 0 }, /* ss_replenish_period */ + { 0L, 0 } /* ss_initial_budget */ + }, + PTHREAD_CREATE_JOINABLE, /* detachstate */ + 1 /* cputime_clock_allowed */ +}; + +/*PAGE + * + * _POSIX_Threads_Sporadic_budget_TSR + */ + +void _POSIX_Threads_Sporadic_budget_TSR( + Objects_Id id, + void *argument +) +{ + unsigned32 ticks; + unsigned32 new_priority; + Thread_Control *the_thread; + POSIX_API_Control *api; + + the_thread = argument; + + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + ticks = _POSIX_Timespec_to_interval( &api->schedparam.ss_initial_budget ); + + if ( !ticks ) + ticks = 1; + + the_thread->cpu_time_budget = ticks; + + new_priority = _POSIX_Priority_To_core( api->ss_high_priority ); + the_thread->real_priority = new_priority; + + if ( the_thread->resource_count == 0 || + the_thread->current_priority > new_priority ) + _Thread_Change_priority( the_thread, new_priority, TRUE ); + + ticks = _POSIX_Timespec_to_interval( &api->schedparam.ss_replenish_period ); + + if ( !ticks ) + ticks = 1; + + _Watchdog_Insert_ticks( &api->Sporadic_timer, ticks ); +} + +/*PAGE + * + * _POSIX_Threads_Sporadic_budget_callout + */ + +void _POSIX_Threads_Sporadic_budget_callout( + Thread_Control *the_thread +) +{ + POSIX_API_Control *api; + unsigned32 new_priority; + + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + /* + * This will prevent the thread from consuming its entire "budget" + * while at low priority. + */ + + + the_thread->cpu_time_budget = 0xFFFFFFFF; /* XXX should be based on MAX_U32 */ + + new_priority = _POSIX_Priority_To_core( api->schedparam.ss_low_priority ); + the_thread->real_priority = new_priority; + + if ( the_thread->resource_count == 0 || + the_thread->current_priority > new_priority ) + _Thread_Change_priority( the_thread, new_priority, TRUE ); +} + +/*PAGE + * + * _POSIX_Threads_Create_extension + * + * XXX + */ + +boolean _POSIX_Threads_Create_extension( + Thread_Control *executing, + Thread_Control *created +) +{ + POSIX_API_Control *api; + POSIX_API_Control *executing_api; + + api = _Workspace_Allocate( sizeof( POSIX_API_Control ) ); + + if ( !api ) + return FALSE; + + created->API_Extensions[ THREAD_API_POSIX ] = api; + + /* XXX check all fields are touched */ + api->Attributes = _POSIX_Threads_Default_attributes; + api->detachstate = _POSIX_Threads_Default_attributes.detachstate; + api->schedpolicy = _POSIX_Threads_Default_attributes.schedpolicy; + api->schedparam = _POSIX_Threads_Default_attributes.schedparam; + api->schedparam.sched_priority = + _POSIX_Priority_From_core( created->current_priority ); + + /* + * If the thread is not a posix thread, then all posix signals are blocked + * by default. + */ + + /* XXX use signal constants */ + api->signals_pending = 0; + if ( _Objects_Get_class( created->Object.id ) == OBJECTS_POSIX_THREADS ) { + executing_api = _Thread_Executing->API_Extensions[ THREAD_API_POSIX ]; + api->signals_blocked = api->signals_blocked; + } else + api->signals_blocked = 0xffffffff; + +/* XXX set signal parameters -- block all signals for non-posix threads */ + + _Thread_queue_Initialize( + &api->Join_List, + OBJECTS_NO_CLASS, /* only used for proxy operations */ + THREAD_QUEUE_DISCIPLINE_FIFO, + STATES_WAITING_FOR_JOIN_AT_EXIT, + NULL, /* no extract proxy handler */ + 0 + ); + + _Watchdog_Initialize( + &api->Sporadic_timer, + _POSIX_Threads_Sporadic_budget_TSR, + created->Object.id, + created + ); + + return TRUE; +} + +/*PAGE + * + * _POSIX_Threads_Delete_extension + */ + +User_extensions_routine _POSIX_Threads_Delete_extension( + Thread_Control *executing, + Thread_Control *deleted +) +{ + Thread_Control *the_thread; + POSIX_API_Control *api; + void **value_ptr; + + api = deleted->API_Extensions[ THREAD_API_POSIX ]; + + /* XXX run cancellation handlers */ + + _POSIX_Keys_Run_destructors( deleted ); + + /* + * Wakeup all the tasks which joined with this one + */ + + value_ptr = (void **) deleted->Wait.return_argument; + + while ( (the_thread = _Thread_queue_Dequeue( &api->Join_List )) ) + *(void **)the_thread->Wait.return_argument = value_ptr; + + if ( api->schedpolicy == SCHED_SPORADIC ) + (void) _Watchdog_Remove( &api->Sporadic_timer ); + + deleted->API_Extensions[ THREAD_API_POSIX ] = NULL; + + (void) _Workspace_Free( api ); +} + +/*PAGE + * + * _POSIX_Threads_Initialize_user_threads + * + * This routine creates and starts all configured user + * initialzation threads. + * + * Input parameters: NONE + * + * Output parameters: NONE + */ + +void _POSIX_Threads_Initialize_user_threads( void ) +{ + int status; + unsigned32 index; + unsigned32 maximum; + posix_initialization_threads_table *user_threads; + pthread_t thread_id; + pthread_attr_t attr; + + user_threads = _POSIX_Threads_User_initialization_threads; + maximum = _POSIX_Threads_Number_of_initialization_threads; + + if ( !user_threads || maximum == 0 ) + return; + + /* + * Be careful .. if the default attribute set changes, this may need to. + * + * Setting the attributes explicitly is critical, since we don't want + * to inherit the idle tasks attributes. + */ + + for ( index=0 ; index < maximum ; index++ ) { + status = pthread_attr_init( &attr ); + assert( !status ); + + status = pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); + assert( !status ); + + status = pthread_attr_setstacksize( &attr, PTHREAD_MINIMUM_STACK_SIZE * 2 ); + assert( !status ); + + status = pthread_create( + &thread_id, + &attr, + user_threads[ index ].entry, + NULL + ); + assert( !status ); + } +} + +/*PAGE + * + * API Extension control structures + */ + +API_extensions_Control _POSIX_Threads_API_extensions = { + { NULL, NULL }, + NULL, /* predriver */ + _POSIX_Threads_Initialize_user_threads, /* postdriver */ + _POSIX_signals_Post_switch_extension, /* post switch */ +}; + +User_extensions_Control _POSIX_Threads_User_extensions = { + { NULL, NULL }, + { _POSIX_Threads_Create_extension, /* create */ + NULL, /* start */ + NULL, /* restart */ + _POSIX_Threads_Delete_extension, /* delete */ + NULL, /* switch */ + NULL, /* begin */ + NULL, /* exitted */ + NULL /* fatal */ + } +}; + +/*PAGE + * + * _POSIX_Threads_Manager_initialization + * + * This routine initializes all threads manager related data structures. + * + * Input parameters: + * maximum_pthreads - maximum configured pthreads + * + * Output parameters: NONE + */ + +void _POSIX_Threads_Manager_initialization( + unsigned32 maximum_pthreads, + unsigned32 number_of_initialization_threads, + posix_initialization_threads_table *user_threads + +) +{ + _POSIX_Threads_Number_of_initialization_threads = + number_of_initialization_threads; + _POSIX_Threads_User_initialization_threads = user_threads; + + /* + * There may not be any POSIX initialization threads configured. + */ + +#if 0 + if ( user_threads == NULL || number_of_initialization_threads == 0 ) + _Internal_error_Occurred( INTERNAL_ERROR_POSIX_API, TRUE, EINVAL ); +#endif + + _Objects_Initialize_information( + &_POSIX_Threads_Information, + OBJECTS_POSIX_THREADS, + FALSE, /* does not support global */ + maximum_pthreads, + sizeof( Thread_Control ), + TRUE, + 5, /* length is arbitrary for now */ + TRUE /* this class is threads */ + ); + + /* + * Add all the extensions for this API + */ + + _User_extensions_Add_API_set( &_POSIX_Threads_User_extensions ); + + _API_extensions_Add( &_POSIX_Threads_API_extensions ); + + /* + * If we supported MP, then here we would ... + * Register the MP Process Packet routine. + */ + +} + +/*PAGE + * + * 3.1.3 Register Fork Handlers, P1003.1c/Draft 10, P1003.1c/Draft 10, p. 27 + * + * RTEMS does not support processes, so we fall under this and do not + * provide this routine: + * + * "Either the implementation shall support the pthread_atfork() function + * as described above or the pthread_atfork() funciton shall not be + * provided." + */ + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_setscope( + pthread_attr_t *attr, + int contentionscope +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + switch ( contentionscope ) { + case PTHREAD_SCOPE_PROCESS: + attr->contentionscope = contentionscope; + return 0; + + case PTHREAD_SCOPE_SYSTEM: + return ENOTSUP; + + default: + return EINVAL; + } +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_getscope( + const pthread_attr_t *attr, + int *contentionscope +) +{ + if ( !attr || !attr->is_initialized || !contentionscope ) + return EINVAL; + + *contentionscope = attr->contentionscope; + return 0; +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_setinheritsched( + pthread_attr_t *attr, + int inheritsched +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + switch ( inheritsched ) { + case PTHREAD_INHERIT_SCHED: + case PTHREAD_EXPLICIT_SCHED: + attr->inheritsched = inheritsched; + return 0; + + default: + return ENOTSUP; + } +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_getinheritsched( + const pthread_attr_t *attr, + int *inheritsched +) +{ + if ( !attr || !attr->is_initialized || !inheritsched ) + return EINVAL; + + *inheritsched = attr->inheritsched; + return 0; +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_setschedpolicy( + pthread_attr_t *attr, + int policy +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + switch ( policy ) { + case SCHED_OTHER: + case SCHED_FIFO: + case SCHED_RR: + case SCHED_SPORADIC: + attr->schedpolicy = policy; + return 0; + + default: + return ENOTSUP; + } +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Parameters, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_getschedpolicy( + const pthread_attr_t *attr, + int *policy +) +{ + if ( !attr || !attr->is_initialized || !policy ) + return EINVAL; + + *policy = attr->schedpolicy; + return 0; +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Parameters, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_setschedparam( + pthread_attr_t *attr, + const struct sched_param *param +) +{ + if ( !attr || !attr->is_initialized || !param ) + return EINVAL; + + attr->schedparam = *param; + return 0; +} + +/*PAGE + * + * 13.5.1 Thread Creation Scheduling Attributes, P1003.1c/Draft 10, p. 120 + */ + +int pthread_attr_getschedparam( + const pthread_attr_t *attr, + struct sched_param *param +) +{ + if ( !attr || !attr->is_initialized || !param ) + return EINVAL; + + *param = attr->schedparam; + return 0; +} + +/*PAGE + * + * 13.5.2 Dynamic Thread Scheduling Parameters Access, + * P1003.1c/Draft 10, p. 124 + */ + +int pthread_getschedparam( + pthread_t thread, + int *policy, + struct sched_param *param +) +{ + Objects_Locations location; + POSIX_API_Control *api; + register Thread_Control *the_thread; + + if ( !policy || !param ) + return EINVAL; + + the_thread = _POSIX_Threads_Get( thread, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + return ESRCH; + case OBJECTS_LOCAL: + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + *policy = api->schedpolicy; + *param = api->schedparam; + param->sched_priority = + _POSIX_Priority_From_core( the_thread->current_priority ); + _Thread_Enable_dispatch(); + return 0; + } + + return POSIX_BOTTOM_REACHED(); + +} + +/*PAGE + * + * 13.5.2 Dynamic Thread Scheduling Parameters Access, + * P1003.1c/Draft 10, p. 124 + */ + +int pthread_setschedparam( + pthread_t thread, + int policy, + struct sched_param *param +) +{ + register Thread_Control *the_thread; + POSIX_API_Control *api; + Thread_CPU_budget_algorithms budget_algorithm; + Thread_CPU_budget_algorithm_callout budget_callout; + Objects_Locations location; + + /* + * Check all the parameters + */ + + if ( !param ) + return EINVAL; + + if ( !_POSIX_Priority_Is_valid( param->sched_priority ) ) + return EINVAL; + + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE; + budget_callout = NULL; + + switch ( policy ) { + case SCHED_OTHER: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE; + break; + + case SCHED_FIFO: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE; + break; + + case SCHED_RR: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE; + break; + + case SCHED_SPORADIC: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_CALLOUT; + budget_callout = _POSIX_Threads_Sporadic_budget_callout; + + if ( _POSIX_Timespec_to_interval( ¶m->ss_replenish_period ) < + _POSIX_Timespec_to_interval( ¶m->ss_initial_budget ) ) + return EINVAL; + + if ( !_POSIX_Priority_Is_valid( param->ss_low_priority ) ) + return EINVAL; + + break; + + default: + return EINVAL; + } + + /* + * Actually change the scheduling policy and parameters + */ + + the_thread = _POSIX_Threads_Get( thread, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + return ESRCH; + case OBJECTS_LOCAL: + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + if ( api->schedpolicy == SCHED_SPORADIC ) + (void) _Watchdog_Remove( &api->Sporadic_timer ); + + api->schedpolicy = policy; + api->schedparam = *param; + the_thread->budget_algorithm = budget_algorithm; + the_thread->budget_callout = budget_callout; + + switch ( api->schedpolicy ) { + case SCHED_OTHER: + case SCHED_FIFO: + case SCHED_RR: + the_thread->cpu_time_budget = _Thread_Ticks_per_timeslice; + + the_thread->real_priority = + _POSIX_Priority_To_core( api->schedparam.sched_priority ); + + _Thread_Change_priority( + the_thread, + the_thread->real_priority, + TRUE + ); + break; + + case SCHED_SPORADIC: + api->ss_high_priority = api->schedparam.sched_priority; + _POSIX_Threads_Sporadic_budget_TSR( 0, the_thread ); + break; + } + + _Thread_Enable_dispatch(); + return 0; + } + return POSIX_BOTTOM_REACHED(); +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_init( + pthread_attr_t *attr +) +{ + if ( !attr ) + return EINVAL; + + *attr = _POSIX_Threads_Default_attributes; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_destroy( + pthread_attr_t *attr +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + attr->is_initialized = FALSE; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_getstacksize( + const pthread_attr_t *attr, + size_t *stacksize +) +{ + if ( !attr || !attr->is_initialized || !stacksize ) + return EINVAL; + + *stacksize = attr->stacksize; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_setstacksize( + pthread_attr_t *attr, + size_t stacksize +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + if (stacksize < PTHREAD_MINIMUM_STACK_SIZE) + attr->stacksize = PTHREAD_MINIMUM_STACK_SIZE; + else + attr->stacksize = stacksize; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_getstackaddr( + const pthread_attr_t *attr, + void **stackaddr +) +{ + if ( !attr || !attr->is_initialized || !stackaddr ) + return EINVAL; + + *stackaddr = attr->stackaddr; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_setstackaddr( + pthread_attr_t *attr, + void *stackaddr +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + attr->stackaddr = stackaddr; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_getdetachstate( + const pthread_attr_t *attr, + int *detachstate +) +{ + if ( !attr || !attr->is_initialized || !detachstate ) + return EINVAL; + + *detachstate = attr->detachstate; + return 0; +} + +/*PAGE + * + * 16.1.1 Thread Creation Attributes, P1003.1c/Draft 10, p, 140 + */ + +int pthread_attr_setdetachstate( + pthread_attr_t *attr, + int detachstate +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + switch ( detachstate ) { + case PTHREAD_CREATE_DETACHED: + case PTHREAD_CREATE_JOINABLE: + attr->detachstate = detachstate; + return 0; + + default: + return EINVAL; + } +} + +/*PAGE + * + * 16.1.2 Thread Creation, P1003.1c/Draft 10, p. 144 + */ + +int pthread_create( + pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)( void * ), + void *arg +) +{ + const pthread_attr_t *the_attr; + Priority_Control core_priority; + Thread_CPU_budget_algorithms budget_algorithm; + Thread_CPU_budget_algorithm_callout budget_callout; + boolean is_fp; + boolean status; + Thread_Control *the_thread; + char *default_name = "psx"; + POSIX_API_Control *api; + int schedpolicy = SCHED_RR; + struct sched_param schedparam; + + the_attr = (attr) ? attr : &_POSIX_Threads_Default_attributes; + + if ( !the_attr->is_initialized ) + return EINVAL; + + /* + * Core Thread Initialize insures we get the minimum amount of + * stack space if it is allowed to allocate it itself. + */ + + if ( the_attr->stackaddr && !_Stack_Is_enough( the_attr->stacksize ) ) + return EINVAL; + +#if 0 + int cputime_clock_allowed; /* see time.h */ + POSIX_NOT_IMPLEMENTED(); +#endif + + /* + * P1003.1c/Draft 10, p. 121. + * + * If inheritsched is set to PTHREAD_INHERIT_SCHED, then this thread + * inherits scheduling attributes from the creating thread. If it is + * PTHREAD_EXPLICIT_SCHED, then scheduling parameters come from the + * attributes structure. + */ + + switch ( the_attr->inheritsched ) { + case PTHREAD_INHERIT_SCHED: + api = _Thread_Executing->API_Extensions[ THREAD_API_POSIX ]; + schedpolicy = api->schedpolicy; + schedparam = api->schedparam; + break; + + case PTHREAD_EXPLICIT_SCHED: + schedpolicy = the_attr->schedpolicy; + schedparam = the_attr->schedparam; + break; + + default: + return EINVAL; + } + + /* + * Check the contentionscope since rtems only supports PROCESS wide + * contention (i.e. no system wide contention). + */ + + if ( the_attr->contentionscope != PTHREAD_SCOPE_PROCESS ) + return ENOTSUP; + + /* + * Interpret the scheduling parameters. + */ + + if ( !_POSIX_Priority_Is_valid( schedparam.sched_priority ) ) + return EINVAL; + + core_priority = _POSIX_Priority_To_core( schedparam.sched_priority ); + + /* + * Set the core scheduling policy information. + */ + + budget_callout = NULL; + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE; + + switch ( schedpolicy ) { + case SCHED_OTHER: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE; + break; + + case SCHED_FIFO: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_NONE; + break; + + case SCHED_RR: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE; + break; + + case SCHED_SPORADIC: + budget_algorithm = THREAD_CPU_BUDGET_ALGORITHM_CALLOUT; + budget_callout = _POSIX_Threads_Sporadic_budget_callout; + + if ( _POSIX_Timespec_to_interval( &schedparam.ss_replenish_period ) < + _POSIX_Timespec_to_interval( &schedparam.ss_initial_budget ) ) + return EINVAL; + + if ( !_POSIX_Priority_Is_valid( schedparam.ss_low_priority ) ) + return EINVAL; + + break; + + default: + return EINVAL; + } + + /* + * Currently all POSIX threads are floating point if the hardware + * supports it. + */ + + is_fp = CPU_HARDWARE_FP; + + /* + * Disable dispatch for protection + */ + + _Thread_Disable_dispatch(); + + /* + * Allocate the thread control block. + * + * NOTE: Global threads are not currently supported. + */ + + the_thread = _POSIX_Threads_Allocate(); + + if ( !the_thread ) { + _Thread_Enable_dispatch(); + return EAGAIN; + } + + /* + * Initialize the core thread for this task. + */ + + status = _Thread_Initialize( + &_POSIX_Threads_Information, + the_thread, + the_attr->stackaddr, + the_attr->stacksize, + is_fp, + core_priority, + TRUE, /* preemptible */ + budget_algorithm, + budget_callout, + 0, /* isr level */ + &default_name /* posix threads don't have a name */ + ); + + if ( !status ) { + _POSIX_Threads_Free( the_thread ); + _Thread_Enable_dispatch(); + return EAGAIN; + } + + /* + * finish initializing the per API structure + */ + + + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + api->Attributes = *the_attr; + api->detachstate = the_attr->detachstate; + api->schedpolicy = schedpolicy; + api->schedparam = schedparam; + + /* + * This insures we evaluate the process-wide signals pending when we + * first run. + * + * NOTE: Since the thread starts with all unblocked, this is necessary. + */ + + the_thread->do_post_task_switch_extension = TRUE; + + /* + * POSIX threads are allocated and started in one operation. + */ + + status = _Thread_Start( + the_thread, + THREAD_START_POINTER, + start_routine, + arg, + 0 /* unused */ + ); + + if ( schedpolicy == SCHED_SPORADIC ) { + _Watchdog_Insert_ticks( + &api->Sporadic_timer, + _POSIX_Timespec_to_interval( &api->schedparam.ss_replenish_period ) + ); + } + + /* + * _Thread_Start only fails if the thread was in the incorrect state + */ + + if ( !status ) { + _POSIX_Threads_Free( the_thread ); + _Thread_Enable_dispatch(); + return EINVAL; + } + + /* + * Return the id and indicate we successfully created the thread + */ + + *thread = the_thread->Object.id; + + _Thread_Enable_dispatch(); + + return 0; +} + +/*PAGE + * + * 16.1.3 Wait for Thread Termination, P1003.1c/Draft 10, p. 147 + */ + +int pthread_join( + pthread_t thread, + void **value_ptr +) +{ + register Thread_Control *the_thread; + POSIX_API_Control *api; + Objects_Locations location; + void *return_pointer; + + the_thread = _POSIX_Threads_Get( thread, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + return ESRCH; + case OBJECTS_LOCAL: + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + if ( api->detachstate == PTHREAD_CREATE_DETACHED ) { + _Thread_Enable_dispatch(); + return EINVAL; + } + + if ( _Thread_Is_executing( the_thread ) ) { + _Thread_Enable_dispatch(); + return EDEADLK; + } + + /* + * Put ourself on the threads join list + */ + + _Thread_Executing->Wait.return_argument = (unsigned32 *) &return_pointer; + + _Thread_queue_Enter_critical_section( &api->Join_List ); + + _Thread_queue_Enqueue( &api->Join_List, WATCHDOG_NO_TIMEOUT ); + + _Thread_Enable_dispatch(); + + if ( value_ptr ) + *value_ptr = return_pointer; + return 0; + } + + return POSIX_BOTTOM_REACHED(); +} + +/*PAGE + * + * 16.1.4 Detaching a Thread, P1003.1c/Draft 10, p. 149 + */ + +int pthread_detach( + pthread_t thread +) +{ + register Thread_Control *the_thread; + POSIX_API_Control *api; + Objects_Locations location; + + the_thread = _POSIX_Threads_Get( thread, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + return ESRCH; + case OBJECTS_LOCAL: + + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + api->detachstate = PTHREAD_CREATE_DETACHED; + _Thread_Enable_dispatch(); + return 0; + } + + return POSIX_BOTTOM_REACHED(); +} + +/*PAGE + * + * 16.1.5.1 Thread Termination, p1003.1c/Draft 10, p. 150 + * + * NOTE: Key destructors are executed in the POSIX api delete extension. + */ + +void pthread_exit( + void *value_ptr +) +{ + _Thread_Disable_dispatch(); + + _Thread_Executing->Wait.return_argument = (unsigned32 *)value_ptr; + + _Thread_Close( &_POSIX_Threads_Information, _Thread_Executing ); + + _POSIX_Threads_Free( _Thread_Executing ); + + _Thread_Enable_dispatch(); +} + +/*PAGE + * + * 16.1.6 Get Calling Thread's ID, p1003.1c/Draft 10, p. 152 + */ + +pthread_t pthread_self( void ) +{ + return _Thread_Executing->Object.id; +} + +/*PAGE + * + * 16.1.7 Compare Thread IDs, p1003.1c/Draft 10, p. 153 + * + * NOTE: POSIX does not define the behavior when either thread id is invalid. + */ + +int pthread_equal( + pthread_t t1, + pthread_t t2 +) +{ + /* + * If the system is configured for debug, then we will do everything we + * can to insure that both ids are valid. Otherwise, we will do the + * cheapest possible thing to determine if they are equal. + */ + +#ifndef RTEMS_DEBUG + return _Objects_Are_ids_equal( t1, t2 ); +#else + int status; + Objects_Locations location; + + /* + * By default this is not a match. + */ + + status = 0; + + /* + * Validate the first id and return 0 if it is not valid + */ + + (void) _POSIX_Threads_Get( t1, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + break; + + case OBJECTS_LOCAL: + + /* + * Validate the second id and return 0 if it is not valid + */ + + (void) _POSIX_Threads_Get( t2, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: + break; + case OBJECTS_LOCAL: + status = _Objects_Are_ids_equal( t1, t2 ); + break; + } + _Thread_Unnest_dispatch(); + break; + } + + _Thread_Enable_dispatch(); + return status; +#endif +} + +/*PAGE + * + * 16.1.8 Dynamic Package Initialization, P1003.1c/Draft 10, p. 154 + */ + +int pthread_once( + pthread_once_t *once_control, + void (*init_routine)(void) +) +{ + if ( !once_control || !init_routine ) + return EINVAL; + + _Thread_Disable_dispatch(); + + if ( !once_control->init_executed ) { + once_control->is_initialized = TRUE; + once_control->init_executed = TRUE; + (*init_routine)(); + } + + _Thread_Enable_dispatch(); + return 0; +} + +/*PAGE + * + * 20.1.6 Accessing a Thread CPU-time Clock, P1003.4b/Draft 8, p. 58 + */ + +int pthread_getcpuclockid( + pthread_t pid, + clockid_t *clock_id +) +{ + return POSIX_NOT_IMPLEMENTED(); +} + +/*PAGE + * + * 20.1.7 CPU-time Clock Thread Creation Attribute, P1003.4b/Draft 8, p. 59 + */ + +int pthread_attr_setcputime( + pthread_attr_t *attr, + int clock_allowed +) +{ + if ( !attr || !attr->is_initialized ) + return EINVAL; + + switch ( clock_allowed ) { + case CLOCK_ENABLED: + case CLOCK_DISABLED: + attr->cputime_clock_allowed = clock_allowed; + return 0; + + default: + return EINVAL; + } +} + +/*PAGE + * + * 20.1.7 CPU-time Clock Thread Creation Attribute, P1003.4b/Draft 8, p. 59 + */ + +int pthread_attr_getcputime( + pthread_attr_t *attr, + int *clock_allowed +) +{ + if ( !attr || !attr->is_initialized || !clock_allowed ) + return EINVAL; + + *clock_allowed = attr->cputime_clock_allowed; + return 0; +} |