/*
 * FreeRTOS V202411.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/*
 * This file defines one of the more complex set of demo/test tasks.  They are
 * designed to stress test the queue implementation though pseudo simultaneous
 * multiple reads and multiple writes from both tasks of varying priority and
 * interrupts.  The interrupts are prioritised such to ensure that nesting
 * occurs (for those ports that support it).
 *
 * The test ensures that, while being accessed from three tasks and two
 * interrupts, all the data sent to the queues is also received from
 * the same queue, and that no duplicate items are either sent or received.
 * The tests also ensure that a low priority task is never able to successfully
 * read from or write to a queue when a task of higher priority is attempting
 * the same operation.
 */

/* Standard includes. */
#include <string.h>

/* SafeRTOS includes. */
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"

/* Demo app includes. */
#include "IntQueue.h"
#include "IntQueueTimer.h"

#if ( INCLUDE_eTaskGetState != 1 )
    #error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file.
#endif

/* Priorities used by test tasks. */
#ifndef intqHIGHER_PRIORITY
    #define intqHIGHER_PRIORITY          ( configMAX_PRIORITIES - 2 )
#endif
#define intqLOWER_PRIORITY               ( tskIDLE_PRIORITY )

/* The number of values to send/receive before checking that all values were
 * processed as expected. */
#define intqNUM_VALUES_TO_LOG            ( 200 )
#define intqSHORT_DELAY                  ( 140 )

/* The value by which the value being sent to or received from a queue should
 * increment past intqNUM_VALUES_TO_LOG before we check that all values have been
 * sent/received correctly.  This is done to ensure that all tasks and interrupts
 * accessing the queue have completed their accesses with the
 * intqNUM_VALUES_TO_LOG range. */
#define intqVALUE_OVERRUN                ( 50 )

/* The delay used by the polling task.  A short delay is used for code
 * coverage. */
#define intqONE_TICK_DELAY               ( 1 )

/* Each task and interrupt is given a unique identifier.  This value is used to
 * identify which task sent or received each value.  The identifier is also used
 * to distinguish between two tasks that are running the same task function. */
#define intqHIGH_PRIORITY_TASK1          ( ( UBaseType_t ) 1 )
#define intqHIGH_PRIORITY_TASK2          ( ( UBaseType_t ) 2 )
#define intqLOW_PRIORITY_TASK            ( ( UBaseType_t ) 3 )
#define intqFIRST_INTERRUPT              ( ( UBaseType_t ) 4 )
#define intqSECOND_INTERRUPT             ( ( UBaseType_t ) 5 )
#define intqQUEUE_LENGTH                 ( ( UBaseType_t ) 10 )

/* At least intqMIN_ACCEPTABLE_TASK_COUNT values should be sent to/received
 * from each queue by each task, otherwise an error is detected. */
#define intqMIN_ACCEPTABLE_TASK_COUNT    ( 5 )

/* Send the next value to the queue that is normally empty.  This is called
 * from within the interrupts. */
