summaryrefslogblamecommitdiffstats
path: root/testsuites/validation/tx-support.c
blob: 64ebe260f6d3cb53f01fa7460f9b12200ccd594e (plain) (tree)
1
2
3
4
5
6
7
8
9




                                           
                                     
  

                                                                               


   
                                                   


























                                                                              
                       
                      

                       






                                       








                                                                      
                            






















                                                                
                              


   































                                                                          





                           



































                                                             

                                 
          

           



                















                                               




                                                      
                            












                                                            
                            








                                                   

















                                                                   




                                                                            
                          
                                                          
                            













                                                                   
























































































































































































































































































                                                                            




                                                       
                            







                                                                           
                            



                                  


                       




                                        




















































































































































                                                                             
 






























































































































































































































































































































                                                                              
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup RTEMSTestSuitesValidation
 *
 * @brief This source file contains the implementation of support functions for
 *   the validation test cases.
 */

/*
 * 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-support.h"
#include "ts-config.h"

#include <rtems/test.h>
#include <rtems/score/percpu.h>
#include <rtems/score/smpimpl.h>
#include <rtems/score/threaddispatch.h>
#include <rtems/score/threadimpl.h>
#include <rtems/rtems/semimpl.h>

#include <string.h>

rtems_id DoCreateTask( rtems_name name, rtems_task_priority priority )
{
  rtems_status_code sc;
  rtems_id          id;

  sc = rtems_task_create(
    name,
    priority,
    TEST_MINIMUM_STACK_SIZE,
    RTEMS_DEFAULT_MODES,
    RTEMS_DEFAULT_ATTRIBUTES,
    &id
  );
  T_assert_rsc_success( sc );

  return id;
}

void StartTask( rtems_id id, rtems_task_entry entry, void *arg )
{
  rtems_status_code sc;

  sc = rtems_task_start( id, entry, (rtems_task_argument) arg);
  T_assert_rsc_success( sc );
}

void DeleteTask( rtems_id id )
{
  if ( id != 0 ) {
    rtems_status_code sc;

    sc = rtems_task_delete( id );
    T_quiet_rsc_success( sc );
  }
}

void SuspendTask( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_task_suspend( id );
  T_quiet_rsc_success( sc );
}

void SuspendSelf( void )
{
  SuspendTask( RTEMS_SELF );
}

void ResumeTask( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_task_resume( id );
  T_quiet_rsc_success( sc );
}

bool IsTaskSuspended( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_task_is_suspended( id );
  T_quiet_true( sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED );

  return sc == RTEMS_ALREADY_SUSPENDED;
}

rtems_event_set QueryPendingEvents( void )
{
  rtems_status_code sc;
  rtems_event_set   events;

  events = 0;
  sc = rtems_event_receive(
    RTEMS_PENDING_EVENTS,
    RTEMS_EVENT_ALL | RTEMS_NO_WAIT,
    0,
    &events
  );
  T_quiet_rsc_success( sc );

  return events;
}

rtems_event_set PollAnyEvents( void )
{
  rtems_event_set events;

  events = 0;
  (void) rtems_event_receive(
    RTEMS_ALL_EVENTS,
    RTEMS_EVENT_ANY | RTEMS_NO_WAIT,
    0,
    &events
  );

  return events;
}

rtems_event_set ReceiveAnyEvents( void )
{
  return ReceiveAnyEventsTimed( RTEMS_NO_TIMEOUT );
}

rtems_event_set ReceiveAnyEventsTimed( rtems_interval ticks )
{
  rtems_event_set events;

  events = 0;
  (void) rtems_event_receive(
    RTEMS_ALL_EVENTS,
    RTEMS_EVENT_ANY | RTEMS_WAIT,
    ticks,
    &events
  );

  return events;
}

void ReceiveAllEvents( rtems_event_set events )
{
  rtems_status_code sc;
  rtems_event_set   received;

  received = 0;
  sc = rtems_event_receive(
    events,
    RTEMS_EVENT_ALL | RTEMS_WAIT,
    RTEMS_NO_TIMEOUT,
    &received
  );
  T_quiet_rsc_success( sc );
  T_quiet_eq_u32( received, events );
}

void SendEvents( rtems_id id, rtems_event_set events )
{
  rtems_status_code sc;

  sc = rtems_event_send( id, events );
  T_quiet_rsc_success( sc );
}

rtems_mode GetMode( void )
{
  return SetMode( RTEMS_DEFAULT_MODES, RTEMS_CURRENT_MODE );
}

rtems_mode SetMode( rtems_mode set, rtems_mode mask )
{
  rtems_status_code sc;
  rtems_mode        previous;

  sc = rtems_task_mode( set, mask, &previous );
  T_quiet_rsc_success( sc );

  return previous;
}

rtems_task_priority GetPriority( rtems_id id )
{
  return SetPriority( id, RTEMS_CURRENT_PRIORITY );
}

rtems_task_priority GetPriorityByScheduler(
  rtems_id task_id,
  rtems_id scheduler_id
)
{
  rtems_status_code   sc;
  rtems_task_priority priority;

  priority = PRIO_INVALID;
  sc = rtems_task_get_priority( task_id, scheduler_id, &priority );

  if ( sc != RTEMS_SUCCESSFUL ) {
    return PRIO_INVALID;
  }

  return priority;
}

rtems_task_priority SetPriority( rtems_id id, rtems_task_priority priority )
{
  rtems_status_code   sc;
  rtems_task_priority previous;

  previous = PRIO_INVALID;
  sc = rtems_task_set_priority( id, priority, &previous );
  T_quiet_rsc_success( sc );

  return previous;
}

rtems_task_priority GetSelfPriority( void )
{
  return SetPriority( RTEMS_SELF, RTEMS_CURRENT_PRIORITY );
}

rtems_task_priority SetSelfPriority( rtems_task_priority priority )
{
  return SetPriority( RTEMS_SELF, priority );
}

rtems_task_priority SetSelfPriorityNoYield( rtems_task_priority priority )
{
  rtems_status_code sc;
  rtems_id          id;

  /*
   * If the priority is lowered, then this sequence ensures that we do not
   * carry out an implicit yield.
   */

  sc = rtems_semaphore_create(
    rtems_build_name( 'T', 'E', 'M', 'P' ),
    0,
    RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_PRIORITY_CEILING,
    1,
    &id
  );
  T_quiet_rsc_success( sc );

  priority = SetSelfPriority( priority );
  ReleaseMutex( id );
  DeleteMutex( id );

  return priority;
}

