forked from Imagelibrary/rtems
Run the thread restart extensions in the context of the restarted thread. Run them with thread dispatching enabled.
414 lines
12 KiB
C
414 lines
12 KiB
C
/**
|
|
* @file
|
|
*
|
|
* @brief Private Support Information for POSIX Threads
|
|
* @ingroup POSIX_PTHREADS Private Threads
|
|
*/
|
|
|
|
/*
|
|
* COPYRIGHT (c) 1989-2014.
|
|
* On-Line Applications Research Corporation (OAR).
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
|
|
#include <rtems/system.h>
|
|
#include <rtems/config.h>
|
|
#include <rtems/score/apiext.h>
|
|
#include <rtems/score/stack.h>
|
|
#include <rtems/score/threadimpl.h>
|
|
#include <rtems/score/threadqimpl.h>
|
|
#include <rtems/score/userextimpl.h>
|
|
#include <rtems/score/watchdogimpl.h>
|
|
#include <rtems/score/wkspace.h>
|
|
#include <rtems/posix/cancel.h>
|
|
#include <rtems/posix/pthreadimpl.h>
|
|
#include <rtems/posix/priorityimpl.h>
|
|
#include <rtems/posix/psignalimpl.h>
|
|
#include <rtems/posix/config.h>
|
|
#include <rtems/posix/keyimpl.h>
|
|
#include <rtems/posix/time.h>
|
|
#include <rtems/score/timespec.h>
|
|
|
|
/*
|
|
* The default pthreads attributes structure.
|
|
*
|
|
* NOTE: Be careful .. if the default attribute set changes,
|
|
* _POSIX_Threads_Initialize_user_threads will need to be examined.
|
|
*/
|
|
pthread_attr_t _POSIX_Threads_Default_attributes = {
|
|
.is_initialized = true, /* is_initialized */
|
|
.stackaddr = NULL, /* stackaddr */
|
|
.stacksize = 0, /* stacksize -- will be adjusted to minimum */
|
|
.contentionscope = PTHREAD_SCOPE_PROCESS, /* contentionscope */
|
|
.inheritsched = PTHREAD_INHERIT_SCHED, /* inheritsched */
|
|
.schedpolicy = SCHED_FIFO, /* schedpolicy */
|
|
.schedparam =
|
|
{ /* schedparam */
|
|
2, /* sched_priority */
|
|
#if defined(_POSIX_SPORADIC_SERVER) || \
|
|
defined(_POSIX_THREAD_SPORADIC_SERVER)
|
|
0, /* sched_ss_low_priority */
|
|
{ 0L, 0 }, /* sched_ss_repl_period */
|
|
{ 0L, 0 }, /* sched_ss_init_budget */
|
|
0 /* sched_ss_max_repl */
|
|
#endif
|
|
},
|
|
|
|
#if HAVE_DECL_PTHREAD_ATTR_SETGUARDSIZE
|
|
.guardsize = 0, /* guardsize */
|
|
#endif
|
|
#if defined(_POSIX_THREAD_CPUTIME)
|
|
.cputime_clock_allowed = 1, /* cputime_clock_allowed */
|
|
#endif
|
|
.detachstate = PTHREAD_CREATE_JOINABLE, /* detachstate */
|
|
#if defined(__RTEMS_HAVE_SYS_CPUSET_H__)
|
|
.affinitysetsize = 0,
|
|
.affinityset = NULL,
|
|
.affinitysetpreallocated = {{0x0}}
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* _POSIX_Threads_Sporadic_budget_TSR
|
|
*/
|
|
void _POSIX_Threads_Sporadic_budget_TSR(
|
|
Objects_Id id __attribute__((unused)),
|
|
void *argument
|
|
)
|
|
{
|
|
uint32_t ticks;
|
|
uint32_t new_priority;
|
|
Thread_Control *the_thread;
|
|
POSIX_API_Control *api;
|
|
|
|
the_thread = argument;
|
|
|
|
api = the_thread->API_Extensions[ THREAD_API_POSIX ];
|
|
|
|
/* ticks is guaranteed to be at least one */
|
|
ticks = _Timespec_To_ticks( &api->schedparam.sched_ss_init_budget );
|
|
|
|
the_thread->cpu_time_budget = ticks;
|
|
|
|
new_priority = _POSIX_Priority_To_core( api->schedparam.sched_priority );
|
|
the_thread->real_priority = new_priority;
|
|
|
|
/*
|
|
* If holding a resource, then do not change it.
|
|
*/
|
|
#if 0
|
|
printk( "TSR %d %d %d\n", the_thread->resource_count,
|
|
the_thread->current_priority, new_priority );
|
|
#endif
|
|
if ( the_thread->resource_count == 0 ) {
|
|
/*
|
|
* If this would make them less important, then do not change it.
|
|
*/
|
|
if ( the_thread->current_priority > new_priority ) {
|
|
_Thread_Change_priority( the_thread, new_priority, true );
|
|
#if 0
|
|
printk( "raise priority\n" );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* ticks is guaranteed to be at least one */
|
|
ticks = _Timespec_To_ticks( &api->schedparam.sched_ss_repl_period );
|
|
|
|
_Watchdog_Insert_ticks( &api->Sporadic_timer, ticks );
|
|
}
|
|
|
|
/*
|
|
* _POSIX_Threads_Sporadic_budget_callout
|
|
*/
|
|
void _POSIX_Threads_Sporadic_budget_callout(
|
|
Thread_Control *the_thread
|
|
)
|
|
{
|
|
POSIX_API_Control *api;
|
|
uint32_t 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 = UINT32_MAX;
|
|
|
|
new_priority = _POSIX_Priority_To_core(api->schedparam.sched_ss_low_priority);
|
|
the_thread->real_priority = new_priority;
|
|
|
|
/*
|
|
* If holding a resource, then do not change it.
|
|
*/
|
|
#if 0
|
|
printk( "callout %d %d %d\n", the_thread->resource_count,
|
|
the_thread->current_priority, new_priority );
|
|
#endif
|
|
if ( the_thread->resource_count == 0 ) {
|
|
/*
|
|
* Make sure we are actually lowering it. If they have lowered it
|
|
* to logically lower than sched_ss_low_priority, then we do not want to
|
|
* change it.
|
|
*/
|
|
if ( the_thread->current_priority < new_priority ) {
|
|
_Thread_Change_priority( the_thread, new_priority, true );
|
|
#if 0
|
|
printk( "lower priority\n" );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* _POSIX_Threads_Create_extension
|
|
*
|
|
* This method is invoked for each thread created.
|
|
*/
|
|
|
|
static bool _POSIX_Threads_Create_extension(
|
|
Thread_Control *executing __attribute__((unused)),
|
|
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 */
|
|
_POSIX_Threads_Initialize_attributes( &api->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 );
|
|
|
|
/*
|
|
* POSIX 1003.1 1996, 18.2.2.2
|
|
*/
|
|
api->cancelation_requested = 0;
|
|
api->cancelability_state = PTHREAD_CANCEL_ENABLE;
|
|
api->cancelability_type = PTHREAD_CANCEL_DEFERRED;
|
|
#ifndef HAVE_STRUCT__PTHREAD_CLEANUP_CONTEXT
|
|
_Chain_Initialize_empty (&api->Cancellation_Handlers);
|
|
#else /* HAVE_STRUCT__PTHREAD_CLEANUP_CONTEXT */
|
|
api->last_cleanup_context = NULL;
|
|
#endif /* HAVE_STRUCT__PTHREAD_CLEANUP_CONTEXT */
|
|
|
|
/*
|
|
* If the thread is not a posix thread, then all posix signals are blocked
|
|
* by default.
|
|
*
|
|
* The check for class == 1 is debug. Should never really happen.
|
|
*/
|
|
api->signals_pending = SIGNAL_EMPTY_MASK;
|
|
if ( _Objects_Get_API( created->Object.id ) == OBJECTS_POSIX_API
|
|
#if defined(RTEMS_DEBUG)
|
|
&& _Objects_Get_class( created->Object.id ) == 1
|
|
#endif
|
|
) {
|
|
executing_api = _Thread_Executing->API_Extensions[ THREAD_API_POSIX ];
|
|
api->signals_blocked = executing_api->signals_blocked;
|
|
} else {
|
|
api->signals_blocked = SIGNAL_ALL_MASK;
|
|
}
|
|
|
|
_Thread_Action_initialize(
|
|
&api->Signal_action,
|
|
_POSIX_signals_Action_handler
|
|
);
|
|
|
|
_Thread_queue_Initialize(
|
|
&api->Join_List,
|
|
THREAD_QUEUE_DISCIPLINE_FIFO,
|
|
STATES_WAITING_FOR_JOIN_AT_EXIT | STATES_INTERRUPTIBLE_BY_SIGNAL,
|
|
0
|
|
);
|
|
|
|
_Watchdog_Initialize(
|
|
&api->Sporadic_timer,
|
|
_POSIX_Threads_Sporadic_budget_TSR,
|
|
created->Object.id,
|
|
created
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void _POSIX_Threads_Restart_extension(
|
|
Thread_Control *executing,
|
|
Thread_Control *restarted
|
|
)
|
|
{
|
|
(void) executing;
|
|
_POSIX_Threads_cancel_run( restarted );
|
|
}
|
|
|
|
/*
|
|
* _POSIX_Threads_Delete_extension
|
|
*
|
|
* This method is invoked for each thread deleted.
|
|
*/
|
|
static void _POSIX_Threads_Delete_extension(
|
|
Thread_Control *executing __attribute__((unused)),
|
|
Thread_Control *deleted
|
|
)
|
|
{
|
|
Thread_Control *the_thread;
|
|
POSIX_API_Control *api;
|
|
void **value_ptr;
|
|
|
|
api = deleted->API_Extensions[ THREAD_API_POSIX ];
|
|
|
|
/*
|
|
* Run the POSIX cancellation handlers
|
|
*/
|
|
_POSIX_Threads_cancel_run( 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;
|
|
|
|
_Workspace_Free( api );
|
|
}
|
|
|
|
/*
|
|
* _POSIX_Threads_Exitted_extension
|
|
*
|
|
* This method is invoked each time a thread exits.
|
|
*/
|
|
static void _POSIX_Threads_Exitted_extension(
|
|
Thread_Control *executing
|
|
)
|
|
{
|
|
/*
|
|
* If the executing thread was not created with the POSIX API, then this
|
|
* API do not get to define its exit behavior.
|
|
*/
|
|
if ( _Objects_Get_API( executing->Object.id ) == OBJECTS_POSIX_API )
|
|
pthread_exit( executing->Wait.return_argument );
|
|
}
|
|
|
|
/*
|
|
* _POSIX_Threads_Initialize_user_threads
|
|
*
|
|
* This routine creates and starts all configured user
|
|
* initialization threads.
|
|
*/
|
|
static void _POSIX_Threads_Initialize_user_threads( void )
|
|
{
|
|
if ( _POSIX_Threads_Initialize_user_threads_p )
|
|
(*_POSIX_Threads_Initialize_user_threads_p)();
|
|
}
|
|
|
|
/*
|
|
* API Extension control structures
|
|
*/
|
|
API_extensions_Control _POSIX_Threads_API_extensions = {
|
|
#if defined(FUNCTIONALITY_NOT_CURRENTLY_USED_BY_ANY_API)
|
|
.predriver_hook = NULL,
|
|
#endif
|
|
.postdriver_hook = _POSIX_Threads_Initialize_user_threads
|
|
};
|
|
|
|
User_extensions_Control _POSIX_Threads_User_extensions = {
|
|
{ NULL, NULL },
|
|
{ { NULL, NULL }, NULL },
|
|
{ _POSIX_Threads_Create_extension, /* create */
|
|
NULL, /* start */
|
|
_POSIX_Threads_Restart_extension, /* restart */
|
|
_POSIX_Threads_Delete_extension, /* delete */
|
|
NULL, /* switch */
|
|
NULL, /* begin */
|
|
_POSIX_Threads_Exitted_extension, /* exitted */
|
|
NULL /* fatal */
|
|
}
|
|
};
|
|
|
|
/*
|
|
* _POSIX_Threads_Manager_initialization
|
|
*
|
|
* This routine initializes all threads manager related data structures.
|
|
*/
|
|
void _POSIX_Threads_Manager_initialization(void)
|
|
{
|
|
#if defined(__RTEMS_HAVE_SYS_CPUSET_H__)
|
|
pthread_attr_t *attr;
|
|
int i;
|
|
int max_cpus = 1;
|
|
|
|
/* Initialize default attribute. */
|
|
attr = &_POSIX_Threads_Default_attributes;
|
|
|
|
/* We do not support a cpu count over CPU_SETSIZE */
|
|
max_cpus = _SMP_Get_processor_count();
|
|
assert( max_cpus <= CPU_SETSIZE );
|
|
|
|
/* Initialize the affinity to be the set of all available CPU's */
|
|
attr->affinityset = &attr->affinitysetpreallocated;
|
|
attr->affinitysetsize = sizeof( *attr->affinityset );
|
|
CPU_ZERO_S( attr->affinitysetsize, &attr->affinitysetpreallocated );
|
|
|
|
for (i=0; i<max_cpus; i++)
|
|
CPU_SET_S(i, attr->affinitysetsize, attr->affinityset );
|
|
#endif
|
|
|
|
_Objects_Initialize_information(
|
|
&_POSIX_Threads_Information, /* object information table */
|
|
OBJECTS_POSIX_API, /* object API */
|
|
OBJECTS_POSIX_THREADS, /* object class */
|
|
Configuration_POSIX_API.maximum_threads,
|
|
/* maximum objects of this class */
|
|
sizeof( Thread_Control ),
|
|
/* size of this object's control block */
|
|
true, /* true if names for this object are strings */
|
|
_POSIX_PATH_MAX /* maximum length of each object's name */
|
|
#if defined(RTEMS_MULTIPROCESSING)
|
|
,
|
|
false, /* true if this is a global object class */
|
|
NULL /* Proxy extraction support callout */
|
|
#endif
|
|
);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
}
|