#define timerNORMALLY_EMPTY_TX()                                                                                                          \
    if( xQueueIsQueueFullFromISR( xNormallyEmptyQueue ) != pdTRUE )                                                                       \
    {                                                                                                                                     \
        UBaseType_t uxSavedInterruptStatus;                                                                                               \
        uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();                                                                           \
        {                                                                                                                                 \
            uxValueForNormallyEmptyQueue++;                                                                                               \
            if( xQueueSendFromISR( xNormallyEmptyQueue, ( void * ) &uxValueForNormallyEmptyQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
            {                                                                                                                             \
                uxValueForNormallyEmptyQueue--;                                                                                           \
            }                                                                                                                             \
        }                                                                                                                                 \
        taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );                                                                             \
    }                                                                                                                                     \


/* Send the next value to the queue that is normally full.  This is called
 * from within the interrupts. */
#define timerNORMALLY_FULL_TX()                                                                                                         \
    if( xQueueIsQueueFullFromISR( xNormallyFullQueue ) != pdTRUE )                                                                      \
    {                                                                                                                                   \
        UBaseType_t uxSavedInterruptStatus;                                                                                             \
        uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();                                                                         \
        {                                                                                                                               \
            uxValueForNormallyFullQueue++;                                                                                              \
            if( xQueueSendFromISR( xNormallyFullQueue, ( void * ) &uxValueForNormallyFullQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
            {                                                                                                                           \
                uxValueForNormallyFullQueue--;                                                                                          \
            }                                                                                                                           \
        }                                                                                                                               \
        taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );                                                                           \
    }                                                                                                                                   \


/* Receive a value from the normally empty queue.  This is called from within
 * an interrupt. */
#define timerNORMALLY_EMPTY_RX()                                                                         \
    if( xQueueReceiveFromISR( xNormallyEmptyQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) != pdPASS ) \
    {                                                                                                    \
        prvQueueAccessLogError( __LINE__ );                                                              \
    }                                                                                                    \
    else                                                                                                 \
    {                                                                                                    \
        prvRecordValue_NormallyEmpty( uxRxedValue, intqSECOND_INTERRUPT );                               \
    }

/* Receive a value from the normally full queue.  This is called from within
 * an interrupt. */
#define timerNORMALLY_FULL_RX()                                                                         \
    if( xQueueReceiveFromISR( xNormallyFullQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) == pdPASS ) \
    {                                                                                                   \
        prvRecordValue_NormallyFull( uxRxedValue, intqSECOND_INTERRUPT );                               \
    }                                                                                                   \


/*-----------------------------------------------------------*/

/* The two queues used by the test. */
static QueueHandle_t xNormallyEmptyQueue, xNormallyFullQueue;

/* Variables used to detect a stall in one of the tasks. */
static volatile UBaseType_t uxHighPriorityLoops1 = 0, uxHighPriorityLoops2 = 0, uxLowPriorityLoops1 = 0, uxLowPriorityLoops2 = 0;

/* Any unexpected behaviour sets xErrorStatus to fail and log the line that
 * caused the error in xErrorLine. */
static BaseType_t xErrorStatus = pdPASS;
static volatile UBaseType_t xErrorLine = ( UBaseType_t ) 0;

/* Used for sequencing between tasks. */
static BaseType_t xWasSuspended = pdFALSE;

/* The values that are sent to the queues.  An incremented value is sent each
 * time to each queue. */
static volatile UBaseType_t uxValueForNormallyEmptyQueue = 0, uxValueForNormallyFullQueue = 0;

/* A handle to some of the tasks is required so they can be suspended/resumed. */
TaskHandle_t xHighPriorityNormallyEmptyTask1, xHighPriorityNormallyEmptyTask2, xHighPriorityNormallyFullTask1, xHighPriorityNormallyFullTask2;

/* When a value is received in a queue the value is ticked off in the array
 * the array position of the value is set to a the identifier of the task or
 * interrupt that accessed the queue.  This way missing or duplicate values can be
 * detected. */
static uint8_t ucNormallyEmptyReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
static uint8_t ucNormallyFullReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };

/* The test tasks themselves. */
static void prvLowerPriorityNormallyEmptyTask( void * pvParameters );
static void prvLowerPriorityNormallyFullTask( void * pvParameters );
static void prvHigherPriorityNormallyEmptyTask( void * pvParameters );
static void prv1stHigherPriorityNormallyFullTask( void * pvParameters );
static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters );

/* Used to mark the positions within the ucNormallyEmptyReceivedValues and
 * ucNormallyFullReceivedValues arrays, while checking for duplicates. */
static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
                                          UBaseType_t uxSource );
static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
                                         UBaseType_t uxSource );

/* Logs the line on which an error occurred. */
static void prvQueueAccessLogError( UBaseType_t uxLine );

/*-----------------------------------------------------------*/

