score: Implement SMP-specific priority queue

This commit is contained in:
Sebastian Huber
2015-09-04 13:25:03 +02:00
parent c4db18a0bb
commit 3995e6d9c2
13 changed files with 545 additions and 16 deletions
+4 -3
View File
@@ -1008,9 +1008,10 @@ const rtems_libio_helper rtems_fs_init_helper =
CONFIGURE_SCHEDULER_CONTROLS
};
#define CONFIGURE_SCHEDULER_COUNT RTEMS_ARRAY_SIZE( _Scheduler_Table )
#if defined(RTEMS_SMP)
const size_t _Scheduler_Count =
RTEMS_ARRAY_SIZE( _Scheduler_Table );
const size_t _Scheduler_Count = CONFIGURE_SCHEDULER_COUNT;
const Scheduler_Assignment _Scheduler_Assignments[] = {
#if defined(CONFIGURE_SMP_SCHEDULER_ASSIGNMENTS)
@@ -2970,7 +2971,7 @@ const rtems_libio_helper rtems_fs_init_helper =
( \
_Configure_Object_RAM(_tasks, sizeof(Configuration_Thread_control)) \
+ _Configure_From_workspace(_Configure_Max_Objects(_tasks) \
* sizeof(Thread_queue_Heads)) \
* THREAD_QUEUE_HEADS_SIZE(CONFIGURE_SCHEDULER_COUNT)) \
+ _Configure_Max_Objects(_number_FP_tasks) \
* _Configure_From_workspace(CONTEXT_FP_SIZE) \
)
+43 -1
View File
@@ -41,6 +41,26 @@ extern "C" {
typedef struct _Thread_Control Thread_Control;
/**
* @brief Thread priority queue.
*/
typedef struct {
#if defined(RTEMS_SMP)
/**
* @brief Node to enqueue this queue in the FIFO chain of the corresponding
* heads structure.
*
* @see Thread_queue_Heads::Heads::Fifo.
*/
Chain_Node Node;
#endif
/**
* @brief The actual thread priority queue.
*/
RBTree_Control Queue;
} Thread_queue_Priority_queue;
/**
* @brief Thread queue heads.
*
@@ -61,13 +81,19 @@ typedef struct _Thread_queue_Heads {
union {
/**
* @brief This is the FIFO discipline list.
*
* On SMP configurations this FIFO is used to enqueue the per scheduler
* instance priority queues of this structure. This ensures FIFO fairness
* among the highest priority thread of each scheduler instance.
*/
Chain_Control Fifo;
#if !defined(RTEMS_SMP)
/**
* @brief This is the set of threads for priority discipline waiting.
*/
RBTree_Control Priority;
Thread_queue_Priority_queue Priority;
#endif
} Heads;
/**
@@ -81,8 +107,24 @@ typedef struct _Thread_queue_Heads {
* the thread queue heads dedicated to the thread queue of an object.
*/
Chain_Node Free_node;
#if defined(RTEMS_SMP)
/**
* @brief One priority queue per scheduler instance.
*/
Thread_queue_Priority_queue Priority[ RTEMS_ZERO_LENGTH_ARRAY ];
#endif
} Thread_queue_Heads;
#if defined(RTEMS_SMP)
#define THREAD_QUEUE_HEADS_SIZE( scheduler_count ) \
( sizeof( Thread_queue_Heads ) \
+ ( scheduler_count ) * sizeof( Thread_queue_Priority_queue ) )
#else
#define THREAD_QUEUE_HEADS_SIZE( scheduler_count ) \
sizeof( Thread_queue_Heads )
#endif
typedef struct {
/**
* @brief The thread queue heads.
@@ -22,6 +22,7 @@
#include <rtems/score/threadq.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/rbtreeimpl.h>
#include <rtems/score/scheduler.h>
#include <rtems/score/thread.h>
#ifdef __cplusplus
@@ -51,6 +52,21 @@ typedef struct {
#endif
} Thread_queue_Syslock_queue;
RTEMS_INLINE_ROUTINE void _Thread_queue_Heads_initialize(
Thread_queue_Heads *heads
)
{
#if defined(RTEMS_SMP)
size_t i;
for ( i = 0; i < _Scheduler_Count; ++i ) {
_RBTree_Initialize_empty( &heads->Priority[ i ].Queue );
}
#endif
_Chain_Initialize_empty( &heads->Free_chain );
}
RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_initialize(
Thread_queue_Queue *queue
)
+2 -1
View File
@@ -20,6 +20,7 @@
#include <rtems/score/threadimpl.h>
#include <rtems/score/interr.h>
#include <rtems/score/scheduler.h>
#include <rtems/score/wkspace.h>
#define THREAD_OFFSET_ASSERT( field ) \
@@ -73,7 +74,7 @@ void _Thread_Initialize_information(
&information->Free_thread_queue_heads,
_Workspace_Allocate_or_fatal_error,
_Objects_Maximum_per_allocation( maximum ),
sizeof( Thread_queue_Heads )
THREAD_QUEUE_HEADS_SIZE( _Scheduler_Count )
);
}
+2 -2
View File
@@ -142,12 +142,12 @@ bool _Thread_Initialize(
&information->Free_thread_queue_heads,
_Workspace_Allocate,
_Objects_Extend_size( &information->Objects ),
sizeof( *the_thread->Wait.spare_heads )
THREAD_QUEUE_HEADS_SIZE( _Scheduler_Count )
);
if ( the_thread->Wait.spare_heads == NULL ) {
goto failed;
}
_Chain_Initialize_empty( &the_thread->Wait.spare_heads->Free_chain );
_Thread_queue_Heads_initialize( the_thread->Wait.spare_heads );
/*
* Initialize the thread timer
+61 -9
View File
@@ -20,6 +20,7 @@
#include <rtems/score/assert.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/rbtreeimpl.h>
#include <rtems/score/schedulerimpl.h>
static void _Thread_queue_Do_nothing_priority_change(
Thread_Control *the_thread,
@@ -150,22 +151,41 @@ static Thread_Control *_Thread_queue_FIFO_first(
return THREAD_CHAIN_NODE_TO_THREAD( first );
}
static Thread_queue_Priority_queue *_Thread_queue_Priority_queue(
Thread_queue_Heads *heads,
const Thread_Control *the_thread
)
{
#if defined(RTEMS_SMP)
return &heads->Priority[
_Scheduler_Get_index( _Scheduler_Get_own( the_thread ) )
];
#else
(void) the_thread;
return &heads->Heads.Priority;
#endif
}
static void _Thread_queue_Priority_priority_change(
Thread_Control *the_thread,
Priority_Control new_priority,
Thread_queue_Queue *queue
)
{
Thread_queue_Heads *heads = queue->heads;
Thread_queue_Heads *heads = queue->heads;
Thread_queue_Priority_queue *priority_queue;
_Assert( heads != NULL );
priority_queue = _Thread_queue_Priority_queue( heads, the_thread );
_RBTree_Extract(
&heads->Heads.Priority,
&priority_queue->Queue,
&the_thread->Wait.Node.RBTree
);
_RBTree_Insert(
&heads->Heads.Priority,
&priority_queue->Queue,
&the_thread->Wait.Node.RBTree,
_Thread_queue_Compare_priority,
false
@@ -176,7 +196,11 @@ static void _Thread_queue_Priority_do_initialize(
Thread_queue_Heads *heads
)
{
#if defined(RTEMS_SMP)
_Chain_Initialize_empty( &heads->Heads.Fifo );
#else
_RBTree_Initialize_empty( &heads->Heads.Priority );
#endif
}
static void _Thread_queue_Priority_do_enqueue(
@@ -184,8 +208,17 @@ static void _Thread_queue_Priority_do_enqueue(
Thread_Control *the_thread
)
{
Thread_queue_Priority_queue *priority_queue =
_Thread_queue_Priority_queue( heads, the_thread );
#if defined(RTEMS_SMP)
if ( _RBTree_Is_empty( &priority_queue->Queue ) ) {
_Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
}
#endif
_RBTree_Insert(
&heads->Heads.Priority,
&priority_queue->Queue,
&the_thread->Wait.Node.RBTree,
_Thread_queue_Compare_priority,
false
@@ -197,10 +230,21 @@ static void _Thread_queue_Priority_do_extract(
Thread_Control *the_thread
)
{
Thread_queue_Priority_queue *priority_queue =
_Thread_queue_Priority_queue( heads, the_thread );
_RBTree_Extract(
&heads->Heads.Priority,
&priority_queue->Queue,
&the_thread->Wait.Node.RBTree
);
#if defined(RTEMS_SMP)
_Chain_Extract_unprotected( &priority_queue->Node );
if ( !_RBTree_Is_empty( &priority_queue->Queue ) ) {
_Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
}
#endif
}
static void _Thread_queue_Priority_enqueue(
@@ -232,11 +276,19 @@ static Thread_Control *_Thread_queue_Priority_first(
Thread_queue_Heads *heads
)
{
RBTree_Control *priority_queue = &heads->Heads.Priority;
RBTree_Node *first;
Thread_queue_Priority_queue *priority_queue;
RBTree_Node *first;
_Assert( !_RBTree_Is_empty( priority_queue ) );
first = _RBTree_Minimum( priority_queue );
#if defined(RTEMS_SMP)
_Assert( !_Chain_Is_empty( &heads->Heads.Fifo ) );
priority_queue = (Thread_queue_Priority_queue *)
_Chain_First( &heads->Heads.Fifo );
#else
priority_queue = &heads->Heads.Priority;
#endif
_Assert( !_RBTree_Is_empty( &priority_queue->Queue ) );
first = _RBTree_Minimum( &priority_queue->Queue );
return THREAD_RBTREE_NODE_TO_THREAD( first );
}
+54
View File
@@ -186,6 +186,60 @@ To set the scheduler of a task see @ref{Symmetric Multiprocessing Services
SCHEDULER_IDENT - Get ID of a scheduler} and @ref{Symmetric Multiprocessing
Services TASK_SET_SCHEDULER - Set scheduler of a task}.
@subsection Task Priority Queues
Due to the support for clustered scheduling the task priority queues need
special attention. It makes no sense to compare the priority values of two
different scheduler instances. Thus, it is not possible to simply use one
plain priority queue for tasks of different scheduler instances.
One solution to this problem is to use two levels of queues. The top level
queue provides FIFO ordering and contains priority queues. Each priority queue
is associated with a scheduler instance and contains only tasks of this
scheduler instance. Tasks are enqueued in the priority queue corresponding to
their scheduler instance. In case this priority queue was empty, then it is
appended to the FIFO. To dequeue a task the highest priority task of the first
priority queue in the FIFO is selected. Then the first priority queue is
removed from the FIFO. In case the previously first priority queue is not
empty, then it is appended to the FIFO. So there is FIFO fairness with respect
to the highest priority task of each scheduler instances. See also @cite{
Brandenburg, Björn B.: A fully preemptive multiprocessor semaphore protocol for
latency-sensitive real-time applications. In Proceedings of the 25th Euromicro
Conference on Real-Time Systems (ECRTS 2013), pages 292302, 2013.
@uref{http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13b.pdf}}.
Such a two level queue may need a considerable amount of memory if fast enqueue
and dequeue operations are desired (depends on the scheduler instance count).
To mitigate this problem an approch of the FreeBSD kernel was implemented in
RTEMS. We have the invariant that a task can be enqueued on at most one task
queue. Thus, we need only as many queues as we have tasks. Each task is
equipped with spare task queue which it can give to an object on demand. The
task queue uses a dedicated memory space independent of the other memory used
for the task itself. In case a task needs to block, then there are two options
@itemize @bullet
@item the object already has task queue, then the task enqueues itself to this
already present queue and the spare task queue of the task is added to a list
of free queues for this object, or
@item otherwise, then the queue of the task is given to the object and the task
enqueues itself to this queue.
@end itemize
In case the task is dequeued, then there are two options
@itemize @bullet
@item the task is the last task on the queue, then it removes this queue from
the object and reclaims it for its own purpose, or
@item otherwise, then the task removes one queue from the free list of the
object and reclaims it for its own purpose.
@end itemize
Since there are usually more objects than tasks, this actually reduces the
memory demands. In addition the objects contain only a pointer to the task
queue structure. This helps to hide implementation details and makes it
possible to use self-contained synchronization objects in Newlib and GCC (C++
and OpenMP run-time support).
@subsection Scheduler Helping Protocol
The scheduler provides a helping protocol to support locking protocols like
+1
View File
@@ -26,6 +26,7 @@ SUBDIRS += smplock01
SUBDIRS += smpmigration01
SUBDIRS += smpmigration02
SUBDIRS += smpmrsp01
SUBDIRS += smpmutex01
SUBDIRS += smpschedaffinity01
SUBDIRS += smpschedaffinity02
SUBDIRS += smpschedaffinity03
+1
View File
@@ -81,6 +81,7 @@ smplock01/Makefile
smpmigration01/Makefile
smpmigration02/Makefile
smpmrsp01/Makefile
smpmutex01/Makefile
smppsxaffinity01/Makefile
smppsxaffinity02/Makefile
smppsxsignal01/Makefile
@@ -0,0 +1,19 @@
rtems_tests_PROGRAMS = smpmutex01
smpmutex01_SOURCES = init.c
dist_rtems_tests_DATA = smpmutex01.scn smpmutex01.doc
include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
include $(top_srcdir)/../automake/compile.am
include $(top_srcdir)/../automake/leaf.am
AM_CPPFLAGS += -I$(top_srcdir)/../support/include
LINK_OBJS = $(smpmutex01_OBJECTS)
LINK_LIBS = $(smpmutex01_LDLIBS)
smpmutex01$(EXEEXT): $(smpmutex01_OBJECTS) $(smpmutex01_DEPENDENCIES)
@rm -f smpmutex01$(EXEEXT)
$(make-exe)
include $(top_srcdir)/../automake/local.am
+326
View File
@@ -0,0 +1,326 @@
/*
* Copyright (c) 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tmacros.h"
const char rtems_test_name[] = "SMPMUTEX 1";
#define SCHED_A rtems_build_name(' ', ' ', ' ', 'A')
#define SCHED_B rtems_build_name(' ', ' ', ' ', 'B')
#define PART_COUNT 2
#define TASK_COUNT 8
typedef enum {
REQ_WAKE_UP_MASTER = RTEMS_EVENT_0,
REQ_WAKE_UP_HELPER = RTEMS_EVENT_1,
REQ_MTX_OBTAIN = RTEMS_EVENT_2,
REQ_MTX_RELEASE = RTEMS_EVENT_3
} request_id;
typedef enum {
A_1,
A_2_0,
A_2_1,
M,
B_4,
B_5_0,
B_5_1,
H,
NONE
} task_id;
typedef struct {
rtems_id mtx;
rtems_id tasks[TASK_COUNT];
int generation[TASK_COUNT];
int expected_generation[TASK_COUNT];
} test_context;
static test_context test_instance;
static void start_task(
test_context *ctx,
task_id id,
rtems_task_entry entry,
rtems_task_priority prio,
rtems_name scheduler
)
{
rtems_status_code sc;
rtems_id scheduler_id;
sc = rtems_task_create(
rtems_build_name('T', 'A', 'S', 'K'),
prio,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&ctx->tasks[id]
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_scheduler_ident(scheduler, &scheduler_id);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_set_scheduler(ctx->tasks[id], scheduler_id);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_start(ctx->tasks[id], entry, id);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void send_event(test_context *ctx, task_id id, rtems_event_set events)
{
rtems_status_code sc;
sc = rtems_event_send(ctx->tasks[id], events);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static rtems_event_set wait_for_events(void)
{
rtems_event_set events;
rtems_status_code sc;
sc = rtems_event_receive(
RTEMS_ALL_EVENTS,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
return events;
}
static void sync_with_helper(test_context *ctx)
{
rtems_event_set events;
send_event(ctx, H, REQ_WAKE_UP_HELPER);
events = wait_for_events();
rtems_test_assert(events == REQ_WAKE_UP_MASTER);
}
static void request(test_context *ctx, task_id id, request_id req)
{
send_event(ctx, id, req);
sync_with_helper(ctx);
}
static void obtain(test_context *ctx)
{
rtems_status_code sc;
sc = rtems_semaphore_obtain(ctx->mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void release(test_context *ctx)
{
rtems_status_code sc;
sc = rtems_semaphore_release(ctx->mtx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void check_generations(test_context *ctx, task_id a, task_id b)
{
size_t i;
if (a != NONE) {
++ctx->expected_generation[a];
}
if (b != NONE) {
++ctx->expected_generation[b];
}
for (i = 0; i < TASK_COUNT; ++i) {
rtems_test_assert(ctx->generation[i] == ctx->expected_generation[i]);
}
}
static void helper(rtems_task_argument arg)
{
test_context *ctx = &test_instance;
while (true) {
rtems_event_set events = wait_for_events();
rtems_test_assert(events == REQ_WAKE_UP_HELPER);
send_event(ctx, M, REQ_WAKE_UP_MASTER);
}
}
static void worker(rtems_task_argument arg)
{
test_context *ctx = &test_instance;
task_id id = arg;
while (true) {
rtems_event_set events = wait_for_events();
if ((events & REQ_MTX_OBTAIN) != 0) {
obtain(ctx);
++ctx->generation[id];
}
if ((events & REQ_MTX_RELEASE) != 0) {
release(ctx);
++ctx->generation[id];
}
}
}
static void test(void)
{
test_context *ctx = &test_instance;
rtems_status_code sc;
ctx->tasks[M] = rtems_task_self();
start_task(ctx, A_1, worker, 1, SCHED_A);
start_task(ctx, A_2_0, worker, 2, SCHED_A);
start_task(ctx, A_2_1, worker, 2, SCHED_A);
start_task(ctx, B_4, worker, 4, SCHED_B);
start_task(ctx, B_5_0, worker, 5, SCHED_B);
start_task(ctx, B_5_1, worker, 5, SCHED_B);
start_task(ctx, H, helper, 6, SCHED_B);
sc = rtems_semaphore_create(
rtems_build_name(' ', 'M', 'T', 'X'),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
0,
&ctx->mtx
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
obtain(ctx);
request(ctx, A_1, REQ_MTX_OBTAIN);
check_generations(ctx, NONE, NONE);
release(ctx);
check_generations(ctx, A_1, NONE);
request(ctx, A_1, REQ_MTX_RELEASE);
check_generations(ctx, A_1, NONE);
obtain(ctx);
request(ctx, A_2_0, REQ_MTX_OBTAIN);
request(ctx, A_1, REQ_MTX_OBTAIN);
request(ctx, A_2_1, REQ_MTX_OBTAIN);
check_generations(ctx, NONE, NONE);
release(ctx);
check_generations(ctx, A_1, NONE);
request(ctx, A_1, REQ_MTX_RELEASE);
check_generations(ctx, A_1, A_2_0);
request(ctx, A_2_0, REQ_MTX_RELEASE);
check_generations(ctx, A_2_0, A_2_1);
request(ctx, A_2_1, REQ_MTX_RELEASE);
check_generations(ctx, A_2_1, NONE);
obtain(ctx);
request(ctx, B_5_0, REQ_MTX_OBTAIN);
request(ctx, B_4, REQ_MTX_OBTAIN);
request(ctx, B_5_1, REQ_MTX_OBTAIN);
check_generations(ctx, NONE, NONE);
release(ctx);
sync_with_helper(ctx);
check_generations(ctx, B_4, NONE);
request(ctx, B_4, REQ_MTX_RELEASE);
check_generations(ctx, B_4, B_5_0);
request(ctx, B_5_0, REQ_MTX_RELEASE);
check_generations(ctx, B_5_0, B_5_1);
request(ctx, B_5_1, REQ_MTX_RELEASE);
check_generations(ctx, B_5_1, NONE);
obtain(ctx);
request(ctx, A_2_0, REQ_MTX_OBTAIN);
request(ctx, B_5_0, REQ_MTX_OBTAIN);
request(ctx, B_5_1, REQ_MTX_OBTAIN);
request(ctx, B_4, REQ_MTX_OBTAIN);
request(ctx, A_2_1, REQ_MTX_OBTAIN);
request(ctx, A_1, REQ_MTX_OBTAIN);
check_generations(ctx, NONE, NONE);
release(ctx);
check_generations(ctx, A_1, NONE);
request(ctx, A_1, REQ_MTX_RELEASE);
check_generations(ctx, A_1, B_4);
request(ctx, B_4, REQ_MTX_RELEASE);
check_generations(ctx, B_4, A_2_0);
request(ctx, A_2_0, REQ_MTX_RELEASE);
check_generations(ctx, A_2_0, B_5_0);
request(ctx, B_5_0, REQ_MTX_RELEASE);
check_generations(ctx, B_5_0, A_2_1);
request(ctx, A_2_1, REQ_MTX_RELEASE);
check_generations(ctx, A_2_1, B_5_1);
request(ctx, B_5_1, REQ_MTX_RELEASE);
check_generations(ctx, B_5_1, NONE);
}
static void Init(rtems_task_argument arg)
{
TEST_BEGIN();
if (rtems_get_processor_count() >= PART_COUNT) {
test();
}
TEST_END();
rtems_test_exit(0);
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
#define CONFIGURE_SMP_APPLICATION
#define CONFIGURE_SMP_MAXIMUM_PROCESSORS PART_COUNT
#define CONFIGURE_SCHEDULER_SIMPLE_SMP
#include <rtems/scheduler.h>
RTEMS_SCHEDULER_CONTEXT_SIMPLE_SMP(a);
RTEMS_SCHEDULER_CONTEXT_SIMPLE_SMP(b);
#define CONFIGURE_SCHEDULER_CONTROLS \
RTEMS_SCHEDULER_CONTROL_SIMPLE_SMP(a, SCHED_A), \
RTEMS_SCHEDULER_CONTROL_SIMPLE_SMP(b, SCHED_B)
#define CONFIGURE_SMP_SCHEDULER_ASSIGNMENTS \
RTEMS_SCHEDULER_ASSIGN(0, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_MANDATORY), \
RTEMS_SCHEDULER_ASSIGN(1, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_OPTIONAL)
#define CONFIGURE_MAXIMUM_TASKS TASK_COUNT
#define CONFIGURE_MAXIMUM_SEMAPHORES 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_INIT_TASK_PRIORITY 3
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
@@ -0,0 +1,14 @@
This file describes the directives and concepts tested by this test set.
test set name: smpmutex01
directives:
- _Thread_queue_Priority_do_enqueue()
- _Thread_queue_Priority_do_extract()
- _Thread_queue_Priority_first()
concepts:
- Ensure that the thread queue priority discipline enforces FIFO fairness
among the highest priority thread of each scheduler instance.
@@ -0,0 +1,2 @@
*** BEGIN OF TEST SMPMUTEX 1 ***
*** END OF TEST SMPMUTEX 1 ***