rtems_id GetScheduler( rtems_id id )
{
  rtems_status_code sc;
  rtems_id          scheduler_id;

  scheduler_id = 0xffffffff;
  sc = rtems_task_get_scheduler( id, &scheduler_id );
  T_quiet_rsc_success( sc );

  return scheduler_id;
}

rtems_id GetSelfScheduler( void )
{
  return GetScheduler( RTEMS_SELF );
}

void SetScheduler(
  rtems_id            task_id,
  rtems_id            scheduler_id,
  rtems_task_priority priority
)
{
  rtems_status_code sc;

  sc = rtems_task_set_scheduler( task_id, scheduler_id, priority );
  T_quiet_rsc_success( sc );
}

void SetSelfScheduler( rtems_id scheduler_id, rtems_task_priority priority )
{
  SetScheduler( RTEMS_SELF, scheduler_id, priority );
}

void GetAffinity( rtems_id id, cpu_set_t *set )
{
  rtems_status_code sc;

  CPU_ZERO( set );
  sc = rtems_task_get_affinity( id, sizeof( *set ), set );
  T_quiet_rsc_success( sc );
}

void GetSelfAffinity( cpu_set_t *set )
{
  GetAffinity( RTEMS_SELF, set );
}

void SetAffinity( rtems_id id, const cpu_set_t *set )
{
  rtems_status_code sc;

  sc = rtems_task_set_affinity( id, sizeof( *set ), set );
  T_quiet_rsc_success( sc );
}

void SetSelfAffinity( const cpu_set_t *set )
{
  SetAffinity( RTEMS_SELF, set );
}

void SetAffinityOne( rtems_id id, uint32_t cpu_index )
{
  cpu_set_t set;

  CPU_ZERO( &set );
  CPU_SET( (int) cpu_index, &set );
  SetAffinity( id, &set );
}