void vStartInterruptQueueTasks( void )
{
    /* Start the test tasks. */
    xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H1QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask1 );
    xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H2QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask2 );
    xTaskCreate( prvLowerPriorityNormallyEmptyTask, "L1QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
    xTaskCreate( prv1stHigherPriorityNormallyFullTask, "H1QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask1 );
    xTaskCreate( prv2ndHigherPriorityNormallyFullTask, "H2QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask2 );
    xTaskCreate( prvLowerPriorityNormallyFullTask, "L2QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );

    /* Create the queues that are accessed by multiple tasks and multiple
     * interrupts. */
    xNormallyFullQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
    xNormallyEmptyQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );

    /* vQueueAddToRegistry() adds the queue to the queue registry, if one is
     * in use.  The queue registry is provided as a means for kernel aware
     * debuggers to locate queues and has no purpose if a kernel aware debugger
     * is not being used.  The call to vQueueAddToRegistry() will be removed
     * by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
     * defined to be less than 1. */
    vQueueAddToRegistry( xNormallyFullQueue, "NormallyFull" );
    vQueueAddToRegistry( xNormallyEmptyQueue, "NormallyEmpty" );
}
/*-----------------------------------------------------------*/

static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
                                         UBaseType_t uxSource )
{
    if( uxValue < intqNUM_VALUES_TO_LOG )
    {
        /* We don't expect to receive the same value twice, so if the value
         * has already been marked as received an error has occurred. */
        if( ucNormallyFullReceivedValues[ uxValue ] != 0x00 )
        {
            prvQueueAccessLogError( __LINE__ );
        }

        /* Log that this value has been received. */
        ucNormallyFullReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
    }
}
/*-----------------------------------------------------------*/

static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
                                          UBaseType_t uxSource )
{
    if( uxValue < intqNUM_VALUES_TO_LOG )
    {
        /* We don't expect to receive the same value twice, so if the value
         * has already been marked as received an error has occurred. */
        if( ucNormallyEmptyReceivedValues[ uxValue ] != 0x00 )
        {
            prvQueueAccessLogError( __LINE__ );
        }

        /* Log that this value has been received. */
        ucNormallyEmptyReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
    }
}
/*-----------------------------------------------------------*/

static void prvQueueAccessLogError( UBaseType_t uxLine )
{
    /* Latch the line number that caused the error. */
    xErrorLine = uxLine;
    xErrorStatus = pdFAIL;
}
/*-----------------------------------------------------------*/

