summaryrefslogblamecommitdiffstats
path: root/testsuites/validation/tx-thread-queue.c
blob: ee9d2cf96dcf5fd359baa532482c2bf2e50729eb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12




                                           
                                     





                                                                               
                                                   

































































































































































































































































































































                                                                              





                                                     























































































































































































































































































































                                                                              
                                              
 
                                                                      









































































































































































































                                                                          
/* 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 );
}