void SetSelfAffinityOne( uint32_t cpu_index )
{
  SetAffinityOne( RTEMS_SELF, cpu_index );
}

void SetAffinityAll( rtems_id id )
{
  cpu_set_t set;

  CPU_FILL( &set );
  SetAffinity( id, &set );
}

void SetSelfAffinityAll( void )
{
  SetAffinityAll( RTEMS_SELF );
}

void Yield( void )
{
  rtems_status_code sc;

  sc = rtems_task_wake_after( RTEMS_YIELD_PROCESSOR );
  T_quiet_rsc_success( sc );
}

void YieldTask( rtems_id id )
{
  Thread_Control  *the_thread;
  ISR_lock_Context lock_context;
  Per_CPU_Control *cpu_self;

  the_thread = _Thread_Get( id, &lock_context );

  if ( the_thread == NULL ) {
    return;
  }

  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
  _ISR_lock_ISR_enable( &lock_context);
  _Thread_Yield( the_thread );
  _Thread_Dispatch_direct( cpu_self );
}

void AddProcessor( rtems_id scheduler_id, uint32_t cpu_index )
{
  rtems_status_code sc;

  sc = rtems_scheduler_add_processor( scheduler_id, cpu_index );
  T_quiet_rsc_success( sc );
}

void RemoveProcessor( rtems_id scheduler_id, uint32_t cpu_index )
{
  rtems_status_code sc;

  sc = rtems_scheduler_remove_processor( scheduler_id, cpu_index );
  T_quiet_rsc_success( sc );
}

rtems_id CreateMutex( void )
{
  rtems_status_code sc;
  rtems_id          id;

  id = INVALID_ID;
  sc = rtems_semaphore_create(
    rtems_build_name( 'M', 'U', 'T', 'X' ),
    1,
    RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
    0,
    &id
  );
  T_rsc_success( sc );

  return id;
}

rtems_id CreateMutexNoProtocol( void )
{
  rtems_status_code sc;
  rtems_id          id;

  id = INVALID_ID;
  sc = rtems_semaphore_create(
    rtems_build_name( 'M', 'U', 'T', 'X' ),
    1,
    RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY,
    0,
    &id
  );
  T_rsc_success( sc );

  return id;
}

rtems_id CreateMutexFIFO( void )
{
  rtems_status_code sc;
  rtems_id          id;

  id = INVALID_ID;
  sc = rtems_semaphore_create(
    rtems_build_name( 'M', 'U', 'T', 'X' ),
    1,
    RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO,
    0,
    &id
  );
  T_rsc_success( sc );

  return id;
}

void DeleteMutex( rtems_id id )
{
  if ( id != INVALID_ID ) {
    rtems_status_code sc;

    sc = rtems_semaphore_delete( id );
    T_rsc_success( sc );
  }
}

bool IsMutexOwner( rtems_id id )
{
  Semaphore_Control   *the_semaphore;
  Thread_queue_Context queue_context;

  the_semaphore = _Semaphore_Get( id, &queue_context );
  if ( the_semaphore == NULL ) {
    return false;
  }

  _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
  return the_semaphore->Core_control.Wait_queue.Queue.owner ==
    _Thread_Get_executing();
}

void ObtainMutex( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
  T_rsc_success( sc );
}

void ObtainMutexTimed( rtems_id id, rtems_interval ticks )
{
  rtems_status_code sc;

  sc = rtems_semaphore_obtain( id, RTEMS_WAIT, ticks );
  T_rsc_success( sc );
}

void ObtainMutexDeadlock( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
  T_rsc( sc, RTEMS_INCORRECT_STATE );
}

void ReleaseMutex( rtems_id id )
{
  rtems_status_code sc;

  sc = rtems_semaphore_release( id );
  T_rsc_success( sc );
}

Thread_queue_Queue *GetMutexThreadQueue( rtems_id id )
{
  Semaphore_Control   *the_semaphore;
  Thread_queue_Context queue_context;

  the_semaphore = _Semaphore_Get( id, &queue_context );
  if ( the_semaphore == NULL ) {
    return NULL;
  }

  _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
  return &the_semaphore->Core_control.Wait_queue.Queue;
}