static void prvHigherPriorityNormallyEmptyTask( void * pvParameters )
{
    UBaseType_t uxRxed, ux, uxTask1, uxTask2, uxInterrupts, uxErrorCount1 = 0, uxErrorCount2 = 0;

    /* The timer should not be started until after the scheduler has started.
     * More than one task is running this code so we check the parameter value
     * to determine which task should start the timer. */
    if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
    {
        vInitialiseTimerForIntQueueTest();
    }

    for( ; ; )
    {
        /* Block waiting to receive a value from the normally empty queue.
         * Interrupts will write to the queue so we should receive a value. */
        if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqSHORT_DELAY ) != pdPASS )
        {
            prvQueueAccessLogError( __LINE__ );
        }
        else
        {
            /* Note which value was received so we can check all expected
             * values are received and no values are duplicated. */
            prvRecordValue_NormallyEmpty( uxRxed, ( UBaseType_t ) pvParameters );
        }

        /* Ensure the other task running this code gets a chance to execute. */
        taskYIELD();

        if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
        {
            /* Have we received all the expected values? */
            if( uxValueForNormallyEmptyQueue > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
            {
                vTaskSuspend( xHighPriorityNormallyEmptyTask2 );

                uxTask1 = 0;
                uxTask2 = 0;
                uxInterrupts = 0;

                /* Loop through the array, checking that both tasks have
                 * placed values into the array, and that no values are missing.
                 * Start at 1 as we expect position 0 to be unused. */
                for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
                {
                    if( ucNormallyEmptyReceivedValues[ ux ] == 0 )
                    {
                        /* A value is missing. */
                        prvQueueAccessLogError( __LINE__ );
                    }
                    else
                    {
                        if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK1 )
                        {
                            /* Value was placed into the array by task 1. */
                            uxTask1++;
                        }
                        else if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK2 )
                        {
                            /* Value was placed into the array by task 2. */
                            uxTask2++;
                        }
                        else if( ucNormallyEmptyReceivedValues[ ux ] == intqSECOND_INTERRUPT )
                        {
                            uxInterrupts++;
                        }
                    }
                }

                if( uxTask1 < intqMIN_ACCEPTABLE_TASK_COUNT )
                {
                    /* Only task 2 seemed to log any values. */
                    uxErrorCount1++;

                    if( uxErrorCount1 > 2 )
                    {
                        prvQueueAccessLogError( __LINE__ );
                    }
                }
                else
                {
                    uxErrorCount1 = 0;
                }

                if( uxTask2 < intqMIN_ACCEPTABLE_TASK_COUNT )
                {
                    /* Only task 1 seemed to log any values. */
                    uxErrorCount2++;

                    if( uxErrorCount2 > 2 )
                    {
                        prvQueueAccessLogError( __LINE__ );
                    }
                }
                else
                {
                    uxErrorCount2 = 0;
                }

                if( uxInterrupts == 0 )
                {
                    prvQueueAccessLogError( __LINE__ );
                }

                /* Clear the array again, ready to start a new cycle. */
                memset( ucNormallyEmptyReceivedValues, 0x00, sizeof( ucNormallyEmptyReceivedValues ) );

                uxHighPriorityLoops1++;
                portENTER_CRITICAL();
                {
                    uxValueForNormallyEmptyQueue = 0;
                }
                portEXIT_CRITICAL();

                /* Suspend ourselves, allowing the lower priority task to
                 * actually receive something from the queue.  Until now it
                 * will have been prevented from doing so by the higher
                 * priority tasks.  The lower priority task will resume us
                 * if it receives something.  We will then resume the other
                 * higher priority task. */
                vTaskSuspend( NULL );
                vTaskResume( xHighPriorityNormallyEmptyTask2 );
            }
        }
    }
}
/*-----------------------------------------------------------*/

static void prvLowerPriorityNormallyEmptyTask( void * pvParameters )
{
    UBaseType_t uxValue, uxRxed;

    /* The parameters are not being used so avoid compiler warnings. */
    ( void ) pvParameters;

    for( ; ; )
    {
        if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqONE_TICK_DELAY ) != errQUEUE_EMPTY )
        {
            /* A value should only be obtained when the high priority task is
             * suspended. */
            if( eTaskGetState( xHighPriorityNormallyEmptyTask1 ) != eSuspended )
            {
                prvQueueAccessLogError( __LINE__ );
            }

            prvRecordValue_NormallyEmpty( uxRxed, intqLOW_PRIORITY_TASK );

            /* Wake the higher priority task again. */
            vTaskResume( xHighPriorityNormallyEmptyTask1 );
            uxLowPriorityLoops1++;
        }
        else
        {
            /* Raise our priority while we send so we can preempt the higher
             *  priority task, and ensure we get the Tx value into the queue. */
            vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );

            portENTER_CRITICAL();
            {
                uxValueForNormallyEmptyQueue++;
                uxValue = uxValueForNormallyEmptyQueue;
            }
            portEXIT_CRITICAL();

            if( xQueueSend( xNormallyEmptyQueue, &uxValue, portMAX_DELAY ) != pdPASS )
            {
                prvQueueAccessLogError( __LINE__ );
            }

            vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
        }
    }
}
/*-----------------------------------------------------------*/

