summaryrefslogtreecommitdiffstats
path: root/testsuites/validation/tx-thread-queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'testsuites/validation/tx-thread-queue.c')
-rw-r--r--testsuites/validation/tx-thread-queue.c858
1 files changed, 858 insertions, 0 deletions
diff --git a/testsuites/validation/tx-thread-queue.c b/testsuites/validation/tx-thread-queue.c
new file mode 100644
index 0000000000..ee9d2cf96d
--- /dev/null
+++ b/testsuites/validation/tx-thread-queue.c
@@ -0,0 +1,858 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestSuitesValidation
+ *
+ * @brief This source file contains the implementation of the thread queue test
+ * support.
+ */
+
+/*
+ * Copyright (C) 2021 embedded brains GmbH & Co. KG
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tx-thread-queue.h"
+#include "tx-support.h"
+#include "ts-config.h"
+
+#include <rtems/score/threadimpl.h>
+#include <rtems/rtems/semimpl.h>
+
+void TQSend(
+ TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_event_set events
+)
+{
+#if defined( RTEMS_SMP )
+ ctx->event_received[ worker ] = false;
+#endif
+
+ SendEvents( ctx->worker_id[ worker ], events );
+}
+
+void TQWaitForEventsReceived( const TQContext *ctx, TQWorkerKind worker )
+{
+#if defined( RTEMS_SMP )
+ while ( !ctx->event_received[ worker ] ) {
+ /* Wait */
+ }
+#endif
+}
+
+void TQWaitForExecutionStop( const TQContext *ctx, TQWorkerKind worker )
+{
+#if defined( RTEMS_SMP )
+ WaitForExecutionStop( ctx->worker_id[ worker ] );
+#endif
+}
+
+void TQSendAndWaitForExecutionStop(
+ TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_event_set events
+)
+{
+ TQSend( ctx, worker, events );
+
+#if defined( RTEMS_SMP )
+ TQWaitForEventsReceived( ctx, worker );
+ WaitForExecutionStop( ctx->worker_id[ worker ] );
+#endif
+}
+
+void TQWaitForIntendToBlock( const TQContext *ctx, TQWorkerKind worker )
+{
+ const rtems_tcb *thread;
+ Thread_Wait_flags intend_to_block;
+
+ thread = ctx->worker_tcb[ worker ];
+ intend_to_block = THREAD_WAIT_CLASS_OBJECT |
+ THREAD_WAIT_STATE_INTEND_TO_BLOCK;
+
+ while ( _Thread_Wait_flags_get_acquire( thread ) != intend_to_block ) {
+ /* Wait */
+ }
+}
+
+void TQSendAndWaitForIntendToBlock(
+ TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_event_set events
+)
+{
+ TQSend( ctx, worker, events );
+
+#if defined( RTEMS_SMP )
+ TQWaitForEventsReceived( ctx, worker );
+ TQWaitForIntendToBlock( ctx, worker );
+#endif
+}
+
+void TQSendAndWaitForExecutionStopOrIntendToBlock(
+ TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_event_set events
+)
+{
+#if defined( RTEMS_SMP )
+ const rtems_tcb *thread;
+ Thread_Wait_flags intend_to_block;
+#endif
+
+ TQSend( ctx, worker, events );
+
+#if defined( RTEMS_SMP )
+ TQWaitForEventsReceived( ctx, worker );
+ thread = ctx->worker_tcb[ worker ];
+ intend_to_block = THREAD_WAIT_CLASS_OBJECT |
+ THREAD_WAIT_STATE_INTEND_TO_BLOCK;
+
+ while (
+ _Thread_Is_executing_on_a_processor( thread ) &&
+ _Thread_Wait_flags_get_acquire( thread ) != intend_to_block
+ ) {
+ /* Wait */
+ }
+#endif
+}
+
+void TQSendAndSynchronizeRunner(
+ TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_event_set events
+)
+{
+ T_quiet_eq_u32( QueryPendingEvents() & TQ_EVENT_RUNNER_SYNC, 0 );
+ TQSend( ctx, worker, events | TQ_EVENT_RUNNER_SYNC );
+ TQSynchronizeRunner();
+}
+
+void TQClearDone( TQContext *ctx, TQWorkerKind worker )
+{
+ ctx->done[ worker ] = false;
+}
+
+void TQWaitForDone( const TQContext *ctx, TQWorkerKind worker )
+{
+ while ( !ctx->done[ worker ] ) {
+ /* Wait */
+ }
+}
+
+void TQSynchronizeRunner( void )
+{
+ ReceiveAllEvents( TQ_EVENT_RUNNER_SYNC );
+}
+
+void TQSynchronizeRunner2( void )
+{
+ ReceiveAllEvents( TQ_EVENT_RUNNER_SYNC | TQ_EVENT_RUNNER_SYNC_2 );
+}
+
+void TQResetCounter( TQContext *ctx )
+{
+ ctx->counter = 0;
+ memset( &ctx->worker_counter, 0, sizeof( ctx->worker_counter ) );
+}
+
+uint32_t TQGetCounter( const TQContext *ctx )
+{
+ return ctx->counter;
+}
+
+uint32_t TQGetWorkerCounter( const TQContext *ctx, TQWorkerKind worker )
+{
+ return ctx->worker_counter[ worker ];
+}
+
+void TQMutexObtain( const TQContext *ctx, TQMutex mutex )
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_obtain(
+ ctx->mutex_id[ mutex ],
+ RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT
+ );
+ T_rsc_success( sc );
+}
+
+void TQMutexRelease( const TQContext *ctx, TQMutex mutex )
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_release( ctx->mutex_id[ mutex ] );
+ T_rsc_success( sc );
+}
+
+void TQSetPriority(
+ const TQContext *ctx,
+ TQWorkerKind worker,
+ Priority priority
+)
+{
+ SetPriority( ctx->worker_id[ worker ], priority );
+}
+
+Priority TQGetPriority( const TQContext *ctx, TQWorkerKind worker )
+{
+ return GetPriority( ctx->worker_id[ worker ] );
+}
+
+void TQSetScheduler(
+ const TQContext *ctx,
+ TQWorkerKind worker,
+ rtems_id scheduler_id,
+ Priority priority
+)
+{
+#if defined( RTEMS_SMP )
+ rtems_status_code sc;
+
+ sc = rtems_task_set_scheduler(
+ ctx->worker_id[ worker ],
+ scheduler_id,
+ priority
+ );
+ T_rsc_success( sc );
+#else
+ (void) scheduler_id;
+ SetPriority( ctx->worker_id[ worker ], priority );
+#endif
+}
+
+static void Count( TQContext *ctx, TQWorkerKind worker )
+{
+ unsigned int counter;
+
+ counter = _Atomic_Fetch_add_uint( &ctx->counter, 1, ATOMIC_ORDER_RELAXED );
+ ctx->worker_counter[ worker ] = counter + 1;
+}
+
+static void Enqueue( TQContext *ctx, TQWorkerKind worker, TQWait wait )
+{
+ ctx->status[ worker ] = TQEnqueue( ctx, wait );
+ Count( ctx, worker );
+}
+
+static void ThreadQueueDeadlock(
+ rtems_fatal_source source,
+ rtems_fatal_code code,
+ void *arg
+)
+{
+ TQContext *ctx;
+
+ ctx = arg;
+ T_eq_int( source, INTERNAL_ERROR_CORE );
+ T_eq_int( code, INTERNAL_ERROR_THREAD_QUEUE_DEADLOCK );
+ SetFatalHandler( NULL, NULL );
+ longjmp( ctx->before_enqueue, 1 );
+}
+
+static void Worker( rtems_task_argument arg, TQWorkerKind worker )
+{
+ TQContext *ctx;
+
+ ctx = (TQContext *) arg;
+
+ while ( true ) {
+ rtems_event_set events;
+
+ events = ReceiveAnyEvents();
+ ctx->event_received[ worker ] = true;
+
+ if ( ( events & TQ_EVENT_HELPER_A_SYNC ) != 0 ) {
+ SendEvents( ctx->worker_id[ TQ_HELPER_A ], TQ_EVENT_RUNNER_SYNC );
+ }
+
+ if ( ( events & TQ_EVENT_HELPER_B_SYNC ) != 0 ) {
+ SendEvents( ctx->worker_id[ TQ_HELPER_B ], TQ_EVENT_RUNNER_SYNC );
+ }
+
+ if ( ( events & TQ_EVENT_SCHEDULER_RECORD_START ) != 0 ) {
+ TQSchedulerRecordStart( ctx );
+ }
+
+ if ( ( events & TQ_EVENT_ENQUEUE_PREPARE ) != 0 ) {
+ TQEnqueuePrepare( ctx );
+ }
+
+ if ( ( events & TQ_EVENT_ENQUEUE ) != 0 ) {
+ Enqueue( ctx, worker, ctx->wait );
+ }
+
+ if ( ( events & TQ_EVENT_ENQUEUE_TIMED ) != 0 ) {
+ Enqueue( ctx, worker, TQ_WAIT_TIMED );
+ }
+
+ if ( ( events & TQ_EVENT_ENQUEUE_FATAL ) != 0 ) {
+ SetFatalHandler( ThreadQueueDeadlock, ctx );
+
+ if ( setjmp( ctx->before_enqueue ) == 0 ) {
+ ctx->status[ worker ] = STATUS_MINUS_ONE;
+ Enqueue( ctx, worker, ctx->wait );
+ } else {
+ ctx->status[ worker ] = STATUS_DEADLOCK;
+ }
+ }
+
+ if ( ( events & TQ_EVENT_TIMEOUT ) != 0 ) {
+ Per_CPU_Control *cpu_self;
+
+ cpu_self = _Thread_Dispatch_disable();
+ _Thread_Timeout( &ctx->worker_tcb[ worker ]->Timer.Watchdog );
+ _Thread_Dispatch_direct( cpu_self );
+ }
+
+ if ( ( events & TQ_EVENT_FLUSH_ALL ) != 0 ) {
+ TQFlush( ctx, true );
+ }
+
+ if ( ( events & TQ_EVENT_FLUSH_PARTIAL ) != 0 ) {
+ TQFlush( ctx, false );
+ }
+
+ if ( ( events & TQ_EVENT_ENQUEUE_DONE ) != 0 ) {
+ TQEnqueueDone( ctx );
+ }
+
+ if ( ( events & TQ_EVENT_SURRENDER ) != 0 ) {
+ Status_Control status;
+
+ status = TQSurrender( ctx );
+ T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_A_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_A );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_A_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_A );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_B_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_B );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_B_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_B );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_C_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_C );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_C_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_C );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_D_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_D );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_D_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_D );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_NO_PROTOCOL_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_NO_PROTOCOL );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_NO_PROTOCOL_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_NO_PROTOCOL );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_FIFO_OBTAIN ) != 0 ) {
+ TQMutexObtain( ctx, TQ_MUTEX_FIFO );
+ }
+
+ if ( ( events & TQ_EVENT_MUTEX_FIFO_RELEASE ) != 0 ) {
+ TQMutexRelease( ctx, TQ_MUTEX_FIFO );
+ }
+
+ if ( ( events & TQ_EVENT_PIN ) != 0 ) {
+ _Thread_Pin( _Thread_Get_executing() );
+ }
+
+ if ( ( events & TQ_EVENT_UNPIN ) != 0 ) {
+ Per_CPU_Control *cpu_self;
+
+ cpu_self = _Thread_Dispatch_disable();
+ _Thread_Unpin( _Thread_Get_executing(), cpu_self );
+ _Thread_Dispatch_direct( cpu_self );
+ }
+
+ if ( ( events & TQ_EVENT_SCHEDULER_RECORD_STOP ) != 0 ) {
+ TQSchedulerRecordStop( ctx );
+ }
+
+ if ( ( events & TQ_EVENT_RUNNER_SYNC ) != 0 ) {
+ SendEvents( ctx->runner_id, TQ_EVENT_RUNNER_SYNC );
+ }
+
+ if ( ( events & TQ_EVENT_COUNT ) != 0 ) {
+ Count( ctx, worker );
+ }
+
+ if ( ( events & TQ_EVENT_BUSY_WAIT ) != 0 ) {
+ while ( ctx->busy_wait[ worker ] ) {
+ /* Wait */
+ }
+ }
+
+ if ( ( events & TQ_EVENT_RUNNER_SYNC_2 ) != 0 ) {
+ SendEvents( ctx->runner_id, TQ_EVENT_RUNNER_SYNC_2 );
+ }
+
+ ctx->done[ worker ] = true;
+ }
+}
+
+static void BlockerA( rtems_task_argument arg )
+{
+ Worker( arg, TQ_BLOCKER_A );
+}
+
+static void BlockerB( rtems_task_argument arg )
+{
+ Worker( arg, TQ_BLOCKER_B );
+}
+
+static void BlockerC( rtems_task_argument arg )
+{
+ Worker( arg, TQ_BLOCKER_C );
+}
+
+static void BlockerD( rtems_task_argument arg )
+{
+ Worker( arg, TQ_BLOCKER_D );
+}
+
+static void BlockerE( rtems_task_argument arg )
+{
+ Worker( arg, TQ_BLOCKER_E );
+}
+
+static void WorkerF( rtems_task_argument arg )
+{
+ Worker( arg, TQ_WORKER_F );
+}
+
+static void HelperA( rtems_task_argument arg )
+{
+ Worker( arg, TQ_HELPER_A );
+}
+
+static void HelperB( rtems_task_argument arg )
+{
+ Worker( arg, TQ_HELPER_B );
+}
+
+static void HelperC( rtems_task_argument arg )
+{
+ Worker( arg, TQ_HELPER_C );
+}
+
+void TQInitialize( TQContext *ctx )
+{
+ rtems_status_code sc;
+ size_t i;
+
+ ctx->runner_id = rtems_task_self();
+ ctx->runner_tcb = GetThread( RTEMS_SELF );
+
+ /*
+ * Use a lower priority than all started worker tasks to make sure they wait
+ * for events.
+ */
+ SetSelfPriority( PRIO_VERY_LOW );
+
+ for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->mutex_id ); ++i ) {
+ rtems_attribute attributes;
+
+ attributes = RTEMS_BINARY_SEMAPHORE;
+
+ if ( i == TQ_MUTEX_NO_PROTOCOL ) {
+ attributes |= RTEMS_PRIORITY;
+ } else if ( i == TQ_MUTEX_FIFO ) {
+ attributes |= RTEMS_FIFO;
+ } else {
+ attributes |= RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY;
+ }
+
+ sc = rtems_semaphore_create(
+ rtems_build_name( 'M', 'T', 'X', 'A' + i ),
+ 1,
+ attributes,
+ 0,
+ &ctx->mutex_id[ i ]
+ );
+ T_rsc_success( sc );
+ }
+
+ ctx->worker_id[ TQ_BLOCKER_A ] = CreateTask( "BLKA", PRIO_HIGH );
+ StartTask( ctx->worker_id[ TQ_BLOCKER_A ], BlockerA, ctx );
+ ctx->worker_id[ TQ_BLOCKER_B ] = CreateTask( "BLKB", PRIO_VERY_HIGH );
+ StartTask( ctx->worker_id[ TQ_BLOCKER_B ], BlockerB, ctx );
+ ctx->worker_id[ TQ_BLOCKER_C ] = CreateTask( "BLKC", PRIO_ULTRA_HIGH );
+ StartTask( ctx->worker_id[ TQ_BLOCKER_C ], BlockerC, ctx );
+ ctx->worker_id[ TQ_BLOCKER_D ] = CreateTask( "BLKD", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_BLOCKER_D ], BlockerD, ctx );
+ ctx->worker_id[ TQ_BLOCKER_E ] = CreateTask( "BLKE", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_BLOCKER_E ], BlockerE, ctx );
+ ctx->worker_id[ TQ_WORKER_F ] = CreateTask( "WRKF", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_WORKER_F ], WorkerF, ctx );
+ ctx->worker_id[ TQ_HELPER_A ] = CreateTask( "HLPA", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_HELPER_A ], HelperA, ctx );
+ ctx->worker_id[ TQ_HELPER_B ] = CreateTask( "HLPB", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_HELPER_B ], HelperB, ctx );
+ ctx->worker_id[ TQ_HELPER_C ] = CreateTask( "HLPC", PRIO_LOW );
+ StartTask( ctx->worker_id[ TQ_HELPER_C ], HelperC, ctx );
+
+ for (i = 0; i < RTEMS_ARRAY_SIZE( ctx->worker_tcb ); ++i) {
+ ctx->worker_tcb[ i ] = GetThread( ctx->worker_id[ i ] );
+ }
+
+ SetSelfPriority( PRIO_NORMAL );
+}
+
+void TQDestroy( TQContext *ctx )
+{
+ size_t i;
+
+ for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->worker_id ); ++i ) {
+ DeleteTask( ctx->worker_id[ i ] );
+ }
+
+ for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->mutex_id ); ++i ) {
+ if ( ctx->mutex_id[ i ] != 0 ) {
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_delete( ctx->mutex_id[ i ] );
+ T_rsc_success( sc );
+ }
+ }
+
+ RestoreRunnerPriority();
+}
+
+void TQReset( TQContext *ctx )
+{
+ rtems_id scheduler_id;
+
+ scheduler_id = SCHEDULER_A_ID;
+ SetScheduler( ctx->runner_id, scheduler_id, PRIO_NORMAL );
+ TQSetScheduler( ctx, TQ_BLOCKER_A, scheduler_id, PRIO_HIGH );
+ TQSetScheduler( ctx, TQ_BLOCKER_B, scheduler_id, PRIO_VERY_HIGH );
+ TQSetScheduler( ctx, TQ_BLOCKER_C, scheduler_id, PRIO_ULTRA_HIGH );
+ TQSetScheduler( ctx, TQ_BLOCKER_D, scheduler_id, PRIO_LOW );
+ TQSetScheduler( ctx, TQ_BLOCKER_E, scheduler_id, PRIO_LOW );
+ TQSetScheduler( ctx, TQ_HELPER_A, scheduler_id, PRIO_LOW );
+ TQSetScheduler( ctx, TQ_HELPER_B, scheduler_id, PRIO_LOW );
+ TQSetScheduler( ctx, TQ_HELPER_C, scheduler_id, PRIO_LOW );
+}
+
+void TQSortMutexesByID( TQContext *ctx )
+{
+ size_t i;
+ size_t n;
+
+ n = 3;
+
+ /* Bubble sort */
+ for ( i = 1; i < n ; ++i ) {
+ size_t j;
+
+ for ( j = 0; j < n - i; ++j ) {
+ if ( ctx->mutex_id[ j ] > ctx->mutex_id[ j + 1 ] ) {
+ rtems_id tmp;
+
+ tmp = ctx->mutex_id[ j ];
+ ctx->mutex_id[ j ] = ctx->mutex_id[ j + 1 ];
+ ctx->mutex_id[ j + 1 ] = tmp;
+ }
+ }
+ }
+}
+
+void TQGetProperties( TQContext *ctx, TQWorkerKind enqueued_worker )
+{
+ ( *ctx->get_properties )( ctx, enqueued_worker );
+}
+
+Status_Control TQConvertStatus( TQContext *ctx, Status_Control status )
+{
+ return ( *ctx->convert_status )( status );
+}
+
+void TQEnqueuePrepare( TQContext *ctx )
+{
+ ( *ctx->enqueue_prepare )( ctx );
+}
+
+Status_Control TQEnqueue( TQContext *ctx, TQWait wait )
+{
+ return ( *ctx->enqueue )( ctx, wait );
+}
+
+Status_Control TQEnqueueFatal( TQContext *ctx )
+{
+ Status_Control status;
+
+ SetFatalHandler( ThreadQueueDeadlock, ctx );
+ status = STATUS_MINUS_ONE;
+
+ if ( setjmp( ctx->before_enqueue ) == 0 ) {
+ status = TQEnqueue( ctx, ctx->wait );
+ } else {
+ status = STATUS_DEADLOCK;
+ }
+
+ return status;
+}
+
+void TQEnqueueDone( TQContext *ctx )
+{
+ ( *ctx->enqueue_done )( ctx );
+}
+
+Status_Control TQSurrender( TQContext *ctx )
+{
+ return ( *ctx->surrender )( ctx );
+}
+
+void TQFlush( TQContext *ctx, bool flush_all )
+{
+ ctx->flush_count = ( *ctx->flush )( ctx, ctx->how_many, flush_all );
+}
+
+rtems_tcb *TQGetOwner( TQContext *ctx )
+{
+ rtems_tcb *( *get_owner )( TQContext * );
+
+ get_owner = ctx->get_owner;
+
+ if ( get_owner == NULL ) {
+ return NULL;
+ }
+
+ return ( *get_owner )( ctx );
+}
+
+void TQSchedulerRecordStart( TQContext *ctx )
+{
+ T_scheduler_log *log;
+
+ log = T_scheduler_record_40( &ctx->scheduler_log );
+ T_null( log );
+}
+
+void TQSchedulerRecordStop( TQContext *ctx )
+{
+ T_scheduler_log *log;
+
+ log = T_scheduler_record( NULL );
+ T_eq_ptr( &log->header, &ctx->scheduler_log.header );
+}
+
+const T_scheduler_event *TQGetNextAny( TQContext *ctx, size_t *index )
+{
+ return T_scheduler_next_any(
+ &ctx->scheduler_log.header,
+ index
+ );
+}
+
+const T_scheduler_event *TQGetNextBlock( TQContext *ctx, size_t *index )
+{
+ return T_scheduler_next(
+ &ctx->scheduler_log.header,
+ T_SCHEDULER_BLOCK,
+ index
+ );
+}
+
+const T_scheduler_event *TQGetNextUnblock( TQContext *ctx, size_t *index )
+{
+ return T_scheduler_next(
+ &ctx->scheduler_log.header,
+ T_SCHEDULER_UNBLOCK,
+ index
+ );
+}
+
+const T_scheduler_event *TQGetNextUpdatePriority(
+ TQContext *ctx,
+ size_t *index
+)
+{
+ return T_scheduler_next(
+ &ctx->scheduler_log.header,
+ T_SCHEDULER_UPDATE_PRIORITY,
+ index
+ );
+}
+
+const T_scheduler_event *TQGetNextAskForHelp(
+ TQContext *ctx,
+ size_t *index
+)
+{
+ return T_scheduler_next(
+ &ctx->scheduler_log.header,
+ T_SCHEDULER_ASK_FOR_HELP,
+ index
+ );
+}
+
+void TQDoNothing( TQContext *ctx )
+{
+ (void) ctx;
+}
+
+Status_Control TQDoNothingSuccessfully( TQContext *ctx )
+{
+ (void) ctx;
+
+ return STATUS_SUCCESSFUL;
+}
+
+Status_Control TQConvertStatusClassic( Status_Control status )
+{
+ return STATUS_BUILD( STATUS_GET_CLASSIC( status ), 0 );
+}
+
+Status_Control TQConvertStatusPOSIX( Status_Control status )
+{
+ return STATUS_BUILD( 0, STATUS_GET_POSIX( status ) );
+}
+
+void TQEnqueuePrepareDefault( TQContext *ctx )
+{
+ Status_Control status;
+
+ status = TQEnqueue( ctx, TQ_NO_WAIT );
+ T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
+}
+
+void TQEnqueueDoneDefault( TQContext *ctx )
+{
+ Status_Control status;
+
+ status = TQSurrender( ctx );
+ T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
+}
+
+Status_Control TQEnqueueClassicSem( TQContext *ctx, TQWait wait )
+{
+ rtems_status_code sc;
+ rtems_option option;
+ rtems_option timeout;
+
+ switch ( wait ) {
+ case TQ_WAIT_FOREVER:
+ option = RTEMS_WAIT;
+ timeout = RTEMS_NO_TIMEOUT;
+ break;
+ case TQ_WAIT_TIMED:
+ option = RTEMS_WAIT;
+ timeout = UINT32_MAX;
+ break;
+ default:
+ option = RTEMS_NO_WAIT;
+ timeout = 0;
+ break;
+ }
+
+ sc = rtems_semaphore_obtain( ctx->thread_queue_id, option, timeout );
+
+ return STATUS_BUILD( sc, 0 );
+}
+
+Status_Control TQSurrenderClassicSem( TQContext *ctx )
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_release( ctx->thread_queue_id );
+
+ return STATUS_BUILD( sc, 0 );
+}
+
+rtems_tcb *TQGetOwnerClassicSem( TQContext *ctx )
+{
+ Semaphore_Control *semaphore;
+ Thread_queue_Context queue_context;
+ rtems_tcb *thread;
+
+ semaphore = _Semaphore_Get( ctx->thread_queue_id, &queue_context );
+ T_assert_not_null( semaphore );
+ thread = semaphore->Core_control.Wait_queue.Queue.owner;
+ _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
+
+ return thread;
+}
+
+uint32_t TQSemGetCount( TQSemContext *ctx )
+{
+ return ( *ctx->get_count )( ctx );
+}
+
+void TQSemSetCount( TQSemContext *ctx, uint32_t count )
+{
+ ( *ctx->set_count )( ctx, count );
+}
+
+uint32_t TQSemGetCountClassic( TQSemContext *ctx )
+{
+ Semaphore_Control *semaphore;
+ Thread_queue_Context queue_context;
+ uint32_t count;
+
+ semaphore = _Semaphore_Get( ctx->base.thread_queue_id, &queue_context );
+ T_assert_not_null( semaphore );
+ count = semaphore->Core_control.Semaphore.count;
+ _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
+
+ return count;
+}
+
+void TQSemSetCountClassic( TQSemContext *ctx, uint32_t count )
+{
+ Semaphore_Control *semaphore;
+ Thread_queue_Context queue_context;
+
+ semaphore = _Semaphore_Get( ctx->base.thread_queue_id, &queue_context );
+ T_assert_not_null( semaphore );
+ semaphore->Core_control.Semaphore.count = count;
+ _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
+}