summaryrefslogtreecommitdiff
path: root/include/rtems/score/schedulersmpimpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/rtems/score/schedulersmpimpl.h')
-rw-r--r--include/rtems/score/schedulersmpimpl.h1089
1 files changed, 1089 insertions, 0 deletions
diff --git a/include/rtems/score/schedulersmpimpl.h b/include/rtems/score/schedulersmpimpl.h
new file mode 100644
index 0000000000..a395f2c0ba
--- /dev/null
+++ b/include/rtems/score/schedulersmpimpl.h
@@ -0,0 +1,1089 @@
+/**
+ * @file
+ *
+ * @brief SMP Scheduler Implementation
+ *
+ * @ingroup ScoreSchedulerSMP
+ */
+
+/*
+ * Copyright (c) 2013-2015 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * 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_SCHEDULERSMPIMPL_H
+#define _RTEMS_SCORE_SCHEDULERSMPIMPL_H
+
+#include <rtems/score/schedulersmp.h>
+#include <rtems/score/assert.h>
+#include <rtems/score/chainimpl.h>
+#include <rtems/score/schedulersimpleimpl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @addtogroup ScoreSchedulerSMP
+ *
+ * The scheduler nodes can be in four states
+ * - @ref SCHEDULER_SMP_NODE_BLOCKED,
+ * - @ref SCHEDULER_SMP_NODE_SCHEDULED, and
+ * - @ref SCHEDULER_SMP_NODE_READY.
+ *
+ * State transitions are triggered via basic operations
+ * - _Scheduler_SMP_Enqueue_ordered(),
+ * - _Scheduler_SMP_Enqueue_scheduled_ordered(), and
+ * - _Scheduler_SMP_Block().
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ *
+ * bs [label="BLOCKED"];
+ * ss [label="SCHEDULED", fillcolor="green"];
+ * rs [label="READY", fillcolor="red"];
+ *
+ * edge [label="enqueue"];
+ * edge [fontcolor="darkgreen", color="darkgreen"];
+ *
+ * bs -> ss;
+ *
+ * edge [fontcolor="red", color="red"];
+ *
+ * bs -> rs;
+ *
+ * edge [label="enqueue other"];
+ *
+ * ss -> rs;
+ *
+ * edge [label="block"];
+ * edge [fontcolor="black", color="black"];
+ *
+ * ss -> bs;
+ * rs -> bs;
+ *
+ * edge [label="block other"];
+ * edge [fontcolor="darkgreen", color="darkgreen"];
+ *
+ * rs -> ss;
+ * }
+ * @enddot
+ *
+ * During system initialization each processor of the scheduler instance starts
+ * with an idle thread assigned to it. Lets have a look at an example with two
+ * idle threads I and J with priority 5. We also have blocked threads A, B and
+ * C with priorities 1, 2 and 3 respectively. The scheduler nodes are ordered
+ * with respect to the thread priority from left to right in the below
+ * diagrams. The highest priority node (lowest priority number) is the
+ * leftmost node. Since the processor assignment is independent of the thread
+ * priority the processor indices may move from one state to the other.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ * subgraph {
+ * rank = same;
+ *
+ * i [label="I (5)", fillcolor="green"];
+ * j [label="J (5)", fillcolor="green"];
+ * a [label="A (1)"];
+ * b [label="B (2)"];
+ * c [label="C (3)"];
+ * i -> j;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * i -> p0;
+ * j -> p1;
+ * }
+ * @enddot
+ *
+ * Lets start A. For this an enqueue operation is performed.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * i [label="I (5)", fillcolor="green"];
+ * j [label="J (5)", fillcolor="red"];
+ * a [label="A (1)", fillcolor="green"];
+ * b [label="B (2)"];
+ * c [label="C (3)"];
+ * a -> i;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * i -> p0;
+ * a -> p1;
+ * }
+ * @enddot
+ *
+ * Lets start C.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * a [label="A (1)", fillcolor="green"];
+ * c [label="C (3)", fillcolor="green"];
+ * i [label="I (5)", fillcolor="red"];
+ * j [label="J (5)", fillcolor="red"];
+ * b [label="B (2)"];
+ * a -> c;
+ * i -> j;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * c -> p0;
+ * a -> p1;
+ * }
+ * @enddot
+ *
+ * Lets start B.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * a [label="A (1)", fillcolor="green"];
+ * b [label="B (2)", fillcolor="green"];
+ * c [label="C (3)", fillcolor="red"];
+ * i [label="I (5)", fillcolor="red"];
+ * j [label="J (5)", fillcolor="red"];
+ * a -> b;
+ * c -> i -> j;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * b -> p0;
+ * a -> p1;
+ * }
+ * @enddot
+ *
+ * Lets change the priority of thread A to 4.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * b [label="B (2)", fillcolor="green"];
+ * c [label="C (3)", fillcolor="green"];
+ * a [label="A (4)", fillcolor="red"];
+ * i [label="I (5)", fillcolor="red"];
+ * j [label="J (5)", fillcolor="red"];
+ * b -> c;
+ * a -> i -> j;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * b -> p0;
+ * c -> p1;
+ * }
+ * @enddot
+ *
+ * Now perform a blocking operation with thread B. Please note that thread A
+ * migrated now from processor 0 to processor 1 and thread C still executes on
+ * processor 1.
+ *
+ * @dot
+ * digraph {
+ * node [style="filled"];
+ * edge [dir="none"];
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * c [label="C (3)", fillcolor="green"];
+ * a [label="A (4)", fillcolor="green"];
+ * i [label="I (5)", fillcolor="red"];
+ * j [label="J (5)", fillcolor="red"];
+ * b [label="B (2)"];
+ * c -> a;
+ * i -> j;
+ * }
+ *
+ * subgraph {
+ * rank = same;
+ *
+ * p0 [label="PROCESSOR 0", shape="box"];
+ * p1 [label="PROCESSOR 1", shape="box"];
+ * }
+ *
+ * a -> p0;
+ * c -> p1;
+ * }
+ * @enddot
+ *
+ * @{
+ */
+
+typedef Scheduler_Node *( *Scheduler_SMP_Get_highest_ready )(
+ Scheduler_Context *context,
+ Scheduler_Node *node
+);
+
+typedef Scheduler_Node *( *Scheduler_SMP_Get_lowest_scheduled )(
+ Scheduler_Context *context,
+ Scheduler_Node *filter,
+ Chain_Node_order order
+);
+
+typedef void ( *Scheduler_SMP_Extract )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_extract
+);
+
+typedef void ( *Scheduler_SMP_Insert )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_insert
+);
+
+typedef void ( *Scheduler_SMP_Move )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_move
+);
+
+typedef void ( *Scheduler_SMP_Update )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_update,
+ Priority_Control new_priority
+);
+
+typedef Thread_Control *( *Scheduler_SMP_Enqueue )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_enqueue,
+ Thread_Control *needs_help
+);
+
+typedef Thread_Control *( *Scheduler_SMP_Enqueue_scheduled )(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_enqueue
+);
+
+typedef void ( *Scheduler_SMP_Allocate_processor )(
+ Scheduler_Context *context,
+ Thread_Control *scheduled,
+ Thread_Control *victim
+);
+
+static inline bool _Scheduler_SMP_Insert_priority_lifo_order(
+ const Chain_Node *to_insert,
+ const Chain_Node *next
+)
+{
+ const Scheduler_SMP_Node *node_to_insert =
+ (const Scheduler_SMP_Node *) to_insert;
+ const Scheduler_SMP_Node *node_next =
+ (const Scheduler_SMP_Node *) next;
+
+ return node_to_insert->priority <= node_next->priority;
+}
+
+static inline bool _Scheduler_SMP_Insert_priority_fifo_order(
+ const Chain_Node *to_insert,
+ const Chain_Node *next
+)
+{
+ const Scheduler_SMP_Node *node_to_insert =
+ (const Scheduler_SMP_Node *) to_insert;
+ const Scheduler_SMP_Node *node_next =
+ (const Scheduler_SMP_Node *) next;
+
+ return node_to_insert->priority < node_next->priority;
+}
+
+static inline Scheduler_SMP_Context *_Scheduler_SMP_Get_self(
+ Scheduler_Context *context
+)
+{
+ return (Scheduler_SMP_Context *) context;
+}
+
+static inline void _Scheduler_SMP_Initialize(
+ Scheduler_SMP_Context *self
+)
+{
+ _Chain_Initialize_empty( &self->Scheduled );
+ _Chain_Initialize_empty( &self->Idle_threads );
+}
+
+static inline Scheduler_SMP_Node *_Scheduler_SMP_Thread_get_node(
+ Thread_Control *thread
+)
+{
+ return (Scheduler_SMP_Node *) _Scheduler_Thread_get_node( thread );
+}
+
+static inline Scheduler_SMP_Node *_Scheduler_SMP_Thread_get_own_node(
+ Thread_Control *thread
+)
+{
+ return (Scheduler_SMP_Node *) _Scheduler_Thread_get_own_node( thread );
+}
+
+static inline Scheduler_SMP_Node *_Scheduler_SMP_Node_downcast(
+ Scheduler_Node *node
+)
+{
+ return (Scheduler_SMP_Node *) node;
+}
+
+static inline void _Scheduler_SMP_Node_initialize(
+ Scheduler_SMP_Node *node,
+ Thread_Control *thread
+)
+{
+ _Scheduler_Node_do_initialize( &node->Base, thread );
+ node->state = SCHEDULER_SMP_NODE_BLOCKED;
+}
+
+static inline void _Scheduler_SMP_Node_update_priority(
+ Scheduler_SMP_Node *node,
+ Priority_Control new_priority
+)
+{
+ node->priority = new_priority;
+}
+
+extern const bool _Scheduler_SMP_Node_valid_state_changes[ 3 ][ 3 ];
+
+static inline void _Scheduler_SMP_Node_change_state(
+ Scheduler_SMP_Node *node,
+ Scheduler_SMP_Node_state new_state
+)
+{
+ _Assert(
+ _Scheduler_SMP_Node_valid_state_changes[ node->state ][ new_state ]
+ );
+
+ node->state = new_state;
+}
+
+static inline bool _Scheduler_SMP_Is_processor_owned_by_us(
+ const Scheduler_Context *context,
+ const Per_CPU_Control *cpu
+)
+{
+ return cpu->scheduler_context == context;
+}
+
+static inline Thread_Control *_Scheduler_SMP_Get_idle_thread(
+ Scheduler_Context *context
+)
+{
+ Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
+ Thread_Control *idle = (Thread_Control *)
+ _Chain_Get_first_unprotected( &self->Idle_threads );
+
+ _Assert( &idle->Object.Node != _Chain_Tail( &self->Idle_threads ) );
+
+ return idle;
+}
+
+static inline void _Scheduler_SMP_Release_idle_thread(
+ Scheduler_Context *context,
+ Thread_Control *idle
+)
+{
+ Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
+
+ _Chain_Prepend_unprotected( &self->Idle_threads, &idle->Object.Node );
+}
+
+static inline void _Scheduler_SMP_Allocate_processor_lazy(
+ Scheduler_Context *context,
+ Thread_Control *scheduled_thread,
+ Thread_Control *victim_thread
+)
+{
+ Per_CPU_Control *scheduled_cpu = _Thread_Get_CPU( scheduled_thread );
+ Per_CPU_Control *victim_cpu = _Thread_Get_CPU( victim_thread );
+ Per_CPU_Control *cpu_self = _Per_CPU_Get();
+ Thread_Control *heir;
+
+ _Assert( _ISR_Get_level() != 0 );
+
+ if ( _Thread_Is_executing_on_a_processor( scheduled_thread ) ) {
+ if ( _Scheduler_SMP_Is_processor_owned_by_us( context, scheduled_cpu ) ) {
+ heir = scheduled_cpu->heir;
+ _Thread_Dispatch_update_heir(
+ cpu_self,
+ scheduled_cpu,
+ scheduled_thread
+ );
+ } else {
+ /* We have to force a migration to our processor set */
+ _Assert(
+ scheduled_thread->Scheduler.debug_real_cpu->heir != scheduled_thread
+ );
+ heir = scheduled_thread;
+ }
+ } else {
+ heir = scheduled_thread;
+ }
+
+ if ( heir != victim_thread ) {
+ _Thread_Set_CPU( heir, victim_cpu );
+ _Thread_Dispatch_update_heir( cpu_self, victim_cpu, heir );
+ }
+}
+
+static inline void _Scheduler_SMP_Allocate_processor(
+ Scheduler_Context *context,
+ Scheduler_Node *scheduled,
+ Scheduler_Node *victim,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ Thread_Control *scheduled_thread = _Scheduler_Node_get_user( scheduled );
+ Thread_Control *victim_thread = _Scheduler_Node_get_user( victim );
+
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( scheduled ),
+ SCHEDULER_SMP_NODE_SCHEDULED
+ );
+ _Scheduler_Thread_change_state( scheduled_thread, THREAD_SCHEDULER_SCHEDULED );
+
+ ( *allocate_processor )( context, scheduled_thread, victim_thread );
+}
+
+static inline Scheduler_Node *_Scheduler_SMP_Get_lowest_scheduled(
+ Scheduler_Context *context,
+ Scheduler_Node *filter,
+ Chain_Node_order order
+)
+{
+ Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
+ Chain_Control *scheduled = &self->Scheduled;
+ Scheduler_Node *lowest_scheduled =
+ (Scheduler_Node *) _Chain_Last( scheduled );
+
+ (void) filter;
+ (void) order;
+
+ _Assert( &lowest_scheduled->Node != _Chain_Tail( scheduled ) );
+
+ return lowest_scheduled;
+}
+
+static inline Thread_Control *_Scheduler_SMP_Enqueue_to_scheduled(
+ Scheduler_Context *context,
+ Scheduler_Node *node,
+ Scheduler_Node *lowest_scheduled,
+ Scheduler_SMP_Insert insert_scheduled,
+ Scheduler_SMP_Move move_from_scheduled_to_ready,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ Thread_Control *needs_help;
+ Scheduler_Try_to_schedule_action action;
+
+ action = _Scheduler_Try_to_schedule_node(
+ context,
+ node,
+ _Scheduler_Node_get_idle( lowest_scheduled ),
+ _Scheduler_SMP_Get_idle_thread
+ );
+
+ if ( action == SCHEDULER_TRY_TO_SCHEDULE_DO_SCHEDULE ) {
+ Thread_Control *lowest_scheduled_user =
+ _Scheduler_Node_get_user( lowest_scheduled );
+ Thread_Control *idle;
+
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( lowest_scheduled ),
+ SCHEDULER_SMP_NODE_READY
+ );
+ _Scheduler_Thread_change_state(
+ lowest_scheduled_user,
+ THREAD_SCHEDULER_READY
+ );
+
+ _Scheduler_SMP_Allocate_processor(
+ context,
+ node,
+ lowest_scheduled,
+ allocate_processor
+ );
+
+ ( *insert_scheduled )( context, node );
+ ( *move_from_scheduled_to_ready )( context, lowest_scheduled );
+
+ idle = _Scheduler_Release_idle_thread(
+ context,
+ lowest_scheduled,
+ _Scheduler_SMP_Release_idle_thread
+ );
+ if ( idle == NULL ) {
+ needs_help = lowest_scheduled_user;
+ } else {
+ needs_help = NULL;
+ }
+ } else if ( action == SCHEDULER_TRY_TO_SCHEDULE_DO_IDLE_EXCHANGE ) {
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( lowest_scheduled ),
+ SCHEDULER_SMP_NODE_READY
+ );
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( node ),
+ SCHEDULER_SMP_NODE_SCHEDULED
+ );
+
+ ( *insert_scheduled )( context, node );
+ ( *move_from_scheduled_to_ready )( context, lowest_scheduled );
+
+ _Scheduler_Exchange_idle_thread(
+ node,
+ lowest_scheduled,
+ _Scheduler_Node_get_idle( lowest_scheduled )
+ );
+
+ needs_help = NULL;
+ } else {
+ _Assert( action == SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK );
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( node ),
+ SCHEDULER_SMP_NODE_BLOCKED
+ );
+ needs_help = NULL;
+ }
+
+ return needs_help;
+}
+
+/**
+ * @brief Enqueues a node according to the specified order function.
+ *
+ * The node must not be in the scheduled state.
+ *
+ * @param[in] context The scheduler instance context.
+ * @param[in] node The node to enqueue.
+ * @param[in] needs_help The thread needing help in case the node cannot be
+ * scheduled.
+ * @param[in] order The order function.
+ * @param[in] insert_ready Function to insert a node into the set of ready
+ * nodes.
+ * @param[in] insert_scheduled Function to insert a node into the set of
+ * scheduled nodes.
+ * @param[in] move_from_scheduled_to_ready Function to move a node from the set
+ * of scheduled nodes to the set of ready nodes.
+ * @param[in] get_lowest_scheduled Function to select the node from the
+ * scheduled nodes to replace. It may not be possible to find one, in this
+ * case a pointer must be returned so that the order functions returns false
+ * if this pointer is passed as the second argument to the order function.
+ * @param[in] allocate_processor Function to allocate a processor to a node
+ * based on the rules of the scheduler.
+ */
+static inline Thread_Control *_Scheduler_SMP_Enqueue_ordered(
+ Scheduler_Context *context,
+ Scheduler_Node *node,
+ Thread_Control *needs_help,
+ Chain_Node_order order,
+ Scheduler_SMP_Insert insert_ready,
+ Scheduler_SMP_Insert insert_scheduled,
+ Scheduler_SMP_Move move_from_scheduled_to_ready,
+ Scheduler_SMP_Get_lowest_scheduled get_lowest_scheduled,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ Scheduler_Node *lowest_scheduled =
+ ( *get_lowest_scheduled )( context, node, order );
+
+ if ( ( *order )( &node->Node, &lowest_scheduled->Node ) ) {
+ needs_help = _Scheduler_SMP_Enqueue_to_scheduled(
+ context,
+ node,
+ lowest_scheduled,
+ insert_scheduled,
+ move_from_scheduled_to_ready,
+ allocate_processor
+ );
+ } else {
+ ( *insert_ready )( context, node );
+ }
+
+ return needs_help;
+}
+
+/**
+ * @brief Enqueues a scheduled node according to the specified order
+ * function.
+ *
+ * @param[in] context The scheduler instance context.
+ * @param[in] node The node to enqueue.
+ * @param[in] order The order function.
+ * @param[in] extract_from_ready Function to extract a node from the set of
+ * ready nodes.
+ * @param[in] get_highest_ready Function to get the highest ready node.
+ * @param[in] insert_ready Function to insert a node into the set of ready
+ * nodes.
+ * @param[in] insert_scheduled Function to insert a node into the set of
+ * scheduled nodes.
+ * @param[in] move_from_ready_to_scheduled Function to move a node from the set
+ * of ready nodes to the set of scheduled nodes.
+ * @param[in] allocate_processor Function to allocate a processor to a node
+ * based on the rules of the scheduler.
+ */
+static inline Thread_Control *_Scheduler_SMP_Enqueue_scheduled_ordered(
+ Scheduler_Context *context,
+ Scheduler_Node *node,
+ Chain_Node_order order,
+ Scheduler_SMP_Extract extract_from_ready,
+ Scheduler_SMP_Get_highest_ready get_highest_ready,
+ Scheduler_SMP_Insert insert_ready,
+ Scheduler_SMP_Insert insert_scheduled,
+ Scheduler_SMP_Move move_from_ready_to_scheduled,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ Thread_Control *needs_help;
+
+ do {
+ Scheduler_Node *highest_ready = ( *get_highest_ready )( context, node );
+
+ /*
+ * The node has been extracted from the scheduled chain. We have to place
+ * it now on the scheduled or ready set.
+ */
+ if ( ( *order )( &node->Node, &highest_ready->Node ) ) {
+ ( *insert_scheduled )( context, node );
+
+ needs_help = NULL;
+ } else {
+ Scheduler_Try_to_schedule_action action;
+
+ action = _Scheduler_Try_to_schedule_node(
+ context,
+ highest_ready,
+ _Scheduler_Node_get_idle( node ),
+ _Scheduler_SMP_Get_idle_thread
+ );
+
+ if ( action == SCHEDULER_TRY_TO_SCHEDULE_DO_SCHEDULE ) {
+ Thread_Control *user = _Scheduler_Node_get_user( node );
+ Thread_Control *idle;
+
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( node ),
+ SCHEDULER_SMP_NODE_READY
+ );
+ _Scheduler_Thread_change_state( user, THREAD_SCHEDULER_READY );
+
+ _Scheduler_SMP_Allocate_processor(
+ context,
+ highest_ready,
+ node,
+ allocate_processor
+ );
+
+ ( *insert_ready )( context, node );
+ ( *move_from_ready_to_scheduled )( context, highest_ready );
+
+ idle = _Scheduler_Release_idle_thread(
+ context,
+ node,
+ _Scheduler_SMP_Release_idle_thread
+ );
+ if ( idle == NULL ) {
+ needs_help = user;
+ } else {
+ needs_help = NULL;
+ }
+ } else if ( action == SCHEDULER_TRY_TO_SCHEDULE_DO_IDLE_EXCHANGE ) {
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( node ),
+ SCHEDULER_SMP_NODE_READY
+ );
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( highest_ready ),
+ SCHEDULER_SMP_NODE_SCHEDULED
+ );
+
+ ( *insert_ready )( context, node );
+ ( *move_from_ready_to_scheduled )( context, highest_ready );
+
+ _Scheduler_Exchange_idle_thread(
+ highest_ready,
+ node,
+ _Scheduler_Node_get_idle( node )
+ );
+
+ needs_help = NULL;
+ } else {
+ _Assert( action == SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK );
+
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( highest_ready ),
+ SCHEDULER_SMP_NODE_BLOCKED
+ );
+
+ ( *extract_from_ready )( context, highest_ready );
+
+ continue;
+ }
+ }
+ } while ( false );
+
+ return needs_help;
+}
+
+static inline void _Scheduler_SMP_Extract_from_scheduled(
+ Scheduler_Node *node
+)
+{
+ _Chain_Extract_unprotected( &node->Node );
+}
+
+static inline void _Scheduler_SMP_Schedule_highest_ready(
+ Scheduler_Context *context,
+ Scheduler_Node *victim,
+ Scheduler_SMP_Extract extract_from_ready,
+ Scheduler_SMP_Get_highest_ready get_highest_ready,
+ Scheduler_SMP_Move move_from_ready_to_scheduled,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ do {
+ Scheduler_Node *highest_ready = ( *get_highest_ready )( context, victim );
+ Scheduler_Try_to_schedule_action action;
+
+ action = _Scheduler_Try_to_schedule_node(
+ context,
+ highest_ready,
+ NULL,
+ _Scheduler_SMP_Get_idle_thread
+ );
+
+ if ( action == SCHEDULER_TRY_TO_SCHEDULE_DO_SCHEDULE ) {
+ _Scheduler_SMP_Allocate_processor(
+ context,
+ highest_ready,
+ victim,
+ allocate_processor
+ );
+
+ ( *move_from_ready_to_scheduled )( context, highest_ready );
+ } else {
+ _Assert( action == SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK );
+
+ _Scheduler_SMP_Node_change_state(
+ _Scheduler_SMP_Node_downcast( highest_ready ),
+ SCHEDULER_SMP_NODE_BLOCKED
+ );
+
+ ( *extract_from_ready )( context, highest_ready );
+
+ continue;
+ }
+ } while ( false );
+}
+
+/**
+ * @brief Blocks a thread.
+ *
+ * @param[in] context The scheduler instance context.
+ * @param[in] thread The thread of the scheduling operation.
+ * @param[in] extract_from_ready Function to extract a node from the set of
+ * ready nodes.
+ * @param[in] get_highest_ready Function to get the highest ready node.
+ * @param[in] move_from_ready_to_scheduled Function to move a node from the set
+ * of ready nodes to the set of scheduled nodes.
+ */
+static inline void _Scheduler_SMP_Block(
+ Scheduler_Context *context,
+ Thread_Control *thread,
+ Scheduler_SMP_Extract extract_from_ready,
+ Scheduler_SMP_Get_highest_ready get_highest_ready,
+ Scheduler_SMP_Move move_from_ready_to_scheduled,
+ Scheduler_SMP_Allocate_processor allocate_processor
+)
+{
+ Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
+ bool is_scheduled = node->state == SCHEDULER_SMP_NODE_SCHEDULED;
+ bool block;
+
+ _Assert( is_scheduled || node->state == SCHEDULER_SMP_NODE_READY );
+
+ block = _Scheduler_Block_node(
+ context,
+ thread,
+ &node->Base,
+ is_scheduled,
+ _Scheduler_SMP_Get_idle_thread
+ );
+ if ( block ) {
+ _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_BLOCKED );
+
+ if ( is_scheduled ) {
+ _Scheduler_SMP_Extract_from_scheduled( &node->Base );
+
+ _Scheduler_SMP_Schedule_highest_ready(
+ context,
+ &node->Base,
+ extract_from_ready,
+ get_highest_ready,
+ move_from_ready_to_scheduled,
+ allocate_processor
+ );
+ } else {
+ ( *extract_from_ready )( context, &node->Base );
+ }
+ }
+}
+
+static inline Thread_Control *_Scheduler_SMP_Unblock(
+ Scheduler_Context *context,
+ Thread_Control *thread,
+ Scheduler_SMP_Enqueue enqueue_fifo
+)
+{
+ Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
+ bool is_scheduled = node->state == SCHEDULER_SMP_NODE_SCHEDULED;
+ bool unblock = _Scheduler_Unblock_node(
+ context,
+ thread,
+ &node->Base,
+ is_scheduled,
+ _Scheduler_SMP_Release_idle_thread
+ );
+ Thread_Control *needs_help;
+
+ if ( unblock ) {
+ if ( node->state == SCHEDULER_SMP_NODE_BLOCKED ) {
+ _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
+
+ needs_help = ( *enqueue_fifo )( context, &node->Base, thread );
+ } else {
+ _Assert( node->state == SCHEDULER_SMP_NODE_READY );
+ _Assert(
+ node->Base.help_state == SCHEDULER_HELP_ACTIVE_OWNER
+ || node->Base.help_state == SCHEDULER_HELP_ACTIVE_RIVAL
+ );
+ _Assert( node->Base.idle == NULL );
+
+ if ( node->Base.accepts_help == thread ) {
+ needs_help = thread;
+ } else {
+ needs_help = NULL;
+ }
+ }
+ } else {
+ needs_help = NULL;
+ }
+
+ return needs_help;
+}
+
+static inline Thread_Control *_Scheduler_SMP_Change_priority(
+ Scheduler_Context *context,
+ Thread_Control *thread,
+ Priority_Control new_priority,
+ bool prepend_it,
+ Scheduler_SMP_Extract extract_from_ready,
+ Scheduler_SMP_Update update,
+ Scheduler_SMP_Enqueue enqueue_fifo,
+ Scheduler_SMP_Enqueue enqueue_lifo,
+ Scheduler_SMP_Enqueue_scheduled enqueue_scheduled_fifo,
+ Scheduler_SMP_Enqueue_scheduled enqueue_scheduled_lifo
+)
+{
+ Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_own_node( thread );
+ Thread_Control *needs_help;
+
+ if ( node->state == SCHEDULER_SMP_NODE_SCHEDULED ) {
+ _Scheduler_SMP_Extract_from_scheduled( &node->Base );
+
+ ( *update )( context, &node->Base, new_priority );
+
+ if ( prepend_it ) {
+ needs_help = ( *enqueue_scheduled_lifo )( context, &node->Base );
+ } else {
+ needs_help = ( *enqueue_scheduled_fifo )( context, &node->Base );
+ }
+ } else if ( node->state == SCHEDULER_SMP_NODE_READY ) {
+ ( *extract_from_ready )( context, &node->Base );
+
+ ( *update )( context, &node->Base, new_priority );
+
+ if ( prepend_it ) {
+ needs_help = ( *enqueue_lifo )( context, &node->Base, NULL );
+ } else {
+ needs_help = ( *enqueue_fifo )( context, &node->Base, NULL );
+ }
+ } else {
+ ( *update )( context, &node->Base, new_priority );
+
+ needs_help = NULL;
+ }
+
+ return needs_help;
+}
+
+static inline Thread_Control *_Scheduler_SMP_Ask_for_help(
+ Scheduler_Context *context,
+ Thread_Control *offers_help,
+ Thread_Control *needs_help,
+ Scheduler_SMP_Enqueue enqueue_fifo
+)
+{
+ Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_own_node( offers_help );
+ Thread_Control *next_needs_help = NULL;
+ Thread_Control *previous_accepts_help;
+
+ previous_accepts_help = node->Base.accepts_help;
+ node->Base.accepts_help = needs_help;
+
+ switch ( node->state ) {
+ case SCHEDULER_SMP_NODE_READY:
+ next_needs_help =
+ _Scheduler_Ask_ready_node_for_help( &node->Base, needs_help );
+ break;
+ case SCHEDULER_SMP_NODE_SCHEDULED:
+ next_needs_help = _Scheduler_Ask_scheduled_node_for_help(
+ context,
+ &node->Base,
+ offers_help,
+ needs_help,
+ previous_accepts_help,
+ _Scheduler_SMP_Release_idle_thread
+ );
+ break;
+ case SCHEDULER_SMP_NODE_BLOCKED:
+ if (
+ _Scheduler_Ask_blocked_node_for_help(
+ context,
+ &node->Base,
+ offers_help,
+ needs_help
+ )
+ ) {
+ _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
+
+ next_needs_help = ( *enqueue_fifo )(
+ context,
+ &node->Base,
+ needs_help
+ );
+ }
+ break;
+ }
+
+ return next_needs_help;
+}
+
+static inline Thread_Control *_Scheduler_SMP_Yield(
+ Scheduler_Context *context,
+ Thread_Control *thread,
+ Scheduler_SMP_Extract extract_from_ready,
+ Scheduler_SMP_Enqueue enqueue_fifo,
+ Scheduler_SMP_Enqueue_scheduled enqueue_scheduled_fifo
+)
+{
+ Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
+ Thread_Control *needs_help;
+
+ if ( node->state == SCHEDULER_SMP_NODE_SCHEDULED ) {
+ _Scheduler_SMP_Extract_from_scheduled( &node->Base );
+
+ needs_help = ( *enqueue_scheduled_fifo )( context, &node->Base );
+ } else {
+ ( *extract_from_ready )( context, &node->Base );
+
+ needs_help = ( *enqueue_fifo )( context, &node->Base, NULL );
+ }
+
+ return needs_help;
+}
+
+static inline void _Scheduler_SMP_Insert_scheduled_lifo(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_insert
+)
+{
+ Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
+
+ _Chain_Insert_ordered_unprotected(
+ &self->Scheduled,
+ &node_to_insert->Node,
+ _Scheduler_SMP_Insert_priority_lifo_order
+ );
+}
+
+static inline void _Scheduler_SMP_Insert_scheduled_fifo(
+ Scheduler_Context *context,
+ Scheduler_Node *node_to_insert
+)
+{
+ Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
+
+ _Chain_Insert_ordered_unprotected(
+ &self->Scheduled,
+ &node_to_insert->Node,
+ _Scheduler_SMP_Insert_priority_fifo_order
+ );
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_SCORE_SCHEDULERSMPIMPL_H */