void RestoreRunnerASR( void )
{
  rtems_status_code sc;

  sc = rtems_signal_catch( NULL, RTEMS_DEFAULT_MODES );
  T_quiet_rsc_success( sc );
}

void RestoreRunnerMode( void )
{
  rtems_status_code sc;
  rtems_mode        mode;

  sc = rtems_task_mode( RTEMS_DEFAULT_MODES, RTEMS_ALL_MODE_MASKS, &mode );
  T_quiet_rsc_success( sc );
}

void RestoreRunnerPriority( void )
{
  SetSelfPriority( 1 );
}

void RestoreRunnerScheduler( void )
{
  SetSelfScheduler( SCHEDULER_A_ID, 1 );
}

Thread_Control *GetThread( rtems_id id )
{
  Thread_Control  *the_thread;
  ISR_lock_Context lock_context;

  the_thread = _Thread_Get( id, &lock_context );

  if ( the_thread == NULL ) {
    return NULL;
  }

  _ISR_lock_ISR_enable( &lock_context);
  return the_thread;
}

Thread_Control *GetExecuting( void )
{
  return _Thread_Get_executing();
}

void KillZombies( void )
{
  _RTEMS_Lock_allocator();
  _Thread_Kill_zombies();
  _RTEMS_Unlock_allocator();
}

void WaitForExecutionStop( rtems_id task_id )
{
#if defined( RTEMS_SMP )
  Thread_Control *the_thread;

  the_thread = GetThread( task_id );
  T_assert_not_null( the_thread );

  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
    /* Wait */
  }
#else
  (void) task_id;
#endif
}

void WaitForIntendToBlock( rtems_id task_id )
{
#if defined( RTEMS_SMP )
  Thread_Control   *the_thread;
  Thread_Wait_flags intend_to_block;

  the_thread = GetThread( task_id );
  T_assert_not_null( the_thread );

  intend_to_block = THREAD_WAIT_CLASS_OBJECT |
    THREAD_WAIT_STATE_INTEND_TO_BLOCK;

  while ( _Thread_Wait_flags_get_acquire( the_thread ) != intend_to_block ) {
    /* Wait */
  }
#else
  (void) task_id;
#endif
}

void WaitForHeir( uint32_t cpu_index, rtems_id task_id )
{
  Per_CPU_Control *cpu;

  cpu = _Per_CPU_Get_by_index( cpu_index );

  while ( cpu->heir->Object.id != task_id ) {
    RTEMS_COMPILER_MEMORY_BARRIER();
  }
}

void WaitForNextTask( uint32_t cpu_index, rtems_id task_id )
{
  Per_CPU_Control *cpu;

  cpu = _Per_CPU_Get_by_index( cpu_index );

  while ( cpu->heir->Object.id == task_id ) {
    RTEMS_COMPILER_MEMORY_BARRIER();
  }

  while ( cpu->thread_dispatch_disable_level != 0 ) {
    RTEMS_COMPILER_MEMORY_BARRIER();
  }
}

void GetTaskTimerInfo( rtems_id id, TaskTimerInfo *info )
{
  GetTaskTimerInfoByThread( GetThread( id ), info );
}

void GetTaskTimerInfoByThread(
  struct _Thread_Control *thread,
  TaskTimerInfo          *info
)
{
  info->expire_ticks = 0;
  info->expire_timespec.tv_sec = -1;
  info->expire_timespec.tv_nsec = -1;

  if ( thread != NULL ) {
    ISR_lock_Context lock_context;
    ISR_lock_Context lock_context_2;
    Per_CPU_Control *cpu;

    _ISR_lock_ISR_disable_and_acquire( &thread->Timer.Lock, &lock_context );
    info->expire_ticks = thread->Timer.Watchdog.expire;
#if defined( RTEMS_SMP )
    cpu = thread->Timer.Watchdog.cpu;
#else
    cpu = _Per_CPU_Get();
#endif
    _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context_2 );

    if ( _Watchdog_Is_scheduled( &thread->Timer.Watchdog ) ) {
      const Watchdog_Header *hdr;

      hdr = thread->Timer.header;

      if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ] ) {
        info->state = TASK_TIMER_TICKS;
      } else {
        _Watchdog_Ticks_to_timespec(
          info->expire_ticks,
          &info->expire_timespec
        );

        if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ] ) {
          info->state = TASK_TIMER_REALTIME;
        } else {
          T_quiet_eq_ptr(
            hdr,
            &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ]
          );
          info->state = TASK_TIMER_MONOTONIC;
        }
      }
    } else {
      info->state = TASK_TIMER_INACTIVE;
    }

    _Watchdog_Per_CPU_release_critical( cpu, &lock_context_2 );
    _ISR_lock_Release_and_ISR_enable( &thread->Timer.Lock, &lock_context );
  } else {
    info->state = TASK_TIMER_INVALID;
  }
}

