FreeRTOS Real Time Operating System, Modified from Kenji Arai's initial port. See freertos.org for full documentation.

Fork of FreeRTOS_on_mbed_v1 by Kenji Arai

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers blocktim.c Source File

blocktim.c

00001 /*
00002     FreeRTOS V6.0.3 - Copyright (C) 2010 Real Time Engineers Ltd.
00003 
00004     ***************************************************************************
00005     *                                                                         *
00006     * If you are:                                                             *
00007     *                                                                         *
00008     *    + New to FreeRTOS,                                                   *
00009     *    + Wanting to learn FreeRTOS or multitasking in general quickly       *
00010     *    + Looking for basic training,                                        *
00011     *    + Wanting to improve your FreeRTOS skills and productivity           *
00012     *                                                                         *
00013     * then take a look at the FreeRTOS eBook                                  *
00014     *                                                                         *
00015     *        "Using the FreeRTOS Real Time Kernel - a Practical Guide"        *
00016     *                  http://www.FreeRTOS.org/Documentation                  *
00017     *                                                                         *
00018     * A pdf reference manual is also available.  Both are usually delivered   *
00019     * to your inbox within 20 minutes to two hours when purchased between 8am *
00020     * and 8pm GMT (although please allow up to 24 hours in case of            *
00021     * exceptional circumstances).  Thank you for your support!                *
00022     *                                                                         *
00023     ***************************************************************************
00024     
00025     This file is part of the FreeRTOS distribution.
00026 
00027     FreeRTOS is free software; you can redistribute it and/or modify it under
00028     the terms of the GNU General Public License (version 2) as published by the
00029     Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
00030     ***NOTE*** The exception to the GPL is included to allow you to distribute
00031     a combined work that includes FreeRTOS without being obliged to provide the
00032     source code for proprietary components outside of the FreeRTOS kernel.
00033     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
00034     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00035     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00036     more details. You should have received a copy of the GNU General Public 
00037     License and the FreeRTOS license exception along with FreeRTOS; if not it 
00038     can be viewed here: http://www.freertos.org/a00114.html and also obtained 
00039     by writing to Richard Barry, contact details for whom are available on the
00040     FreeRTOS WEB site.
00041 
00042     1 tab == 4 spaces!
00043 
00044     http://www.FreeRTOS.org - Documentation, latest information, license and
00045     contact details.
00046 
00047     http://www.SafeRTOS.com - A version that is certified for use in safety
00048     critical systems.
00049 
00050     http://www.OpenRTOS.com - Commercial support, development, porting,
00051     licensing and training services.
00052 */
00053 
00054 /*
00055  * This file contains some test scenarios that ensure tasks do not exit queue
00056  * send or receive functions prematurely.  A description of the tests is
00057  * included within the code.
00058  */
00059 
00060 /* Kernel includes. */
00061 #include "FreeRTOS.h"
00062 #include "task.h"
00063 #include "queue.h"
00064 
00065 /* Demo includes. */
00066 #include "blocktim.h"
00067 
00068 /* Task priorities.  Allow these to be overridden. */
00069 #ifndef bktPRIMARY_PRIORITY
00070     #define bktPRIMARY_PRIORITY            ( 3 )
00071 #endif
00072 
00073 #ifndef bktSECONDARY_PRIORITY
00074     #define bktSECONDARY_PRIORITY        ( 2 )
00075 #endif
00076 
00077 /* Task behaviour. */
00078 #define bktQUEUE_LENGTH                ( 5 )
00079 #define bktSHORT_WAIT                ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )
00080 #define bktPRIMARY_BLOCK_TIME        ( 10 )
00081 #define bktALLOWABLE_MARGIN            ( 15 )
00082 #define bktTIME_TO_BLOCK            ( 175 )
00083 #define bktDONT_BLOCK                ( ( portTickType ) 0 )
00084 #define bktRUN_INDICATOR            ( ( unsigned portBASE_TYPE ) 0x55 )
00085 
00086 /* The queue on which the tasks block. */
00087 static xQueueHandle xTestQueue;
00088 
00089 /* Handle to the secondary task is required by the primary task for calls
00090 to vTaskSuspend/Resume(). */
00091 static xTaskHandle xSecondary;
00092 
00093 /* Used to ensure that tasks are still executing without error. */
00094 static volatile portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;
00095 static volatile portBASE_TYPE xErrorOccurred = pdFALSE;
00096 
00097 /* Provides a simple mechanism for the primary task to know when the
00098 secondary task has executed. */
00099 static volatile unsigned portBASE_TYPE xRunIndicator;
00100 
00101 /* The two test tasks.  Their behaviour is commented within the files. */
00102 static void vPrimaryBlockTimeTestTask( void *pvParameters );
00103 static void vSecondaryBlockTimeTestTask( void *pvParameters );
00104 
00105 /*-----------------------------------------------------------*/
00106 
00107 void vCreateBlockTimeTasks( void )
00108 {
00109     /* Create the queue on which the two tasks block. */
00110     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );
00111 
00112     /* vQueueAddToRegistry() adds the queue to the queue registry, if one is
00113     in use.  The queue registry is provided as a means for kernel aware
00114     debuggers to locate queues and has no purpose if a kernel aware debugger
00115     is not being used.  The call to vQueueAddToRegistry() will be removed
00116     by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
00117     defined to be less than 1. */
00118     vQueueAddToRegistry( xTestQueue, ( signed char * ) "Block_Time_Queue" );
00119 
00120     /* Create the two test tasks. */
00121     xTaskCreate( vPrimaryBlockTimeTestTask, ( signed char * )"BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
00122     xTaskCreate( vSecondaryBlockTimeTestTask, ( signed char * )"BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
00123 }
00124 /*-----------------------------------------------------------*/
00125 
00126 static void vPrimaryBlockTimeTestTask( void *pvParameters )
00127 {
00128 portBASE_TYPE xItem, xData;
00129 portTickType xTimeWhenBlocking;
00130 portTickType xTimeToBlock, xBlockedTime;
00131 
00132     ( void ) pvParameters;
00133 
00134     for( ;; )
00135     {
00136         /*********************************************************************
00137         Test 1
00138 
00139         Simple block time wakeup test on queue receives. */
00140         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00141         {
00142             /* The queue is empty. Attempt to read from the queue using a block
00143             time.  When we wake, ensure the delta in time is as expected. */
00144             xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;
00145 
00146             xTimeWhenBlocking = xTaskGetTickCount();
00147 
00148             /* We should unblock after xTimeToBlock having not received
00149             anything on the queue. */
00150             if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
00151             {
00152                 xErrorOccurred = pdTRUE;
00153             }
00154 
00155             /* How long were we blocked for? */
00156             xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
00157 
00158             if( xBlockedTime < xTimeToBlock )
00159             {
00160                 /* Should not have blocked for less than we requested. */
00161                 xErrorOccurred = pdTRUE;
00162             }
00163 
00164             if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
00165             {
00166                 /* Should not have blocked for longer than we requested,
00167                 although we would not necessarily run as soon as we were
00168                 unblocked so a margin is allowed. */
00169                 xErrorOccurred = pdTRUE;
00170             }
00171         }
00172 
00173         /*********************************************************************
00174         Test 2
00175 
00176         Simple block time wakeup test on queue sends.
00177 
00178         First fill the queue.  It should be empty so all sends should pass. */
00179         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00180         {
00181             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
00182             {
00183                 xErrorOccurred = pdTRUE;
00184             }
00185 
00186             #if configUSE_PREEMPTION == 0
00187                 taskYIELD();
00188             #endif
00189         }
00190 
00191         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00192         {
00193             /* The queue is full. Attempt to write to the queue using a block
00194             time.  When we wake, ensure the delta in time is as expected. */
00195             xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;
00196 
00197             xTimeWhenBlocking = xTaskGetTickCount();
00198 
00199             /* We should unblock after xTimeToBlock having not received
00200             anything on the queue. */
00201             if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
00202             {
00203                 xErrorOccurred = pdTRUE;
00204             }
00205 
00206             /* How long were we blocked for? */
00207             xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
00208 
00209             if( xBlockedTime < xTimeToBlock )
00210             {
00211                 /* Should not have blocked for less than we requested. */
00212                 xErrorOccurred = pdTRUE;
00213             }
00214 
00215             if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
00216             {
00217                 /* Should not have blocked for longer than we requested,
00218                 although we would not necessarily run as soon as we were
00219                 unblocked so a margin is allowed. */
00220                 xErrorOccurred = pdTRUE;
00221             }
00222         }
00223 
00224         /*********************************************************************
00225         Test 3
00226 
00227         Wake the other task, it will block attempting to post to the queue.
00228         When we read from the queue the other task will wake, but before it
00229         can run we will post to the queue again.  When the other task runs it
00230         will find the queue still full, even though it was woken.  It should
00231         recognise that its block time has not expired and return to block for
00232         the remains of its block time.
00233 
00234         Wake the other task so it blocks attempting to post to the already
00235         full queue. */
00236         xRunIndicator = 0;
00237         vTaskResume( xSecondary );
00238 
00239         /* We need to wait a little to ensure the other task executes. */
00240         while( xRunIndicator != bktRUN_INDICATOR )
00241         {
00242             /* The other task has not yet executed. */
00243             vTaskDelay( bktSHORT_WAIT );
00244         }
00245         /* Make sure the other task is blocked on the queue. */
00246         vTaskDelay( bktSHORT_WAIT );
00247         xRunIndicator = 0;
00248 
00249         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00250         {
00251             /* Now when we make space on the queue the other task should wake
00252             but not execute as this task has higher priority. */
00253             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
00254             {
00255                 xErrorOccurred = pdTRUE;
00256             }
00257 
00258             /* Now fill the queue again before the other task gets a chance to
00259             execute.  If the other task had executed we would find the queue
00260             full ourselves, and the other task have set xRunIndicator. */
00261             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
00262             {
00263                 xErrorOccurred = pdTRUE;
00264             }
00265 
00266             if( xRunIndicator == bktRUN_INDICATOR )
00267             {
00268                 /* The other task should not have executed. */
00269                 xErrorOccurred = pdTRUE;
00270             }
00271 
00272             /* Raise the priority of the other task so it executes and blocks
00273             on the queue again. */
00274             vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
00275 
00276             /* The other task should now have re-blocked without exiting the
00277             queue function. */
00278             if( xRunIndicator == bktRUN_INDICATOR )
00279             {
00280                 /* The other task should not have executed outside of the
00281                 queue function. */
00282                 xErrorOccurred = pdTRUE;
00283             }
00284 
00285             /* Set the priority back down. */
00286             vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
00287         }
00288 
00289         /* Let the other task timeout.  When it unblockes it will check that it
00290         unblocked at the correct time, then suspend itself. */
00291         while( xRunIndicator != bktRUN_INDICATOR )
00292         {
00293             vTaskDelay( bktSHORT_WAIT );
00294         }
00295         vTaskDelay( bktSHORT_WAIT );
00296         xRunIndicator = 0;
00297 
00298 
00299         /*********************************************************************
00300         Test 4
00301 
00302         As per test 3 - but with the send and receive the other way around.
00303         The other task blocks attempting to read from the queue.
00304 
00305         Empty the queue.  We should find that it is full. */
00306         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00307         {
00308             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
00309             {
00310                 xErrorOccurred = pdTRUE;
00311             }
00312         }
00313 
00314         /* Wake the other task so it blocks attempting to read from  the
00315         already    empty queue. */
00316         vTaskResume( xSecondary );
00317 
00318         /* We need to wait a little to ensure the other task executes. */
00319         while( xRunIndicator != bktRUN_INDICATOR )
00320         {
00321             vTaskDelay( bktSHORT_WAIT );
00322         }
00323         vTaskDelay( bktSHORT_WAIT );
00324         xRunIndicator = 0;
00325 
00326         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
00327         {
00328             /* Now when we place an item on the queue the other task should
00329             wake but not execute as this task has higher priority. */
00330             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
00331             {
00332                 xErrorOccurred = pdTRUE;
00333             }
00334 
00335             /* Now empty the queue again before the other task gets a chance to
00336             execute.  If the other task had executed we would find the queue
00337             empty ourselves, and the other task would be suspended. */
00338             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
00339             {
00340                 xErrorOccurred = pdTRUE;
00341             }
00342 
00343             if( xRunIndicator == bktRUN_INDICATOR )
00344             {
00345                 /* The other task should not have executed. */
00346                 xErrorOccurred = pdTRUE;
00347             }
00348 
00349             /* Raise the priority of the other task so it executes and blocks
00350             on the queue again. */
00351             vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
00352 
00353             /* The other task should now have re-blocked without exiting the
00354             queue function. */
00355             if( xRunIndicator == bktRUN_INDICATOR )
00356             {
00357                 /* The other task should not have executed outside of the
00358                 queue function. */
00359                 xErrorOccurred = pdTRUE;
00360             }
00361             vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
00362         }
00363 
00364         /* Let the other task timeout.  When it unblockes it will check that it
00365         unblocked at the correct time, then suspend itself. */
00366         while( xRunIndicator != bktRUN_INDICATOR )
00367         {
00368             vTaskDelay( bktSHORT_WAIT );
00369         }
00370         vTaskDelay( bktSHORT_WAIT );
00371 
00372         xPrimaryCycles++;
00373     }
00374 }
00375 /*-----------------------------------------------------------*/
00376 
00377 static void vSecondaryBlockTimeTestTask( void *pvParameters )
00378 {
00379 portTickType xTimeWhenBlocking, xBlockedTime;
00380 portBASE_TYPE xData;
00381 
00382     ( void ) pvParameters;
00383 
00384     for( ;; )
00385     {
00386         /*********************************************************************
00387         Test 1 and 2
00388 
00389         This task does does not participate in these tests. */
00390         vTaskSuspend( NULL );
00391 
00392         /*********************************************************************
00393         Test 3
00394 
00395         The first thing we do is attempt to read from the queue.  It should be
00396         full so we block.  Note the time before we block so we can check the
00397         wake time is as per that expected. */
00398         xTimeWhenBlocking = xTaskGetTickCount();
00399 
00400         /* We should unblock after bktTIME_TO_BLOCK having not sent
00401         anything to the queue. */
00402         xData = 0;
00403         xRunIndicator = bktRUN_INDICATOR;
00404         if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
00405         {
00406             xErrorOccurred = pdTRUE;
00407         }
00408 
00409         /* How long were we inside the send function? */
00410         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
00411 
00412         /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
00413         if( xBlockedTime < bktTIME_TO_BLOCK )
00414         {
00415             xErrorOccurred = pdTRUE;
00416         }
00417 
00418         /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
00419         either.  A margin is permitted as we would not necessarily run as
00420         soon as we unblocked. */
00421         if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
00422         {
00423             xErrorOccurred = pdTRUE;
00424         }
00425 
00426         /* Suspend ready for test 3. */
00427         xRunIndicator = bktRUN_INDICATOR;
00428         vTaskSuspend( NULL );
00429 
00430         /*********************************************************************
00431         Test 4
00432 
00433         As per test three, but with the send and receive reversed. */
00434         xTimeWhenBlocking = xTaskGetTickCount();
00435 
00436         /* We should unblock after bktTIME_TO_BLOCK having not received
00437         anything on the queue. */
00438         xRunIndicator = bktRUN_INDICATOR;
00439         if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
00440         {
00441             xErrorOccurred = pdTRUE;
00442         }
00443 
00444         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
00445 
00446         /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
00447         if( xBlockedTime < bktTIME_TO_BLOCK )
00448         {
00449             xErrorOccurred = pdTRUE;
00450         }
00451 
00452         /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
00453         either.  A margin is permitted as we would not necessarily run as soon
00454         as we unblocked. */
00455         if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
00456         {
00457             xErrorOccurred = pdTRUE;
00458         }
00459 
00460         xRunIndicator = bktRUN_INDICATOR;
00461 
00462         xSecondaryCycles++;
00463     }
00464 }
00465 /*-----------------------------------------------------------*/
00466 
00467 portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void )
00468 {
00469 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
00470 portBASE_TYPE xReturn = pdPASS;
00471 
00472     /* Have both tasks performed at least one cycle since this function was
00473     last called? */
00474     if( xPrimaryCycles == xLastPrimaryCycleCount )
00475     {
00476         xReturn = pdFAIL;
00477     }
00478 
00479     if( xSecondaryCycles == xLastSecondaryCycleCount )
00480     {
00481         xReturn = pdFAIL;
00482     }
00483 
00484     if( xErrorOccurred == pdTRUE )
00485     {
00486         xReturn = pdFAIL;
00487     }
00488 
00489     xLastSecondaryCycleCount = xSecondaryCycles;
00490     xLastPrimaryCycleCount = xPrimaryCycles;
00491 
00492     return xReturn;
00493 }