diff options
Diffstat (limited to 'cpukit/score/src/thread.c')
-rw-r--r-- | cpukit/score/src/thread.c | 1395 |
1 files changed, 1395 insertions, 0 deletions
diff --git a/cpukit/score/src/thread.c b/cpukit/score/src/thread.c new file mode 100644 index 0000000000..e4a3614a53 --- /dev/null +++ b/cpukit/score/src/thread.c @@ -0,0 +1,1395 @@ +/* + * Thread Handler + * + * + * COPYRIGHT (c) 1989-1997. + * On-Line Applications Research Corporation (OAR). + * Copyright assigned to U.S. Government, 1994. + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ + +#include <rtems/system.h> +#include <rtems/score/apiext.h> +#include <rtems/score/context.h> +#include <rtems/score/interr.h> +#include <rtems/score/isr.h> +#include <rtems/score/object.h> +#include <rtems/score/priority.h> +#include <rtems/score/states.h> +#include <rtems/score/sysstate.h> +#include <rtems/score/thread.h> +#include <rtems/score/threadq.h> +#include <rtems/score/userext.h> +#include <rtems/score/wkspace.h> + +/*PAGE + * + * _Thread_Handler_initialization + * + * This routine initializes all thread manager related data structures. + * + * Input parameters: + * ticks_per_timeslice - clock ticks per quantum + * maximum_proxies - number of proxies to initialize + * + * Output parameters: NONE + */ + +char *_Thread_Idle_name = "IDLE"; + +void _Thread_Handler_initialization( + unsigned32 ticks_per_timeslice, + unsigned32 maximum_extensions, + unsigned32 maximum_proxies +) +{ + unsigned32 index; + + /* + * BOTH stacks hooks must be set or both must be NULL. + * Do not allow mixture. + */ + + if ( !( ( _CPU_Table.stack_allocate_hook == 0 ) + == ( _CPU_Table.stack_free_hook == 0 ) ) ) + _Internal_error_Occurred( + INTERNAL_ERROR_CORE, + TRUE, + INTERNAL_ERROR_BAD_STACK_HOOK + ); + + _Context_Switch_necessary = FALSE; + _Thread_Executing = NULL; + _Thread_Heir = NULL; + _Thread_Allocated_fp = NULL; + + _Thread_Do_post_task_switch_extension = 0; + + _Thread_Maximum_extensions = maximum_extensions; + + _Thread_Ticks_per_timeslice = ticks_per_timeslice; + + _Thread_Ready_chain = (Chain_Control *) _Workspace_Allocate_or_fatal_error( + (PRIORITY_MAXIMUM + 1) * sizeof(Chain_Control) + ); + + for ( index=0; index <= PRIORITY_MAXIMUM ; index++ ) + _Chain_Initialize_empty( &_Thread_Ready_chain[ index ] ); + + _Thread_MP_Handler_initialization( maximum_proxies ); + + /* + * Initialize this class of objects. + */ + + _Objects_Initialize_information( + &_Thread_Internal_information, + OBJECTS_INTERNAL_THREADS, + FALSE, + ( _System_state_Is_multiprocessing ) ? 2 : 1, + sizeof( Thread_Control ), + TRUE, + 8, + TRUE + ); + +} + +/*PAGE + * + * _Thread_Create_idle + */ + +void _Thread_Create_idle( void ) +{ + void *idle; + + /* + * The entire workspace is zeroed during its initialization. Thus, all + * fields not explicitly assigned were explicitly zeroed by + * _Workspace_Initialization. + */ + + _Thread_Idle = _Thread_Internal_allocate(); + + /* + * Initialize the IDLE task. + */ + +#if (CPU_PROVIDES_IDLE_THREAD_BODY == TRUE) + idle = (void *) _CPU_Thread_Idle_body; +#else + idle = (void *) _Thread_Idle_body; +#endif + + if ( _CPU_Table.idle_task ) + idle = _CPU_Table.idle_task; + + _Thread_Initialize( + &_Thread_Internal_information, + _Thread_Idle, + NULL, /* allocate the stack */ + THREAD_IDLE_STACK_SIZE, + CPU_IDLE_TASK_IS_FP, + PRIORITY_MAXIMUM, + TRUE, /* preemptable */ + THREAD_CPU_BUDGET_ALGORITHM_NONE, + NULL, /* no budget algorithm callout */ + 0, /* all interrupts enabled */ + _Thread_Idle_name + ); + + /* + * WARNING!!! This is necessary to "kick" start the system and + * MUST be done before _Thread_Start is invoked. + */ + + _Thread_Heir = + _Thread_Executing = _Thread_Idle; + + _Thread_Start( + _Thread_Idle, + THREAD_START_NUMERIC, + idle, + NULL, + 0 + ); + +} + +/*PAGE + * + * _Thread_Start_multitasking + * + * This kernel routine readies the requested thread, the thread chain + * is adjusted. A new heir thread may be selected. + * + * Input parameters: + * system_thread - pointer to system initialization thread control block + * idle_thread - pointer to idle thread control block + * + * Output parameters: NONE + * + * NOTE: This routine uses the "blocking" heir selection mechanism. + * This insures the correct heir after a thread restart. + * + * INTERRUPT LATENCY: + * ready chain + * select heir + */ + +void _Thread_Start_multitasking( void ) +{ + /* + * The system is now multitasking and completely initialized. + * This system thread now either "goes away" in a single processor + * system or "turns into" the server thread in an MP system. + */ + + _System_state_Set( SYSTEM_STATE_UP ); + + _Context_Switch_necessary = FALSE; + + _Thread_Executing = _Thread_Heir; + + /* + * Get the init task(s) running. + * + * Note: Thread_Dispatch() is normally used to dispatch threads. As + * part of its work, Thread_Dispatch() restores floating point + * state for the heir task. + * + * This code avoids Thread_Dispatch(), and so we have to restore + * (actually initialize) the floating point state "by hand". + * + * Ignore the CPU_USE_DEFERRED_FP_SWITCH because we must always + * switch in the first thread if it is FP. + */ + + +#if ( CPU_HARDWARE_FP == TRUE ) + /* + * don't need to worry about saving BSP's floating point state + */ + + if ( _Thread_Heir->fp_context != NULL ) + _Context_Restore_fp( &_Thread_Heir->fp_context ); +#endif + + _Context_Switch( &_Thread_BSP_context, &_Thread_Heir->Registers ); +} + +/*PAGE + * + * _Thread_Dispatch + * + * This kernel routine determines if a dispatch is needed, and if so + * dispatches to the heir thread. Once the heir is running an attempt + * is made to dispatch any ASRs. + * + * ALTERNATE ENTRY POINTS: + * void _Thread_Enable_dispatch(); + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * dispatch thread + * no dispatch thread + */ + +#if ( CPU_INLINE_ENABLE_DISPATCH == FALSE ) +void _Thread_Enable_dispatch( void ) +{ + if ( --_Thread_Dispatch_disable_level ) + return; + _Thread_Dispatch(); +} +#endif + +void _Thread_Dispatch( void ) +{ + Thread_Control *executing; + Thread_Control *heir; + ISR_Level level; + + executing = _Thread_Executing; + _ISR_Disable( level ); + while ( _Context_Switch_necessary == TRUE ) { + heir = _Thread_Heir; + _Thread_Dispatch_disable_level = 1; + _Context_Switch_necessary = FALSE; + _Thread_Executing = heir; + _ISR_Enable( level ); + + heir->ticks_executed++; + + _User_extensions_Thread_switch( executing, heir ); + + if ( heir->budget_algorithm == THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE ) + heir->cpu_time_budget = _Thread_Ticks_per_timeslice; + + /* + * If the CPU has hardware floating point, then we must address saving + * and restoring it as part of the context switch. + * + * The second conditional compilation section selects the algorithm used + * to context switch between floating point tasks. The deferred algorithm + * can be significantly better in a system with few floating point tasks + * because it reduces the total number of save and restore FP context + * operations. However, this algorithm can not be used on all CPUs due + * to unpredictable use of FP registers by some compilers for integer + * operations. + */ + +#if ( CPU_HARDWARE_FP == TRUE ) +#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE ) + if ( (heir->fp_context != NULL) && !_Thread_Is_allocated_fp( heir ) ) { + if ( _Thread_Allocated_fp != NULL ) + _Context_Save_fp( &_Thread_Allocated_fp->fp_context ); + _Context_Restore_fp( &heir->fp_context ); + _Thread_Allocated_fp = heir; + } +#else + if ( executing->fp_context != NULL ) + _Context_Save_fp( &executing->fp_context ); + + if ( heir->fp_context != NULL ) + _Context_Restore_fp( &heir->fp_context ); +#endif +#endif + + _Context_Switch( &executing->Registers, &heir->Registers ); + + executing = _Thread_Executing; + + _ISR_Disable( level ); + } + + _Thread_Dispatch_disable_level = 0; + + _ISR_Enable( level ); + + if ( _Thread_Do_post_task_switch_extension || + executing->do_post_task_switch_extension ) { + executing->do_post_task_switch_extension = FALSE; + _API_extensions_Run_postswitch(); + } + +} + +/*PAGE + * + * _Thread_Stack_Allocate + * + * Allocate the requested stack space for the thread. + * return the actual size allocated after any adjustment + * or return zero if the allocation failed. + * Set the Start.stack field to the address of the stack + */ + +static unsigned32 _Thread_Stack_Allocate( + Thread_Control *the_thread, + unsigned32 stack_size) +{ + void *stack_addr = 0; + + if ( !_Stack_Is_enough( stack_size ) ) + stack_size = STACK_MINIMUM_SIZE; + + /* + * Call ONLY the CPU table stack allocate hook, _or_ the + * the RTEMS workspace allocate. This is so the stack free + * routine can call the correct deallocation routine. + */ + + if ( _CPU_Table.stack_allocate_hook ) + { + stack_addr = (*_CPU_Table.stack_allocate_hook)( stack_size ); + } else { + + /* + * First pad the requested size so we allocate enough memory + * so the context initialization can align it properly. The address + * returned the workspace allocate must be directly stored in the + * stack control block because it is later used in the free sequence. + * + * Thus it is the responsibility of the CPU dependent code to + * get and keep the stack adjust factor, the stack alignment, and + * the context initialization sequence in sync. + */ + + stack_size = _Stack_Adjust_size( stack_size ); + stack_addr = _Workspace_Allocate( stack_size ); + } + + if ( !stack_addr ) + stack_size = 0; + + the_thread->Start.stack = stack_addr; + + return stack_size; +} + +/* + * _Thread_Stack_Free + * + * Deallocate the Thread's stack. + */ + +static void _Thread_Stack_Free( + Thread_Control *the_thread +) +{ + /* + * If the API provided the stack space, then don't free it. + */ + + if ( !the_thread->Start.core_allocated_stack ) + return; + + /* + * Call ONLY the CPU table stack free hook, or the + * the RTEMS workspace free. This is so the free + * routine properly matches the allocation of the stack. + */ + + if ( _CPU_Table.stack_free_hook ) + (*_CPU_Table.stack_free_hook)( the_thread->Start.Initial_stack.area ); + else + _Workspace_Free( the_thread->Start.Initial_stack.area ); +} + +/*PAGE + * + * _Thread_Initialize + * + * XXX + */ + +boolean _Thread_Initialize( + Objects_Information *information, + Thread_Control *the_thread, + void *stack_area, + unsigned32 stack_size, + boolean is_fp, + Priority_Control priority, + boolean is_preemptible, + Thread_CPU_budget_algorithms budget_algorithm, + Thread_CPU_budget_algorithm_callout budget_callout, + unsigned32 isr_level, + Objects_Name name +) +{ + unsigned32 actual_stack_size = 0; + void *stack = NULL; + void *fp_area; + void *extensions_area; + + /* + * Allocate and Initialize the stack for this thread. + */ + + + if ( !stack ) { + if ( !_Stack_Is_enough( stack_size ) ) + actual_stack_size = STACK_MINIMUM_SIZE; + else + actual_stack_size = stack_size; + + actual_stack_size = _Stack_Adjust_size( actual_stack_size ); + stack = stack_area; + + actual_stack_size = _Thread_Stack_Allocate( the_thread, stack_size ); + + if ( !actual_stack_size ) + return FALSE; /* stack allocation failed */ + + stack = the_thread->Start.stack; + + the_thread->Start.core_allocated_stack = TRUE; + } else { + stack = stack_area; + actual_stack_size = stack_size; + the_thread->Start.core_allocated_stack = FALSE; + } + + _Stack_Initialize( + &the_thread->Start.Initial_stack, + stack, + actual_stack_size + ); + + /* + * Allocate the floating point area for this thread + */ + + if ( is_fp ) { + + fp_area = _Workspace_Allocate( CONTEXT_FP_SIZE ); + if ( !fp_area ) { + _Thread_Stack_Free( the_thread ); + return FALSE; + } + fp_area = _Context_Fp_start( fp_area, 0 ); + + } else + fp_area = NULL; + + the_thread->fp_context = fp_area; + the_thread->Start.fp_context = fp_area; + + /* + * Allocate the extensions area for this thread + */ + + if ( _Thread_Maximum_extensions ) { + extensions_area = _Workspace_Allocate( + (_Thread_Maximum_extensions + 1) * sizeof( void * ) + ); + + if ( !extensions_area ) { + if ( fp_area ) + (void) _Workspace_Free( fp_area ); + + _Thread_Stack_Free( the_thread ); + + return FALSE; + } + } else + extensions_area = NULL; + + the_thread->extensions = (void **) extensions_area; + + /* + * General initialization + */ + + the_thread->Start.is_preemptible = is_preemptible; + the_thread->Start.budget_algorithm = budget_algorithm; + the_thread->Start.budget_callout = budget_callout; + the_thread->Start.isr_level = isr_level; + + the_thread->current_state = STATES_DORMANT; + the_thread->resource_count = 0; + the_thread->real_priority = priority; + the_thread->Start.initial_priority = priority; + the_thread->ticks_executed = 0; + + _Thread_Set_priority( the_thread, priority ); + + /* + * Open the object + */ + + _Objects_Open( information, &the_thread->Object, name ); + + /* + * Invoke create extensions + */ + + if ( !_User_extensions_Thread_create( the_thread ) ) { + + if ( extensions_area ) + (void) _Workspace_Free( extensions_area ); + + if ( fp_area ) + (void) _Workspace_Free( fp_area ); + + _Thread_Stack_Free( the_thread ); + + return FALSE; + } + + return TRUE; + +} + +/* + * _Thread_Start + * + * DESCRIPTION: + * + * XXX + */ + +boolean _Thread_Start( + Thread_Control *the_thread, + Thread_Start_types the_prototype, + void *entry_point, + void *pointer_argument, + unsigned32 numeric_argument +) +{ + if ( _States_Is_dormant( the_thread->current_state ) ) { + + the_thread->Start.entry_point = (Thread_Entry) entry_point; + + the_thread->Start.prototype = the_prototype; + the_thread->Start.pointer_argument = pointer_argument; + the_thread->Start.numeric_argument = numeric_argument; + + _Thread_Load_environment( the_thread ); + + _Thread_Ready( the_thread ); + + _User_extensions_Thread_start( the_thread ); + + return TRUE; + } + + return FALSE; + +} + +/* + * _Thread_Restart + * + * DESCRIPTION: + * + * XXX + */ + +boolean _Thread_Restart( + Thread_Control *the_thread, + void *pointer_argument, + unsigned32 numeric_argument +) +{ + if ( !_States_Is_dormant( the_thread->current_state ) ) { + + _Thread_Set_transient( the_thread ); + the_thread->resource_count = 0; + the_thread->is_preemptible = the_thread->Start.is_preemptible; + the_thread->budget_algorithm = the_thread->Start.budget_algorithm; + the_thread->budget_callout = the_thread->Start.budget_callout; + + the_thread->Start.pointer_argument = pointer_argument; + the_thread->Start.numeric_argument = numeric_argument; + + if ( !_Thread_queue_Extract_with_proxy( the_thread ) ) { + + if ( _Watchdog_Is_active( &the_thread->Timer ) ) + (void) _Watchdog_Remove( &the_thread->Timer ); + } + + if ( the_thread->current_priority != the_thread->Start.initial_priority ) { + the_thread->real_priority = the_thread->Start.initial_priority; + _Thread_Set_priority( the_thread, the_thread->Start.initial_priority ); + } + + _Thread_Load_environment( the_thread ); + + _Thread_Ready( the_thread ); + + _User_extensions_Thread_restart( the_thread ); + + if ( _Thread_Is_executing ( the_thread ) ) + _Thread_Restart_self(); + + return TRUE; + } + + return FALSE; +} + +/* + * _Thread_Close + * + * DESCRIPTION: + * + * XXX + */ + +void _Thread_Close( + Objects_Information *information, + Thread_Control *the_thread +) +{ + _Objects_Close( information, &the_thread->Object ); + + _Thread_Set_state( the_thread, STATES_TRANSIENT ); + + if ( !_Thread_queue_Extract_with_proxy( the_thread ) ) { + + if ( _Watchdog_Is_active( &the_thread->Timer ) ) + (void) _Watchdog_Remove( &the_thread->Timer ); + } + + _User_extensions_Thread_delete( the_thread ); + +#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE ) + if ( _Thread_Is_allocated_fp( the_thread ) ) + _Thread_Deallocate_fp(); +#endif + the_thread->fp_context = NULL; + + if ( the_thread->Start.fp_context ) + (void) _Workspace_Free( the_thread->Start.fp_context ); + + _Thread_Stack_Free( the_thread ); + + if ( the_thread->extensions ) + (void) _Workspace_Free( the_thread->extensions ); + + the_thread->Start.stack = NULL; + the_thread->extensions = NULL; +} + +/*PAGE + * + * _Thread_Ready + * + * This kernel routine readies the requested thread, the thread chain + * is adjusted. A new heir thread may be selected. + * + * Input parameters: + * the_thread - pointer to thread control block + * + * Output parameters: NONE + * + * NOTE: This routine uses the "blocking" heir selection mechanism. + * This insures the correct heir after a thread restart. + * + * INTERRUPT LATENCY: + * ready chain + * select heir + */ + +void _Thread_Ready( + Thread_Control *the_thread +) +{ + ISR_Level level; + Thread_Control *heir; + + _ISR_Disable( level ); + + the_thread->current_state = STATES_READY; + + _Priority_Add_to_bit_map( &the_thread->Priority_map ); + + _Chain_Append_unprotected( the_thread->ready, &the_thread->Object.Node ); + + _ISR_Flash( level ); + + _Thread_Calculate_heir(); + + heir = _Thread_Heir; + + if ( !_Thread_Is_executing( heir ) && _Thread_Executing->is_preemptible ) + _Context_Switch_necessary = TRUE; + + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Clear_state + * + * This kernel routine clears the appropriate states in the + * requested thread. The thread ready chain is adjusted if + * necessary and the Heir thread is set accordingly. + * + * Input parameters: + * the_thread - pointer to thread control block + * state - state set to clear + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * priority map + * select heir + */ + + +void _Thread_Clear_state( + Thread_Control *the_thread, + States_Control state +) +{ + ISR_Level level; + States_Control current_state; + + _ISR_Disable( level ); + current_state = the_thread->current_state; + + if ( current_state & state ) { + current_state = + the_thread->current_state = _States_Clear( state, current_state ); + + if ( _States_Is_ready( current_state ) ) { + + _Priority_Add_to_bit_map( &the_thread->Priority_map ); + + _Chain_Append_unprotected(the_thread->ready, &the_thread->Object.Node); + + _ISR_Flash( level ); + + if ( the_thread->current_priority < _Thread_Heir->current_priority ) { + _Thread_Heir = the_thread; + if ( _Thread_Executing->is_preemptible || + the_thread->current_priority == 0 ) + _Context_Switch_necessary = TRUE; + } + } + } + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Set_state + * + * This kernel routine sets the requested state in the THREAD. The + * THREAD chain is adjusted if necessary. + * + * Input parameters: + * the_thread - pointer to thread control block + * state - state to be set + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * ready chain + * select map + */ + +void _Thread_Set_state( + Thread_Control *the_thread, + States_Control state +) +{ + ISR_Level level; + Chain_Control *ready; + + ready = the_thread->ready; + _ISR_Disable( level ); + if ( !_States_Is_ready( the_thread->current_state ) ) { + the_thread->current_state = + _States_Set( state, the_thread->current_state ); + _ISR_Enable( level ); + return; + } + + the_thread->current_state = state; + + if ( _Chain_Has_only_one_node( ready ) ) { + + _Chain_Initialize_empty( ready ); + _Priority_Remove_from_bit_map( &the_thread->Priority_map ); + + } else + _Chain_Extract_unprotected( &the_thread->Object.Node ); + + _ISR_Flash( level ); + + if ( _Thread_Is_heir( the_thread ) ) + _Thread_Calculate_heir(); + + if ( _Thread_Is_executing( the_thread ) ) + _Context_Switch_necessary = TRUE; + + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Set_transient + * + * This kernel routine places the requested thread in the transient state + * which will remove it from the ready queue, if necessary. No + * rescheduling is necessary because it is assumed that the transient + * state will be cleared before dispatching is enabled. + * + * Input parameters: + * the_thread - pointer to thread control block + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * only case + */ + +void _Thread_Set_transient( + Thread_Control *the_thread +) +{ + ISR_Level level; + unsigned32 old_state; + Chain_Control *ready; + + ready = the_thread->ready; + _ISR_Disable( level ); + + old_state = the_thread->current_state; + the_thread->current_state = _States_Set( STATES_TRANSIENT, old_state ); + + if ( _States_Is_ready( old_state ) ) { + if ( _Chain_Has_only_one_node( ready ) ) { + + _Chain_Initialize_empty( ready ); + _Priority_Remove_from_bit_map( &the_thread->Priority_map ); + + } else + _Chain_Extract_unprotected( &the_thread->Object.Node ); + } + + _ISR_Enable( level ); + +} + +/*PAGE + * + * _Thread_Reset_timeslice + * + * This routine will remove the running thread from the ready chain + * and place it immediately at the rear of this chain and then the + * timeslice counter is reset. The heir THREAD will be updated if + * the running is also the currently the heir. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * ready chain + * select heir + */ + +void _Thread_Reset_timeslice( void ) +{ + ISR_Level level; + Thread_Control *executing; + Chain_Control *ready; + + executing = _Thread_Executing; + ready = executing->ready; + _ISR_Disable( level ); + if ( _Chain_Has_only_one_node( ready ) ) { + _ISR_Enable( level ); + return; + } + _Chain_Extract_unprotected( &executing->Object.Node ); + _Chain_Append_unprotected( ready, &executing->Object.Node ); + + _ISR_Flash( level ); + + if ( _Thread_Is_heir( executing ) ) + _Thread_Heir = (Thread_Control *) ready->first; + + _Context_Switch_necessary = TRUE; + + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Tickle_timeslice + * + * This scheduler routine determines if timeslicing is enabled + * for the currently executing thread and, if so, updates the + * timeslice count and checks for timeslice expiration. + * + * Input parameters: NONE + * + * Output parameters: NONE + */ + +void _Thread_Tickle_timeslice( void ) +{ + Thread_Control *executing; + + executing = _Thread_Executing; + + /* + * Increment the number of ticks this thread has been executing + */ + + executing->ticks_executed++; + + /* + * If the thread is not preemptible or is not ready, then + * just return. + */ + + if ( !executing->is_preemptible ) + return; + + if ( !_States_Is_ready( executing->current_state ) ) + return; + + /* + * The cpu budget algorithm determines what happens next. + */ + + switch ( executing->budget_algorithm ) { + case THREAD_CPU_BUDGET_ALGORITHM_NONE: + break; + + case THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE: + case THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE: + if ( --executing->cpu_time_budget == 0 ) { + _Thread_Reset_timeslice(); + executing->cpu_time_budget = _Thread_Ticks_per_timeslice; + } + break; + + case THREAD_CPU_BUDGET_ALGORITHM_CALLOUT: + if ( --executing->cpu_time_budget == 0 ) + (*executing->budget_callout)( executing ); + break; + } +} + +/*PAGE + * + * _Thread_Yield_processor + * + * This kernel routine will remove the running THREAD from the ready chain + * and place it immediatly at the rear of this chain. Reset timeslice + * and yield the processor functions both use this routine, therefore if + * reset is TRUE and this is the only thread on the chain then the + * timeslice counter is reset. The heir THREAD will be updated if the + * running is also the currently the heir. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * ready chain + * select heir + */ + +void _Thread_Yield_processor( void ) +{ + ISR_Level level; + Thread_Control *executing; + Chain_Control *ready; + + executing = _Thread_Executing; + ready = executing->ready; + _ISR_Disable( level ); + if ( !_Chain_Has_only_one_node( ready ) ) { + _Chain_Extract_unprotected( &executing->Object.Node ); + _Chain_Append_unprotected( ready, &executing->Object.Node ); + + _ISR_Flash( level ); + + if ( _Thread_Is_heir( executing ) ) + _Thread_Heir = (Thread_Control *) ready->first; + _Context_Switch_necessary = TRUE; + } + else if ( !_Thread_Is_heir( executing ) ) + _Context_Switch_necessary = TRUE; + + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Load_environment + * + * Load starting environment for another thread from its start area in the + * thread. Only called from t_restart and t_start. + * + * Input parameters: + * the_thread - thread control block pointer + * + * Output parameters: NONE + */ + +void _Thread_Load_environment( + Thread_Control *the_thread +) +{ + boolean is_fp = FALSE; + + if ( the_thread->Start.fp_context ) { + the_thread->fp_context = the_thread->Start.fp_context; + _Context_Initialize_fp( &the_thread->fp_context ); + is_fp = TRUE; + } + + the_thread->do_post_task_switch_extension = FALSE; + the_thread->is_preemptible = the_thread->Start.is_preemptible; + the_thread->budget_algorithm = the_thread->Start.budget_algorithm; + the_thread->budget_callout = the_thread->Start.budget_callout; + + _Context_Initialize( + &the_thread->Registers, + the_thread->Start.Initial_stack.area, + the_thread->Start.Initial_stack.size, + the_thread->Start.isr_level, + _Thread_Handler, + is_fp + ); + +} + +/*PAGE + * + * _Thread_Handler + * + * This routine is the "primal" entry point for all threads. + * _Context_Initialize() dummies up the thread's initial context + * to cause the first Context_Switch() to jump to _Thread_Handler(). + * + * This routine is the default thread exitted error handler. It is + * returned to when a thread exits. The configured fatal error handler + * is invoked to process the exit. + * + * NOTE: + * + * On entry, it is assumed all interrupts are blocked and that this + * routine needs to set the initial isr level. This may or may not + * actually be needed by the context switch routine and as a result + * interrupts may already be at there proper level. Either way, + * setting the initial isr level properly here is safe. + * + * Currently this is only really needed for the posix port, + * ref: _Context_Switch in unix/cpu.c + * + * Input parameters: NONE + * + * Output parameters: NONE + */ + +void _Thread_Handler( void ) +{ + ISR_Level level; + Thread_Control *executing; + + executing = _Thread_Executing; + + /* + * have to put level into a register for those cpu's that use + * inline asm here + */ + + level = executing->Start.isr_level; + _ISR_Set_level(level); + + /* + * Take care that 'begin' extensions get to complete before + * 'switch' extensions can run. This means must keep dispatch + * disabled until all 'begin' extensions complete. + */ + + _User_extensions_Thread_begin( executing ); + + /* + * At this point, the dispatch disable level BETTER be 1. + */ + + _Thread_Enable_dispatch(); + + switch ( executing->Start.prototype ) { + case THREAD_START_NUMERIC: + (*(Thread_Entry_numeric) executing->Start.entry_point)( + executing->Start.numeric_argument + ); + break; + case THREAD_START_POINTER: + (*(Thread_Entry_pointer) executing->Start.entry_point)( + executing->Start.pointer_argument + ); + break; + case THREAD_START_BOTH_POINTER_FIRST: + (*(Thread_Entry_both_pointer_first) executing->Start.entry_point)( + executing->Start.pointer_argument, + executing->Start.numeric_argument + ); + break; + case THREAD_START_BOTH_NUMERIC_FIRST: + (*(Thread_Entry_both_numeric_first) executing->Start.entry_point)( + executing->Start.numeric_argument, + executing->Start.pointer_argument + ); + break; + } + + _User_extensions_Thread_exitted( executing ); + + _Internal_error_Occurred( + INTERNAL_ERROR_CORE, + TRUE, + INTERNAL_ERROR_THREAD_EXITTED + ); +} + +/*PAGE + * + * _Thread_Delay_ended + * + * This routine processes a thread whose delay period has ended. + * It is called by the watchdog handler. + * + * Input parameters: + * id - thread id + * + * Output parameters: NONE + */ + +void _Thread_Delay_ended( + Objects_Id id, + void *ignored +) +{ + Thread_Control *the_thread; + Objects_Locations location; + + the_thread = _Thread_Get( id, &location ); + switch ( location ) { + case OBJECTS_ERROR: + case OBJECTS_REMOTE: /* impossible */ + break; + case OBJECTS_LOCAL: + _Thread_Unblock( the_thread ); + _Thread_Unnest_dispatch(); + break; + } +} + +/*PAGE + * + * _Thread_Change_priority + * + * This kernel routine changes the priority of the thread. The + * thread chain is adjusted if necessary. + * + * Input parameters: + * the_thread - pointer to thread control block + * new_priority - ultimate priority + * prepend_it - TRUE if the thread should be prepended to the chain + * + * Output parameters: NONE + * + * INTERRUPT LATENCY: + * ready chain + * select heir + */ + +void _Thread_Change_priority( + Thread_Control *the_thread, + Priority_Control new_priority, + boolean prepend_it +) +{ + ISR_Level level; + /* boolean do_prepend = FALSE; */ + + /* + * If this is a case where prepending the task to its priority is + * potentially desired, then we need to consider whether to do it. + * This usually occurs when a task lowers its priority implcitly as + * the result of losing inherited priority. Normal explicit priority + * change calls (e.g. rtems_task_set_priority) should always do an + * append not a prepend. + */ + + /* + * Techically, the prepend should conditional on the thread lowering + * its priority but that does allow cxd2004 of the acvc 2.0.1 to + * pass with rtems 4.0.0. This should change when gnat redoes its + * priority scheme. + */ +/* + if ( prepend_it && + _Thread_Is_executing( the_thread ) && + new_priority >= the_thread->current_priority ) + prepend_it = TRUE; +*/ + + _Thread_Set_transient( the_thread ); + + if ( the_thread->current_priority != new_priority ) + _Thread_Set_priority( the_thread, new_priority ); + + _ISR_Disable( level ); + + the_thread->current_state = + _States_Clear( STATES_TRANSIENT, the_thread->current_state ); + + if ( ! _States_Is_ready( the_thread->current_state ) ) { + _ISR_Enable( level ); + return; + } + + _Priority_Add_to_bit_map( &the_thread->Priority_map ); + if ( prepend_it ) + _Chain_Prepend_unprotected( the_thread->ready, &the_thread->Object.Node ); + else + _Chain_Append_unprotected( the_thread->ready, &the_thread->Object.Node ); + + _ISR_Flash( level ); + + _Thread_Calculate_heir(); + + if ( !_Thread_Is_executing_also_the_heir() && + _Thread_Executing->is_preemptible ) + _Context_Switch_necessary = TRUE; + _ISR_Enable( level ); +} + +/*PAGE + * + * _Thread_Set_priority + * + * This directive enables and disables several modes of + * execution for the requesting thread. + * + * Input parameters: + * the_thread - pointer to thread priority + * new_priority - new priority + * + * Output: NONE + */ + +void _Thread_Set_priority( + Thread_Control *the_thread, + Priority_Control new_priority +) +{ + the_thread->current_priority = new_priority; + the_thread->ready = &_Thread_Ready_chain[ new_priority ]; + + _Priority_Initialize_information( &the_thread->Priority_map, new_priority ); +} + +/*PAGE + * + * _Thread_Evaluate_mode + * + * XXX + */ + +boolean _Thread_Evaluate_mode( void ) +{ + Thread_Control *executing; + + executing = _Thread_Executing; + + if ( !_States_Is_ready( executing->current_state ) || + ( !_Thread_Is_heir( executing ) && executing->is_preemptible ) ) { + _Context_Switch_necessary = TRUE; + return TRUE; + } + + return FALSE; +} + +/*PAGE + * + * _Thread_Get + * + * NOTE: If we are not using static inlines, this must be a real + * subroutine call. + * + * NOTE: XXX... This routine may be able to be optimized. + */ + +#ifndef USE_INLINES + +Thread_Control *_Thread_Get ( + Objects_Id id, + Objects_Locations *location +) +{ + Objects_Classes the_class; + Objects_Information *information; + + if ( _Objects_Are_ids_equal( id, OBJECTS_ID_OF_SELF ) ) { + _Thread_Disable_dispatch(); + *location = OBJECTS_LOCAL; + return( _Thread_Executing ); + } + + the_class = _Objects_Get_class( id ); + + if ( the_class > OBJECTS_CLASSES_LAST ) { + *location = OBJECTS_ERROR; + return (Thread_Control *) 0; + } + + information = _Objects_Information_table[ the_class ]; + + if ( !information || !information->is_thread ) { + *location = OBJECTS_ERROR; + return (Thread_Control *) 0; + } + + return (Thread_Control *) _Objects_Get( information, id, location ); +} + +#endif + +/*PAGE + * + * _Thread_Idle_body + * + * This kernel routine is the idle thread. The idle thread runs any time + * no other thread is ready to run. This thread loops forever with + * interrupts enabled. + * + * Input parameters: + * ignored - this parameter is ignored + * + * Output parameters: NONE + */ + +#if (CPU_PROVIDES_IDLE_THREAD_BODY == FALSE) +Thread _Thread_Idle_body( + unsigned32 ignored +) +{ + for( ; ; ) ; +} +#endif |