#if defined( RTEMS_SMP )
static void DoWatchdogTick( void *arg )
{
  (void) arg;
  _Watchdog_Tick( _Per_CPU_Get() );
}
#endif

void ClockTick( void )
{
  Per_CPU_Control *cpu_self;

  cpu_self = _Thread_Dispatch_disable();
#if defined( RTEMS_SMP )
  DoWatchdogTick( NULL );
  _SMP_Othercast_action( DoWatchdogTick, NULL );
#else
  _Watchdog_Tick( cpu_self );
#endif
  _Thread_Dispatch_enable( cpu_self );
}

static void FinalWatchdogTick( Per_CPU_Control *cpu )
{
  ISR_lock_Context  lock_context;
  Watchdog_Header  *header;
  Watchdog_Control *first;

  _ISR_lock_ISR_disable_and_acquire( &cpu->Watchdog.Lock, &lock_context );

  header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ];
  first = _Watchdog_Header_first( header );

  if ( first != NULL ) {
    _Watchdog_Tickle(
      header,
      first,
      UINT64_MAX,
      &cpu->Watchdog.Lock,
      &lock_context
    );
  }

  header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ];
  first = _Watchdog_Header_first( header );

  if ( first != NULL ) {
    _Watchdog_Tickle(
      header,
      first,
      UINT64_MAX,
      &cpu->Watchdog.Lock,
      &lock_context
    );
  }

  header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ];
  first = _Watchdog_Header_first( header );

  if ( first != NULL ) {
    _Watchdog_Tickle(
      header,
      first,
      UINT64_MAX,
      &cpu->Watchdog.Lock,
      &lock_context
    );
  }

  _ISR_lock_Release_and_ISR_enable( &cpu->Watchdog.Lock, &lock_context );
}

#if defined( RTEMS_SMP )
static void DoFinalWatchdogTick( void *arg )
{
  (void) arg;
  FinalWatchdogTick( _Per_CPU_Get() );
}
#endif

void FinalClockTick( void )
{
  Per_CPU_Control *cpu_self;

  cpu_self = _Thread_Dispatch_disable();
#if defined( RTEMS_SMP )
  DoFinalWatchdogTick( NULL );
  _SMP_Othercast_action( DoFinalWatchdogTick, NULL );
#else
  FinalWatchdogTick( cpu_self );
#endif
  _Thread_Dispatch_enable( cpu_self );
}

static FatalHandler fatal_handler;

static void *fatal_arg;

void FatalInitialExtension(
  rtems_fatal_source source,
  bool               always_set_to_false,
  rtems_fatal_code   code
)
{
  FatalHandler fatal;

  T_quiet_false( always_set_to_false );
  fatal = fatal_handler;

  if ( fatal != NULL ) {
    ( *fatal )( source, code, fatal_arg );
  }
}

void SetFatalHandler( FatalHandler fatal, void *arg )
{
  fatal_handler = fatal;
  fatal_arg = arg;
}

static rtems_id task_switch_id;

static rtems_task_switch_extension task_switch_extension;

static void TaskSwitchExtension( rtems_tcb *executing, rtems_tcb *heir )
{
  ( *task_switch_extension )( executing, heir );
}