static void prv1stHigherPriorityNormallyFullTask( void * pvParameters )
{
    UBaseType_t uxValueToTx, ux, uxInterrupts;

    /* The parameters are not being used so avoid compiler warnings. */
    ( void ) pvParameters;

    /* Make sure the queue starts full or near full.  >> 1 as there are two
     * high priority tasks. */
    for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
    {
        portENTER_CRITICAL();
        {
            uxValueForNormallyFullQueue++;
            uxValueToTx = uxValueForNormallyFullQueue;
        }
        portEXIT_CRITICAL();

        xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
    }

    for( ; ; )
    {
        portENTER_CRITICAL();
        {
            uxValueForNormallyFullQueue++;
            uxValueToTx = uxValueForNormallyFullQueue;
        }
        portEXIT_CRITICAL();

        if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
        {
            /* intqHIGH_PRIORITY_TASK2 is never suspended so we would not
             * expect it to ever time out. */
            prvQueueAccessLogError( __LINE__ );
        }

        /* Allow the other task running this code to run. */
        taskYIELD();

        /* Have all the expected values been sent to the queue? */
        if( uxValueToTx > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
        {
            /* Make sure the other high priority task completes its send of
             * any values below intqNUM_VALUE_TO_LOG. */
            vTaskDelay( intqSHORT_DELAY );

            vTaskSuspend( xHighPriorityNormallyFullTask2 );

            if( xWasSuspended == pdTRUE )
            {
                /* We would have expected the other high priority task to have
                 * set this back to false by now. */
                prvQueueAccessLogError( __LINE__ );
            }

            /* Set the suspended flag so an error is not logged if the other
             * task recognises a time out when it is unsuspended. */
            xWasSuspended = pdTRUE;

            /* Check interrupts are also sending. */
            uxInterrupts = 0U;

            /* Start at 1 as we expect position 0 to be unused. */
            for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
            {
                if( ucNormallyFullReceivedValues[ ux ] == 0 )
                {
                    /* A value was missing. */
                    prvQueueAccessLogError( __LINE__ );
                }
                else if( ucNormallyFullReceivedValues[ ux ] == intqSECOND_INTERRUPT )
                {
                    uxInterrupts++;
                }
            }

            if( uxInterrupts == 0 )
            {
                /* No writes from interrupts were found.  Are interrupts
                 * actually running? */
                prvQueueAccessLogError( __LINE__ );
            }

            /* Reset the array ready for the next cycle. */
            memset( ucNormallyFullReceivedValues, 0x00, sizeof( ucNormallyFullReceivedValues ) );

            uxHighPriorityLoops2++;
            portENTER_CRITICAL();
            {
                uxValueForNormallyFullQueue = 0;
            }
            portEXIT_CRITICAL();

            /* Suspend ourselves, allowing the lower priority task to
             * actually receive something from the queue.  Until now it
             * will have been prevented from doing so by the higher
             * priority tasks.  The lower priority task will resume us
             * if it receives something.  We will then resume the other
             * higher priority task. */
            vTaskSuspend( NULL );
            vTaskResume( xHighPriorityNormallyFullTask2 );
        }
    }
}
/*-----------------------------------------------------------*/

static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters )
{
    UBaseType_t uxValueToTx, ux;

    /* The parameters are not being used so avoid compiler warnings. */
    ( void ) pvParameters;

    /* Make sure the queue starts full or near full.  >> 1 as there are two
     * high priority tasks. */
    for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
    {
        portENTER_CRITICAL();
        {
            uxValueForNormallyFullQueue++;
            uxValueToTx = uxValueForNormallyFullQueue;
        }
        portEXIT_CRITICAL();

        xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
    }

    for( ; ; )
    {
        portENTER_CRITICAL();
        {
            uxValueForNormallyFullQueue++;
            uxValueToTx = uxValueForNormallyFullQueue;
        }
        portEXIT_CRITICAL();

        if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
        {
            if( xWasSuspended != pdTRUE )
            {
                /* It is ok to time out if the task has been suspended. */
                prvQueueAccessLogError( __LINE__ );
            }
        }

        xWasSuspended = pdFALSE;

        taskYIELD();
    }
}
/*-----------------------------------------------------------*/

