summaryrefslogtreecommitdiff
path: root/include/rtems/score/threadimpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/rtems/score/threadimpl.h')
-rw-r--r--include/rtems/score/threadimpl.h1505
1 files changed, 1505 insertions, 0 deletions
diff --git a/include/rtems/score/threadimpl.h b/include/rtems/score/threadimpl.h
new file mode 100644
index 0000000000..cf32082340
--- /dev/null
+++ b/include/rtems/score/threadimpl.h
@@ -0,0 +1,1505 @@
+/**
+ * @file
+ *
+ * @brief Inlined Routines from the Thread Handler
+ *
+ * This file contains the macro implementation of the inlined
+ * routines from the Thread handler.
+ */
+
+/*
+ * COPYRIGHT (c) 1989-2008.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * Copyright (c) 2014-2015 embedded brains GmbH.
+ *
+ * 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.
+ */
+
+#ifndef _RTEMS_SCORE_THREADIMPL_H
+#define _RTEMS_SCORE_THREADIMPL_H
+
+#include <rtems/score/thread.h>
+#include <rtems/score/assert.h>
+#include <rtems/score/chainimpl.h>
+#include <rtems/score/interr.h>
+#include <rtems/score/isr.h>
+#include <rtems/score/objectimpl.h>
+#include <rtems/score/resourceimpl.h>
+#include <rtems/score/statesimpl.h>
+#include <rtems/score/sysstate.h>
+#include <rtems/score/threadqimpl.h>
+#include <rtems/score/todimpl.h>
+#include <rtems/score/freechain.h>
+#include <rtems/config.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup ScoreThread
+ */
+/**@{**/
+
+/**
+ * The following structure contains the information necessary to manage
+ * a thread which it is waiting for a resource.
+ */
+#define THREAD_STATUS_PROXY_BLOCKING 0x1111111
+
+/**
+ * Self for the GNU Ada Run-Time
+ */
+SCORE_EXTERN void *rtems_ada_self;
+
+typedef struct {
+ Objects_Information Objects;
+
+ Freechain_Control Free_thread_queue_heads;
+} Thread_Information;
+
+/**
+ * The following defines the information control block used to
+ * manage this class of objects.
+ */
+SCORE_EXTERN Thread_Information _Thread_Internal_information;
+
+/**
+ * The following points to the thread whose floating point
+ * context is currently loaded.
+ */
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+SCORE_EXTERN Thread_Control *_Thread_Allocated_fp;
+#endif
+
+#define THREAD_CHAIN_NODE_TO_THREAD( node ) \
+ RTEMS_CONTAINER_OF( node, Thread_Control, Wait.Node.Chain )
+
+#define THREAD_RBTREE_NODE_TO_THREAD( node ) \
+ RTEMS_CONTAINER_OF( node, Thread_Control, Wait.Node.RBTree )
+
+#if defined(RTEMS_SMP)
+#define THREAD_RESOURCE_NODE_TO_THREAD( node ) \
+ RTEMS_CONTAINER_OF( node, Thread_Control, Resource_node )
+#endif
+
+void _Thread_Initialize_information(
+ Thread_Information *information,
+ Objects_APIs the_api,
+ uint16_t the_class,
+ uint32_t maximum,
+ bool is_string,
+ uint32_t maximum_name_length
+#if defined(RTEMS_MULTIPROCESSING)
+ ,
+ bool supports_global
+#endif
+);
+
+/**
+ * @brief Initialize thread handler.
+ *
+ * This routine performs the initialization necessary for this handler.
+ */
+void _Thread_Handler_initialization(void);
+
+/**
+ * @brief Create idle thread.
+ *
+ * This routine creates the idle thread.
+ *
+ * @warning No thread should be created before this one.
+ */
+void _Thread_Create_idle(void);
+
+/**
+ * @brief Start thread multitasking.
+ *
+ * This routine initiates multitasking. It is invoked only as
+ * part of initialization and its invocation is the last act of
+ * the non-multitasking part of the system initialization.
+ */
+void _Thread_Start_multitasking( void ) RTEMS_NO_RETURN;
+
+/**
+ * @brief Allocate the requested stack space for the thread.
+ *
+ * Allocate the requested stack space for the thread.
+ * Set the Start.stack field to the address of the stack.
+ *
+ * @param[in] the_thread is the thread where the stack space is requested
+ * @param[in] stack_size is the stack space is requested
+ *
+ * @retval actual size allocated after any adjustment
+ * @retval zero if the allocation failed
+ */
+size_t _Thread_Stack_Allocate(
+ Thread_Control *the_thread,
+ size_t stack_size
+);
+
+/**
+ * @brief Deallocate thread stack.
+ *
+ * Deallocate the Thread's stack.
+ */
+void _Thread_Stack_Free(
+ Thread_Control *the_thread
+);
+
+/**
+ * @brief Initialize thread.
+ *
+ * This routine initializes the specified the thread. It allocates
+ * all memory associated with this thread. It completes by adding
+ * the thread to the local object table so operations on this
+ * thread id are allowed.
+ *
+ * @note If stack_area is NULL, it is allocated from the workspace.
+ *
+ * @note If the stack is allocated from the workspace, then it is
+ * guaranteed to be of at least minimum size.
+ */
+bool _Thread_Initialize(
+ Thread_Information *information,
+ Thread_Control *the_thread,
+ const struct Scheduler_Control *scheduler,
+ void *stack_area,
+ size_t stack_size,
+ bool is_fp,
+ Priority_Control priority,
+ bool is_preemptible,
+ Thread_CPU_budget_algorithms budget_algorithm,
+ Thread_CPU_budget_algorithm_callout budget_callout,
+ uint32_t isr_level,
+ Objects_Name name
+);
+
+/**
+ * @brief Initializes thread and executes it.
+ *
+ * This routine initializes the executable information for a thread
+ * and makes it ready to execute. After this routine executes, the
+ * thread competes with all other threads for CPU time.
+ *
+ * @param the_thread is the thread to be initialized
+ * @param the_prototype
+ * @param entry_point
+ * @param pointer_argument
+ * @param numeric_argument
+ * @param[in,out] cpu The processor if used to start an idle thread
+ * during system initialization. Must be set to @c NULL to start a normal
+ * thread.
+ */
+bool _Thread_Start(
+ Thread_Control *the_thread,
+ Thread_Start_types the_prototype,
+ void *entry_point,
+ void *pointer_argument,
+ Thread_Entry_numeric_type numeric_argument,
+ Per_CPU_Control *cpu
+);
+
+bool _Thread_Restart(
+ Thread_Control *the_thread,
+ Thread_Control *executing,
+ void *pointer_argument,
+ Thread_Entry_numeric_type numeric_argument
+);
+
+void _Thread_Yield( Thread_Control *executing );
+
+bool _Thread_Set_life_protection( bool protect );
+
+void _Thread_Life_action_handler(
+ Thread_Control *executing,
+ Thread_Action *action,
+ Per_CPU_Control *cpu,
+ ISR_Level level
+);
+
+/**
+ * @brief Kills all zombie threads in the system.
+ *
+ * Threads change into the zombie state as the last step in the thread
+ * termination sequence right before a context switch to the heir thread is
+ * initiated. Since the thread stack is still in use during this phase we have
+ * to postpone the thread stack reclamation until this point. On SMP
+ * configurations we may have to busy wait for context switch completion here.
+ */
+void _Thread_Kill_zombies( void );
+
+/**
+ * @brief Closes the thread.
+ *
+ * Closes the thread object and starts the thread termination sequence. In
+ * case the executing thread is not terminated, then this function waits until
+ * the terminating thread reached the zombie state.
+ */
+void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing );
+
+/**
+ * @brief Clears the specified thread state.
+ *
+ * In case the previous state is a non-ready state and the next state is the
+ * ready state, then the thread is unblocked by the scheduler.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] state The state to clear. It must not be zero.
+ *
+ * @return The previous state.
+ */
+States_Control _Thread_Clear_state(
+ Thread_Control *the_thread,
+ States_Control state
+);
+
+/**
+ * @brief Sets the specified thread state.
+ *
+ * In case the previous state is the ready state, then the thread is blocked by
+ * the scheduler.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] state The state to set. It must not be zero.
+ *
+ * @return The previous state.
+ */
+States_Control _Thread_Set_state(
+ Thread_Control *the_thread,
+ States_Control state
+);
+
+/**
+ * @brief Clears all thread states.
+ *
+ * In case the previous state is a non-ready state, then the thread is
+ * unblocked by the scheduler.
+ *
+ * @param[in] the_thread The thread.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Ready(
+ Thread_Control *the_thread
+)
+{
+ _Thread_Clear_state( the_thread, STATES_ALL_SET );
+}
+
+/**
+ * @brief Initializes enviroment for a thread.
+ *
+ * This routine initializes the context of @a the_thread to its
+ * appropriate starting state.
+ *
+ * @param[in] the_thread is the pointer to the thread control block.
+ */
+void _Thread_Load_environment(
+ Thread_Control *the_thread
+);
+
+/**
+ * @brief Wrapper function for all threads.
+ *
+ * This routine is the wrapper function for all threads. It is
+ * the starting point for all threads. The user provided thread
+ * entry point is invoked by this routine. Operations
+ * which must be performed immediately before and after the user's
+ * thread executes are found here.
+ *
+ * @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.
+ */
+void _Thread_Handler( void );
+
+/**
+ * @brief Executes the global constructors and then restarts itself as the
+ * first initialization thread.
+ *
+ * The first initialization thread is the first RTEMS initialization task or
+ * the first POSIX initialization thread in case no RTEMS initialization tasks
+ * are present.
+ */
+void *_Thread_Global_construction( void );
+
+/**
+ * @brief Ended the delay of a thread.
+ *
+ * This routine is invoked when a thread must be unblocked at the
+ * end of a time based delay (i.e. wake after or wake when).
+ * It is called by the watchdog handler.
+ *
+ * @param[in] id is the thread id
+ * @param[in] ignored is not used
+ */
+void _Thread_Delay_ended(
+ Objects_Id id,
+ void *ignored
+);
+
+/**
+ * @brief Returns true if the left thread priority is less than the right
+ * thread priority in the intuitive sense of priority and false otherwise.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Priority_less_than(
+ Priority_Control left,
+ Priority_Control right
+)
+{
+ return left > right;
+}
+
+/**
+ * @brief Returns the highest priority of the left and right thread priorities
+ * in the intuitive sense of priority.
+ */
+RTEMS_INLINE_ROUTINE Priority_Control _Thread_Priority_highest(
+ Priority_Control left,
+ Priority_Control right
+)
+{
+ return _Thread_Priority_less_than( left, right ) ? right : left;
+}
+
+/**
+ * @brief Filters a thread priority change.
+ *
+ * Called by _Thread_Change_priority() under the protection of the thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in, out] new_priority The new priority of the thread. The filter may
+ * alter this value.
+ * @param[in] arg The argument passed to _Thread_Change_priority().
+ *
+ * @retval true Change the current priority.
+ * @retval false Otherwise.
+ */
+typedef bool ( *Thread_Change_priority_filter )(
+ Thread_Control *the_thread,
+ Priority_Control *new_priority,
+ void *arg
+);
+
+/**
+ * @brief Changes the priority of a thread if allowed by the filter function.
+ *
+ * It changes current priority of the thread to the new priority in case the
+ * filter function returns true. In this case the scheduler is notified of the
+ * priority change as well.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_priority The new priority of the thread.
+ * @param[in] arg The argument for the filter function.
+ * @param[in] filter The filter function to determine if a priority change is
+ * allowed and optionally perform other actions under the protection of the
+ * thread lock simultaneously with the update of the current priority.
+ * @param[in] prepend_it In case this is true, then the thread is prepended to
+ * its priority group in its scheduler instance, otherwise it is appended.
+ */
+void _Thread_Change_priority(
+ Thread_Control *the_thread,
+ Priority_Control new_priority,
+ void *arg,
+ Thread_Change_priority_filter filter,
+ bool prepend_it
+);
+
+/**
+ * @brief Raises the priority of a thread.
+ *
+ * It changes the current priority of the thread to the new priority if the new
+ * priority is higher than the current priority. In this case the thread is
+ * appended to its new priority group in its scheduler instance.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_priority The new priority of the thread.
+ *
+ * @see _Thread_Change_priority().
+ */
+void _Thread_Raise_priority(
+ Thread_Control *the_thread,
+ Priority_Control new_priority
+);
+
+/**
+ * @brief Inherit the priority of a thread.
+ *
+ * It changes the current priority of the inheritor thread to the current priority
+ * of the ancestor thread if it is higher than the current priority of the inheritor
+ * thread. In this case the inheritor thread is appended to its new priority group
+ * in its scheduler instance.
+ *
+ * On SMP configurations, the priority is changed to PRIORITY_PSEUDO_ISR in
+ * case the own schedulers of the inheritor and ancestor thread differ (priority
+ * boosting).
+ *
+ * @param[in] inheritor The thread to inherit the priority.
+ * @param[in] ancestor The thread to bequeath its priority to the inheritor
+ * thread.
+ */
+#if defined(RTEMS_SMP)
+void _Thread_Inherit_priority(
+ Thread_Control *inheritor,
+ Thread_Control *ancestor
+);
+#else
+RTEMS_INLINE_ROUTINE void _Thread_Inherit_priority(
+ Thread_Control *inheritor,
+ Thread_Control *ancestor
+)
+{
+ _Thread_Raise_priority( inheritor, ancestor->current_priority );
+}
+#endif
+
+/**
+ * @brief Sets the current to the real priority of a thread.
+ *
+ * Sets the priority restore hint to false.
+ */
+void _Thread_Restore_priority( Thread_Control *the_thread );
+
+/**
+ * @brief Sets the priority of a thread.
+ *
+ * It sets the real priority of the thread. In addition it changes the current
+ * priority of the thread if the new priority is higher than the current
+ * priority or the thread owns no resources.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_priority The new priority of the thread.
+ * @param[out] old_priority The old real priority of the thread. This pointer
+ * must not be @c NULL.
+ * @param[in] prepend_it In case this is true, then the thread is prepended to
+ * its priority group in its scheduler instance, otherwise it is appended.
+ *
+ * @see _Thread_Change_priority().
+ */
+void _Thread_Set_priority(
+ Thread_Control *the_thread,
+ Priority_Control new_priority,
+ Priority_Control *old_priority,
+ bool prepend_it
+);
+
+/**
+ * @brief Maps thread Id to a TCB pointer.
+ *
+ * This function maps thread IDs to thread control
+ * blocks. If ID corresponds to a local thread, then it
+ * returns the_thread control pointer which maps to ID
+ * and @a location is set to OBJECTS_LOCAL. If the thread ID is
+ * global and resides on a remote node, then location is set
+ * to OBJECTS_REMOTE, and the_thread is undefined.
+ * Otherwise, location is set to OBJECTS_ERROR and
+ * the_thread is undefined.
+ *
+ * @param[in] id is the id of the thread.
+ * @param[in] location is the location of the block.
+ *
+ * @note The performance of many RTEMS services depends upon
+ * the quick execution of the "good object" path in this
+ * routine. If there is a possibility of saving a few
+ * cycles off the execution time, this routine is worth
+ * further optimization attention.
+ */
+Thread_Control *_Thread_Get (
+ Objects_Id id,
+ Objects_Locations *location
+);
+
+/**
+ * @brief Gets a thread by its identifier.
+ *
+ * @see _Objects_Get_isr_disable().
+ */
+Thread_Control *_Thread_Get_interrupt_disable(
+ Objects_Id id,
+ Objects_Locations *location,
+ ISR_lock_Context *lock_context
+);
+
+RTEMS_INLINE_ROUTINE Per_CPU_Control *_Thread_Get_CPU(
+ const Thread_Control *thread
+)
+{
+#if defined(RTEMS_SMP)
+ return thread->Scheduler.cpu;
+#else
+ (void) thread;
+
+ return _Per_CPU_Get();
+#endif
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Set_CPU(
+ Thread_Control *thread,
+ Per_CPU_Control *cpu
+)
+{
+#if defined(RTEMS_SMP)
+ thread->Scheduler.cpu = cpu;
+#else
+ (void) thread;
+ (void) cpu;
+#endif
+}
+
+/**
+ * This function returns true if the_thread is the currently executing
+ * thread, and false otherwise.
+ */
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_executing (
+ const Thread_Control *the_thread
+)
+{
+ return ( the_thread == _Thread_Executing );
+}
+
+#if defined(RTEMS_SMP)
+/**
+ * @brief Returns @a true in case the thread executes currently on some
+ * processor in the system, otherwise @a false.
+ *
+ * Do not confuse this with _Thread_Is_executing() which checks only the
+ * current processor.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Is_executing_on_a_processor(
+ const Thread_Control *the_thread
+)
+{
+ return _CPU_Context_Get_is_executing( &the_thread->Registers );
+}
+#endif
+
+/**
+ * @brief Returns @a true and sets time_of_context_switch to the
+ * time of the last context switch when the thread is currently executing
+ * in the system, otherwise @a false.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Get_time_of_last_context_switch(
+ Thread_Control *the_thread,
+ Timestamp_Control *time_of_context_switch
+)
+{
+ bool retval = false;
+
+ _Thread_Disable_dispatch();
+ #ifndef RTEMS_SMP
+ if ( _Thread_Executing->Object.id == the_thread->Object.id ) {
+ *time_of_context_switch = _Thread_Time_of_last_context_switch;
+ retval = true;
+ }
+ #else
+ if ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
+ *time_of_context_switch =
+ _Thread_Get_CPU( the_thread )->time_of_last_context_switch;
+ retval = true;
+ }
+ #endif
+ _Thread_Enable_dispatch();
+ return retval;
+}
+
+
+/**
+ * This function returns true if the_thread is the heir
+ * thread, and false otherwise.
+ */
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_heir (
+ const Thread_Control *the_thread
+)
+{
+ return ( the_thread == _Thread_Heir );
+}
+
+/**
+ * This routine clears any blocking state for the_thread. It performs
+ * any necessary scheduling operations including the selection of
+ * a new heir thread.
+ */
+
+RTEMS_INLINE_ROUTINE void _Thread_Unblock (
+ Thread_Control *the_thread
+)
+{
+ _Thread_Clear_state( the_thread, STATES_BLOCKED );
+}
+
+/**
+ * This routine resets the current context of the calling thread
+ * to that of its initial state.
+ */
+
+RTEMS_INLINE_ROUTINE void _Thread_Restart_self( Thread_Control *executing )
+{
+#if defined(RTEMS_SMP)
+ ISR_Level level;
+
+ _Giant_Release( _Per_CPU_Get() );
+
+ _ISR_Disable_without_giant( level );
+ ( void ) level;
+#endif
+
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+ if ( executing->fp_context != NULL )
+ _Context_Restore_fp( &executing->fp_context );
+#endif
+
+ _CPU_Context_Restart_self( &executing->Registers );
+}
+
+/**
+ * This function returns true if the floating point context of
+ * the_thread is currently loaded in the floating point unit, and
+ * false otherwise.
+ */
+
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+RTEMS_INLINE_ROUTINE bool _Thread_Is_allocated_fp (
+ const Thread_Control *the_thread
+)
+{
+ return ( the_thread == _Thread_Allocated_fp );
+}
+#endif
+
+/*
+ * 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.
+ */
+
+RTEMS_INLINE_ROUTINE void _Thread_Save_fp( Thread_Control *executing )
+{
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+#if ( CPU_USE_DEFERRED_FP_SWITCH != TRUE )
+ if ( executing->fp_context != NULL )
+ _Context_Save_fp( &executing->fp_context );
+#endif
+#endif
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Restore_fp( Thread_Control *executing )
+{
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE )
+ if ( (executing->fp_context != NULL) &&
+ !_Thread_Is_allocated_fp( executing ) ) {
+ if ( _Thread_Allocated_fp != NULL )
+ _Context_Save_fp( &_Thread_Allocated_fp->fp_context );
+ _Context_Restore_fp( &executing->fp_context );
+ _Thread_Allocated_fp = executing;
+ }
+#else
+ if ( executing->fp_context != NULL )
+ _Context_Restore_fp( &executing->fp_context );
+#endif
+#endif
+}
+
+/**
+ * This routine is invoked when the currently loaded floating
+ * point context is now longer associated with an active thread.
+ */
+
+#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
+RTEMS_INLINE_ROUTINE void _Thread_Deallocate_fp( void )
+{
+ _Thread_Allocated_fp = NULL;
+}
+#endif
+
+/**
+ * This function returns true if dispatching is disabled, and false
+ * otherwise.
+ */
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_context_switch_necessary( void )
+{
+ return ( _Thread_Dispatch_necessary );
+}
+
+/**
+ * This function returns true if the_thread is NULL and false otherwise.
+ */
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_null (
+ const Thread_Control *the_thread
+)
+{
+ return ( the_thread == NULL );
+}
+
+/**
+ * @brief Is proxy blocking.
+ *
+ * status which indicates that a proxy is blocking, and false otherwise.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Is_proxy_blocking (
+ uint32_t code
+)
+{
+ return (code == THREAD_STATUS_PROXY_BLOCKING);
+}
+
+RTEMS_INLINE_ROUTINE uint32_t _Thread_Get_maximum_internal_threads(void)
+{
+ /* Idle threads */
+ uint32_t maximum_internal_threads =
+ rtems_configuration_get_maximum_processors();
+
+ /* MPCI thread */
+#if defined(RTEMS_MULTIPROCESSING)
+ if ( _System_state_Is_multiprocessing ) {
+ ++maximum_internal_threads;
+ }
+#endif
+
+ return maximum_internal_threads;
+}
+
+RTEMS_INLINE_ROUTINE Thread_Control *_Thread_Internal_allocate( void )
+{
+ return (Thread_Control *)
+ _Objects_Allocate_unprotected( &_Thread_Internal_information.Objects );
+}
+
+/**
+ * @brief Gets the heir of the processor and makes it executing.
+ *
+ * Must be called with interrupts disabled. The thread dispatch necessary
+ * indicator is cleared as a side-effect.
+ *
+ * @return The heir thread.
+ *
+ * @see _Thread_Dispatch(), _Thread_Start_multitasking() and
+ * _Thread_Dispatch_update_heir().
+ */
+RTEMS_INLINE_ROUTINE Thread_Control *_Thread_Get_heir_and_make_it_executing(
+ Per_CPU_Control *cpu_self
+)
+{
+ Thread_Control *heir;
+
+ heir = cpu_self->heir;
+ cpu_self->dispatch_necessary = false;
+ cpu_self->executing = heir;
+
+ return heir;
+}
+
+#if defined( RTEMS_SMP )
+RTEMS_INLINE_ROUTINE void _Thread_Dispatch_update_heir(
+ Per_CPU_Control *cpu_self,
+ Per_CPU_Control *cpu_for_heir,
+ Thread_Control *heir
+)
+{
+ cpu_for_heir->heir = heir;
+
+ if ( cpu_for_heir == cpu_self ) {
+ cpu_self->dispatch_necessary = true;
+ } else {
+ _Per_CPU_Send_interrupt( cpu_for_heir );
+ }
+}
+#endif
+
+RTEMS_INLINE_ROUTINE void _Thread_Update_cpu_time_used(
+ Thread_Control *executing,
+ Timestamp_Control *time_of_last_context_switch
+)
+{
+ Timestamp_Control uptime;
+ Timestamp_Control ran;
+
+ _TOD_Get_uptime( &uptime );
+ _Timestamp_Subtract(
+ time_of_last_context_switch,
+ &uptime,
+ &ran
+ );
+ *time_of_last_context_switch = uptime;
+ _Timestamp_Add_to( &executing->cpu_time_used, &ran );
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Action_control_initialize(
+ Thread_Action_control *action_control
+)
+{
+ _Chain_Initialize_empty( &action_control->Chain );
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Action_initialize(
+ Thread_Action *action,
+ Thread_Action_handler handler
+)
+{
+ action->handler = handler;
+ _Chain_Set_off_chain( &action->Node );
+}
+
+RTEMS_INLINE_ROUTINE Per_CPU_Control *
+ _Thread_Action_ISR_disable_and_acquire_for_executing( ISR_Level *level )
+{
+ Per_CPU_Control *cpu;
+
+ _ISR_Disable_without_giant( *level );
+ cpu = _Per_CPU_Get();
+ _Per_CPU_Acquire( cpu );
+
+ return cpu;
+}
+
+RTEMS_INLINE_ROUTINE Per_CPU_Control *_Thread_Action_ISR_disable_and_acquire(
+ Thread_Control *thread,
+ ISR_Level *level
+)
+{
+ Per_CPU_Control *cpu;
+
+ _ISR_Disable_without_giant( *level );
+ cpu = _Thread_Get_CPU( thread );
+ _Per_CPU_Acquire( cpu );
+
+ return cpu;
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Action_release_and_ISR_enable(
+ Per_CPU_Control *cpu,
+ ISR_Level level
+)
+{
+ _Per_CPU_Release_and_ISR_enable( cpu, level );
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Add_post_switch_action(
+ Thread_Control *thread,
+ Thread_Action *action
+)
+{
+ Per_CPU_Control *cpu_of_thread;
+ ISR_Level level;
+
+ cpu_of_thread = _Thread_Action_ISR_disable_and_acquire( thread, &level );
+
+#if defined(RTEMS_SMP)
+ if ( _Per_CPU_Get() == cpu_of_thread ) {
+ cpu_of_thread->dispatch_necessary = true;
+ } else {
+ _Per_CPU_Send_interrupt( cpu_of_thread );
+ }
+#else
+ cpu_of_thread->dispatch_necessary = true;
+#endif
+
+ _Chain_Append_if_is_off_chain_unprotected(
+ &thread->Post_switch_actions.Chain,
+ &action->Node
+ );
+
+ _Thread_Action_release_and_ISR_enable( cpu_of_thread, level );
+}
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_life_restarting(
+ Thread_Life_state life_state
+)
+{
+ return ( life_state & THREAD_LIFE_RESTARTING ) != 0;
+}
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_life_terminating(
+ Thread_Life_state life_state
+)
+{
+ return ( life_state & THREAD_LIFE_TERMINATING ) != 0;
+}
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_life_protected(
+ Thread_Life_state life_state
+)
+{
+ return ( life_state & THREAD_LIFE_PROTECTED ) != 0;
+}
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_life_changing(
+ Thread_Life_state life_state
+)
+{
+ return ( life_state & THREAD_LIFE_RESTARTING_TERMINATING ) != 0;
+}
+
+/**
+ * @brief Returns true if the thread owns resources, and false otherwise.
+ *
+ * Resources are accounted with the Thread_Control::resource_count resource
+ * counter. This counter is used by semaphore objects for example.
+ *
+ * In addition to the resource counter there is a resource dependency tree
+ * available on SMP configurations. In case this tree is non-empty, then the
+ * thread owns resources.
+ *
+ * @param[in] the_thread The thread.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Owns_resources(
+ const Thread_Control *the_thread
+)
+{
+ bool owns_resources = the_thread->resource_count != 0;
+
+#if defined(RTEMS_SMP)
+ owns_resources = owns_resources
+ || _Resource_Node_owns_resources( &the_thread->Resource_node );
+#endif
+
+ return owns_resources;
+}
+
+/**
+ * @brief Acquires the default thread lock inside a critical section
+ * (interrupts disabled).
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * release.
+ *
+ * @see _Thread_Lock_release_default().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_acquire_default_critical(
+ Thread_Control *the_thread,
+ ISR_lock_Context *lock_context
+)
+{
+ _Assert( _ISR_Get_level() != 0 );
+#if defined(RTEMS_SMP)
+ _SMP_ticket_lock_Acquire(
+ &the_thread->Lock.Default,
+ &_Thread_Executing->Lock.Stats,
+ &lock_context->Lock_context.Stats_context
+ );
+#else
+ (void) the_thread;
+ (void) lock_context;
+#endif
+}
+
+/**
+ * @brief Acquires the default thread lock and returns the executing thread.
+ *
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * release.
+ *
+ * @return The executing thread.
+ *
+ * @see _Thread_Lock_release_default().
+ */
+RTEMS_INLINE_ROUTINE Thread_Control *_Thread_Lock_acquire_default_for_executing(
+ ISR_lock_Context *lock_context
+)
+{
+ Thread_Control *executing;
+
+ _ISR_lock_ISR_disable( lock_context );
+ executing = _Thread_Executing;
+ _Thread_Lock_acquire_default_critical( executing, lock_context );
+
+ return executing;
+}
+
+/**
+ * @brief Acquires the default thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * release.
+ *
+ * @see _Thread_Lock_release_default().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_acquire_default(
+ Thread_Control *the_thread,
+ ISR_lock_Context *lock_context
+)
+{
+ _ISR_lock_ISR_disable( lock_context );
+ _Thread_Lock_acquire_default_critical( the_thread, lock_context );
+}
+
+/**
+ * @brief Releases the thread lock inside a critical section (interrupts
+ * disabled).
+ *
+ * The previous interrupt status is not restored.
+ *
+ * @param[in] lock The lock.
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * acquire.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_release_critical(
+ void *lock,
+ ISR_lock_Context *lock_context
+)
+{
+#if defined(RTEMS_SMP)
+ _SMP_ticket_lock_Release(
+ lock,
+ &lock_context->Lock_context.Stats_context
+ );
+#else
+ (void) lock;
+ (void) lock_context;
+#endif
+}
+
+/**
+ * @brief Releases the thread lock.
+ *
+ * @param[in] lock The lock returned by _Thread_Lock_acquire().
+ * @param[in] lock_context The lock context used for _Thread_Lock_acquire().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_release(
+ void *lock,
+ ISR_lock_Context *lock_context
+)
+{
+ _Thread_Lock_release_critical( lock, lock_context );
+ _ISR_lock_ISR_enable( lock_context );
+}
+
+/**
+ * @brief Releases the default thread lock inside a critical section
+ * (interrupts disabled).
+ *
+ * The previous interrupt status is not restored.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * acquire.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_release_default_critical(
+ Thread_Control *the_thread,
+ ISR_lock_Context *lock_context
+)
+{
+ _Thread_Lock_release_critical(
+#if defined(RTEMS_SMP)
+ &the_thread->Lock.Default,
+#else
+ NULL,
+#endif
+ lock_context
+ );
+}
+
+/**
+ * @brief Releases the default thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] lock_context The lock context used for the corresponding lock
+ * acquire.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_release_default(
+ Thread_Control *the_thread,
+ ISR_lock_Context *lock_context
+)
+{
+ _Thread_Lock_release_default_critical( the_thread, lock_context );
+ _ISR_lock_ISR_enable( lock_context );
+}
+
+/**
+ * @brief Acquires the thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] lock_context The lock context for _Thread_Lock_release().
+ *
+ * @return The lock required by _Thread_Lock_release().
+ */
+RTEMS_INLINE_ROUTINE void *_Thread_Lock_acquire(
+ Thread_Control *the_thread,
+ ISR_lock_Context *lock_context
+)
+{
+#if defined(RTEMS_SMP)
+ SMP_ticket_lock_Control *lock;
+
+ while ( true ) {
+ unsigned int first_generation;
+ unsigned int second_generation;
+
+ _ISR_lock_ISR_disable( lock_context );
+
+ /*
+ * Ensure that we read our first lock generation before we obtain our
+ * current lock. See _Thread_Lock_set_unprotected().
+ */
+ first_generation = _Atomic_Load_uint(
+ &the_thread->Lock.generation,
+ ATOMIC_ORDER_ACQUIRE
+ );
+
+ lock = the_thread->Lock.current;
+ _SMP_ticket_lock_Acquire(
+ lock,
+ &_Thread_Executing->Lock.Stats,
+ &lock_context->Lock_context.Stats_context
+ );
+
+ /*
+ * The C11 memory model doesn't guarantee that we read the latest
+ * generation here. For this a read-modify-write operation would be
+ * necessary. We read at least the new generation set up by the owner of
+ * our current thread lock, and so on.
+ */
+ second_generation = _Atomic_Load_uint(
+ &the_thread->Lock.generation,
+ ATOMIC_ORDER_ACQUIRE
+ );
+
+ if ( first_generation == second_generation ) {
+ return lock;
+ }
+
+ _Thread_Lock_release( lock, lock_context );
+ }
+#else
+ _ISR_Disable( lock_context->isr_level );
+
+ return NULL;
+#endif
+}
+
+#if defined(RTEMS_SMP)
+/*
+ * Internal function, use _Thread_Lock_set() or _Thread_Lock_restore_default()
+ * instead.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Lock_set_unprotected(
+ Thread_Control *the_thread,
+ SMP_ticket_lock_Control *new_lock
+)
+{
+ the_thread->Lock.current = new_lock;
+
+ /*
+ * The generation release corresponds to the generation acquire in
+ * _Thread_Lock_acquire() and ensures that the new lock and other fields are
+ * visible to the next thread lock owner. Otherwise someone would be able to
+ * read an up to date generation number and an old lock. See
+ * _Thread_Wait_set_queue() and _Thread_Wait_restore_default_operations().
+ *
+ * Since we set a new lock right before, this increment is not protected by a
+ * lock and thus must be an atomic operation.
+ */
+ _Atomic_Fetch_add_uint(
+ &the_thread->Lock.generation,
+ 1,
+ ATOMIC_ORDER_RELEASE
+ );
+}
+#endif
+
+/**
+ * @brief Sets a new thread lock.
+ *
+ * The caller must not be the owner of the default thread lock. The caller
+ * must be the owner of the new lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_lock The new thread lock.
+ */
+#if defined(RTEMS_SMP)
+RTEMS_INLINE_ROUTINE void _Thread_Lock_set(
+ Thread_Control *the_thread,
+ SMP_ticket_lock_Control *new_lock
+)
+{
+ ISR_lock_Context lock_context;
+
+ _Thread_Lock_acquire_default_critical( the_thread, &lock_context );
+ _Assert( the_thread->Lock.current == &the_thread->Lock.Default );
+ _Thread_Lock_set_unprotected( the_thread, new_lock );
+ _Thread_Lock_release_default_critical( the_thread, &lock_context );
+}
+#else
+#define _Thread_Lock_set( the_thread, new_lock ) \
+ do { } while ( 0 )
+#endif
+
+/**
+ * @brief Restores the default thread lock.
+ *
+ * The caller must be the owner of the current thread lock.
+ *
+ * @param[in] the_thread The thread.
+ */
+#if defined(RTEMS_SMP)
+RTEMS_INLINE_ROUTINE void _Thread_Lock_restore_default(
+ Thread_Control *the_thread
+)
+{
+ _Thread_Lock_set_unprotected( the_thread, &the_thread->Lock.Default );
+}
+#else
+#define _Thread_Lock_restore_default( the_thread ) \
+ do { } while ( 0 )
+#endif
+
+/**
+ * @brief The initial thread wait flags value set by _Thread_Initialize().
+ */
+#define THREAD_WAIT_FLAGS_INITIAL 0x0U
+
+/**
+ * @brief Mask to get the thread wait state flags.
+ */
+#define THREAD_WAIT_STATE_MASK 0xffU
+
+/**
+ * @brief Indicates that the thread begins with the blocking operation.
+ *
+ * A blocking operation consists of an optional watchdog initialization and the
+ * setting of the appropriate thread blocking state with the corresponding
+ * scheduler block operation.
+ */
+#define THREAD_WAIT_STATE_INTEND_TO_BLOCK 0x1U
+
+/**
+ * @brief Indicates that the thread completed the blocking operation.
+ */
+#define THREAD_WAIT_STATE_BLOCKED 0x2U
+
+/**
+ * @brief Indicates that a condition to end the thread wait occurred.
+ *
+ * This could be a timeout, a signal, an event or a resource availability.
+ */
+#define THREAD_WAIT_STATE_READY_AGAIN 0x4U
+
+/**
+ * @brief Mask to get the thread wait class flags.
+ */
+#define THREAD_WAIT_CLASS_MASK 0xff00U
+
+/**
+ * @brief Indicates that the thread waits for an event.
+ */
+#define THREAD_WAIT_CLASS_EVENT 0x100U
+
+/**
+ * @brief Indicates that the thread waits for a system event.
+ */
+#define THREAD_WAIT_CLASS_SYSTEM_EVENT 0x200U
+
+/**
+ * @brief Indicates that the thread waits for a object.
+ */
+#define THREAD_WAIT_CLASS_OBJECT 0x400U
+
+RTEMS_INLINE_ROUTINE void _Thread_Wait_flags_set(
+ Thread_Control *the_thread,
+ Thread_Wait_flags flags
+)
+{
+#if defined(RTEMS_SMP)
+ _Atomic_Store_uint( &the_thread->Wait.flags, flags, ATOMIC_ORDER_RELAXED );
+#else
+ the_thread->Wait.flags = flags;
+#endif
+}
+
+RTEMS_INLINE_ROUTINE Thread_Wait_flags _Thread_Wait_flags_get(
+ const Thread_Control *the_thread
+)
+{
+#if defined(RTEMS_SMP)
+ return _Atomic_Load_uint( &the_thread->Wait.flags, ATOMIC_ORDER_RELAXED );
+#else
+ return the_thread->Wait.flags;
+#endif
+}
+
+/**
+ * @brief Tries to change the thread wait flags inside a critical section
+ * (interrupts disabled).
+ *
+ * In case the wait flags are equal to the expected wait flags, then the wait
+ * flags are set to the desired wait flags.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] expected_flags The expected wait flags.
+ * @param[in] desired_flags The desired wait flags.
+ *
+ * @retval true The wait flags were equal to the expected wait flags.
+ * @retval false Otherwise.
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Wait_flags_try_change_critical(
+ Thread_Control *the_thread,
+ Thread_Wait_flags expected_flags,
+ Thread_Wait_flags desired_flags
+)
+{
+#if defined(RTEMS_SMP)
+ return _Atomic_Compare_exchange_uint(
+ &the_thread->Wait.flags,
+ &expected_flags,
+ desired_flags,
+ ATOMIC_ORDER_RELAXED,
+ ATOMIC_ORDER_RELAXED
+ );
+#else
+ bool success = the_thread->Wait.flags == expected_flags;
+
+ if ( success ) {
+ the_thread->Wait.flags = desired_flags;
+ }
+
+ return success;
+#endif
+}
+
+/**
+ * @brief Tries to change the thread wait flags.
+ *
+ * @see _Thread_Wait_flags_try_change_critical().
+ */
+RTEMS_INLINE_ROUTINE bool _Thread_Wait_flags_try_change(
+ Thread_Control *the_thread,
+ Thread_Wait_flags expected_flags,
+ Thread_Wait_flags desired_flags
+)
+{
+ bool success;
+#if !defined(RTEMS_SMP)
+ ISR_Level level;
+
+ _ISR_Disable_without_giant( level );
+#endif
+
+ success = _Thread_Wait_flags_try_change_critical(
+ the_thread,
+ expected_flags,
+ desired_flags
+ );
+
+#if !defined(RTEMS_SMP)
+ _ISR_Enable_without_giant( level );
+#endif
+
+ return success;
+}
+
+/**
+ * @brief Sets the thread queue.
+ *
+ * The caller must be the owner of the thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_queue The new queue.
+ *
+ * @see _Thread_Lock_set().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Wait_set_queue(
+ Thread_Control *the_thread,
+ Thread_queue_Queue *new_queue
+)
+{
+ the_thread->Wait.queue = new_queue;
+}
+
+/**
+ * @brief Sets the thread queue operations.
+ *
+ * The caller must be the owner of the thread lock.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] new_operations The new queue operations.
+ *
+ * @see _Thread_Lock_set() and _Thread_Wait_restore_default_operations().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Wait_set_operations(
+ Thread_Control *the_thread,
+ const Thread_queue_Operations *new_operations
+)
+{
+ the_thread->Wait.operations = new_operations;
+}
+
+/**
+ * @brief Restores the default thread queue operations.
+ *
+ * The caller must be the owner of the thread lock.
+ *
+ * @param[in] the_thread The thread.
+ *
+ * @see _Thread_Wait_set_operations().
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Wait_restore_default_operations(
+ Thread_Control *the_thread
+)
+{
+ the_thread->Wait.operations = &_Thread_queue_Operations_default;
+}
+
+/**
+ * @brief Sets the thread wait timeout code.
+ *
+ * @param[in] the_thread The thread.
+ * @param[in] timeout_code The new thread wait timeout code.
+ */
+RTEMS_INLINE_ROUTINE void _Thread_Wait_set_timeout_code(
+ Thread_Control *the_thread,
+ uint32_t timeout_code
+)
+{
+ the_thread->Wait.timeout_code = timeout_code;
+}
+
+/**
+ * @brief General purpose thread wait timeout.
+ *
+ * @param[in] id Unused.
+ * @param[in] arg The thread.
+ */
+void _Thread_Timeout( Objects_Id id, void *arg );
+
+RTEMS_INLINE_ROUTINE void _Thread_Debug_set_real_processor(
+ Thread_Control *the_thread,
+ Per_CPU_Control *cpu
+)
+{
+#if defined(RTEMS_SMP) && defined(RTEMS_DEBUG)
+ the_thread->Scheduler.debug_real_cpu = cpu;
+#else
+ (void) the_thread;
+ (void) cpu;
+#endif
+}
+
+/** @}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(RTEMS_MULTIPROCESSING)
+#include <rtems/score/threadmp.h>
+#endif
+
+#endif
+/* end of include file */