void SetTaskSwitchExtension( rtems_task_switch_extension task_switch )
{
  rtems_task_switch_extension last;
  rtems_status_code           sc;

  last = task_switch_extension;

  if ( task_switch == NULL ) {
    if ( last != NULL ) {
      sc = rtems_extension_delete( task_switch_id );
      T_quiet_rsc_success( sc );

      task_switch_extension = NULL;
    }
  } else {
    task_switch_extension = task_switch;

    if ( last == NULL ) {
      rtems_extensions_table table = {
        .thread_switch = TaskSwitchExtension
      };

      sc = rtems_extension_create(
        rtems_build_name( 'T', 'S', 'W', 'I' ),
        &table,
        &task_switch_id
      );
      T_quiet_rsc_success( sc );
    }
  }
}

void ClearExtensionCalls( ExtensionCalls *calls )
{
  memset( calls, 0, sizeof( *calls ) );
}

void CopyExtensionCalls( const ExtensionCalls *from, ExtensionCalls *to )
{
  memcpy( to, from, sizeof( *to ) );
}

#if defined(RTEMS_SMP)
static volatile bool delay_thread_dispatch;

static void DelayThreadDispatchHandler( void *arg )
{
  (void) arg;

  while ( delay_thread_dispatch ) {
    /* Wait */
  }
}

static const Per_CPU_Job_context delay_thread_dispatch_context = {
  .handler = DelayThreadDispatchHandler
};

static Per_CPU_Job delay_thread_dispatch_job = {
  .context = &delay_thread_dispatch_context
};
#endif

void StartDelayThreadDispatch( uint32_t cpu_index )
{
#if defined(RTEMS_SMP)
  if ( rtems_configuration_get_maximum_processors() > cpu_index ) {
    delay_thread_dispatch = true;
    _Per_CPU_Submit_job(
      _Per_CPU_Get_by_index( cpu_index ),
      &delay_thread_dispatch_job
    );
  }
#endif
}

void StopDelayThreadDispatch( uint32_t cpu_index )
{
#if defined(RTEMS_SMP)
  if ( rtems_configuration_get_maximum_processors() > cpu_index ) {
    Per_CPU_Control *cpu_self;

    cpu_self = _Thread_Dispatch_disable();
    delay_thread_dispatch = false;
    _Per_CPU_Wait_for_job(
      _Per_CPU_Get_by_index( cpu_index ),
      &delay_thread_dispatch_job
    );
    _Thread_Dispatch_enable( cpu_self );
  }
#endif
}

bool AreInterruptsEnabled( void )
{
  return _ISR_Get_level() == 0;
}

static bool IsWhiteSpace( char c )
{
  return c == ' ' || c == '\t';
}

bool IsWhiteSpaceOnly( const char *s )
{
  char c;

  while ( ( c = *s ) != '\0' ) {
    if ( !IsWhiteSpace( c ) ) {
      return false;
    }

    ++s;
  }

  return true;
}

static const char *EatWhiteSpace( const char *s )
{
  char c;

  while ( ( c = *s ) != '\0' ) {
    if ( !IsWhiteSpace( c ) ) {
      break;
    }

    ++s;
  }

  return s;
}

bool IsEqualIgnoreWhiteSpace( const char *a, const char *b )
{
  while ( true ) {
    a = EatWhiteSpace( a );
    b = EatWhiteSpace( b );

    if ( *a != *b ) {
      return false;
    }

    if ( *a == '\0' ) {
      return true;
    }

    ++a;
    ++b;
  }

  return true;
}

#if defined(RTEMS_SMP)
bool TicketLockIsAvailable( const SMP_ticket_lock_Control *lock )
{
  unsigned int now_serving;
  unsigned int next_ticket;

  now_serving = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED );
  next_ticket = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED );

  return now_serving == next_ticket;
}

void TicketLockWaitForOwned( const SMP_ticket_lock_Control *lock )
{
  while ( TicketLockIsAvailable( lock ) ) {
    /* Wait */
  }
}

void TicketLockWaitForOthers(
  const SMP_ticket_lock_Control *lock,
  unsigned int                   others
)
{
  unsigned int expected;
  unsigned int actual;

  expected = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED );
  expected += others + 1;

  do {
    actual = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED );
  } while ( expected != actual );
}
#endif