static void prvLowerPriorityNormallyFullTask( void * pvParameters )
{
    UBaseType_t uxValue, uxTxed = 9999;

    /* The parameters are not being used so avoid compiler warnings. */
    ( void ) pvParameters;

    for( ; ; )
    {
        if( xQueueSend( xNormallyFullQueue, &uxTxed, intqONE_TICK_DELAY ) != errQUEUE_FULL )
        {
            /* Should only succeed when the higher priority task is suspended */
            if( eTaskGetState( xHighPriorityNormallyFullTask1 ) != eSuspended )
            {
                prvQueueAccessLogError( __LINE__ );
            }

            vTaskResume( xHighPriorityNormallyFullTask1 );
            uxLowPriorityLoops2++;
        }
        else
        {
            /* Raise our priority while we receive so we can preempt the higher
             * priority task, and ensure we get the value from the queue. */
            vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );

            if( xQueueReceive( xNormallyFullQueue, &uxValue, portMAX_DELAY ) != pdPASS )
            {
                prvQueueAccessLogError( __LINE__ );
            }
            else
            {
                prvRecordValue_NormallyFull( uxValue, intqLOW_PRIORITY_TASK );
            }

            vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
        }
    }
}
/*-----------------------------------------------------------*/

BaseType_t xFirstTimerHandler( void )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    UBaseType_t uxRxedValue;
    static UBaseType_t uxNextOperation = 0;

    /* Called from a timer interrupt.  Perform various read and write
     * accesses on the queues. */

    uxNextOperation++;

    if( uxNextOperation & ( UBaseType_t ) 0x01 )
    {
        timerNORMALLY_EMPTY_TX();
        timerNORMALLY_EMPTY_TX();
        timerNORMALLY_EMPTY_TX();
    }
    else
    {
        timerNORMALLY_FULL_RX();
        timerNORMALLY_FULL_RX();
        timerNORMALLY_FULL_RX();
    }

    return xHigherPriorityTaskWoken;
}
/*-----------------------------------------------------------*/

BaseType_t xSecondTimerHandler( void )
{
    UBaseType_t uxRxedValue;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    static UBaseType_t uxNextOperation = 0;

    /* Called from a timer interrupt.  Perform various read and write
     * accesses on the queues. */

    uxNextOperation++;

    if( uxNextOperation & ( UBaseType_t ) 0x01 )
    {
        timerNORMALLY_EMPTY_TX();
        timerNORMALLY_EMPTY_TX();

        timerNORMALLY_EMPTY_RX();
        timerNORMALLY_EMPTY_RX();
    }
    else
    {
        timerNORMALLY_FULL_RX();
        timerNORMALLY_FULL_TX();
        timerNORMALLY_FULL_TX();
        timerNORMALLY_FULL_TX();
    }

    return xHigherPriorityTaskWoken;
}
/*-----------------------------------------------------------*/


BaseType_t xAreIntQueueTasksStillRunning( void )
{
    static UBaseType_t uxLastHighPriorityLoops1 = 0, uxLastHighPriorityLoops2 = 0, uxLastLowPriorityLoops1 = 0, uxLastLowPriorityLoops2 = 0;

    /* xErrorStatus can be set outside of this function.  This function just
     * checks that all the tasks are still cycling. */

    if( uxHighPriorityLoops1 == uxLastHighPriorityLoops1 )
    {
        /* The high priority 1 task has stalled. */
        prvQueueAccessLogError( __LINE__ );
    }

    uxLastHighPriorityLoops1 = uxHighPriorityLoops1;

    if( uxHighPriorityLoops2 == uxLastHighPriorityLoops2 )
    {
        /* The high priority 2 task has stalled. */
        prvQueueAccessLogError( __LINE__ );
    }

    uxLastHighPriorityLoops2 = uxHighPriorityLoops2;

    if( uxLowPriorityLoops1 == uxLastLowPriorityLoops1 )
    {
        /* The low priority 1 task has stalled. */
        prvQueueAccessLogError( __LINE__ );
    }

    uxLastLowPriorityLoops1 = uxLowPriorityLoops1;

    if( uxLowPriorityLoops2 == uxLastLowPriorityLoops2 )
    {
        /* The low priority 2 task has stalled. */
        prvQueueAccessLogError( __LINE__ );
    }

    uxLastLowPriorityLoops2 = uxLowPriorityLoops2;

    return xErrorStatus;
}
