From ab3e96a6a9218eebf40f008e668a66fb85d3480e Mon Sep 17 00:00:00 2001 From: richardbarry Date: Sun, 27 Aug 2006 14:09:54 +0000 Subject: [PATCH] Changes ready for V4.1.0. git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@28 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- Demo/Common/Minimal/blocktim.c | 467 +++++++++++++++++++++++++++++++++ Demo/Common/include/blocktim.h | 41 +++ Demo/PC/main.c | 8 + Demo/PC/rtosdemo.tgt | 210 +++++++++------ Demo/PC/rtosdemo.wpj | 4 +- Source/include/projdefs.h | 10 +- Source/include/task.h | 25 ++ Source/queue.c | 293 +++++++++++---------- Source/tasks.c | 47 +++- 9 files changed, 870 insertions(+), 235 deletions(-) create mode 100644 Demo/Common/Minimal/blocktim.c create mode 100644 Demo/Common/include/blocktim.h diff --git a/Demo/Common/Minimal/blocktim.c b/Demo/Common/Minimal/blocktim.c new file mode 100644 index 000000000..754604c20 --- /dev/null +++ b/Demo/Common/Minimal/blocktim.c @@ -0,0 +1,467 @@ +/* + FreeRTOS.org V4.0.5 - Copyright (C) 2003-2006 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + *************************************************************************** +*/ + +/* + * This file contains some test scenarios that ensure tasks do not exit queue + * send or receive functions prematurely. A description of the tests is + * included within the code. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Task priorities. */ +#define bktPRIMARY_PRIORITY ( 3 ) +#define bktSECONDARY_PRIORITY ( 2 ) + +/* Task behaviour. */ +#define bktQUEUE_LENGTH ( 5 ) +#define bktSHORT_WAIT ( ( ( portTickType ) 20 ) / portTICK_RATE_MS ) +#define bktPRIMARY_BLOCK_TIME ( 10 ) +#define bktALLOWABLE_MARGIN ( 12 ) +#define bktTIME_TO_BLOCK ( 175 ) +#define bktDONT_BLOCK ( ( portTickType ) 0 ) +#define bktRUN_INDICATOR ( ( unsigned portBASE_TYPE ) 0x55 ) + +/* The queue on which the tasks block. */ +static xQueueHandle xTestQueue; + +/* Handle to the secondary task is required by the primary task for calls +to vTaskSuspend/Resume(). */ +static xTaskHandle xSecondary; + +/* Used to ensure that tasks are still executing without error. */ +static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0; +static portBASE_TYPE xErrorOccurred = pdFALSE; + +/* Provides a simple mechanism for the primary task to know when the +secondary task has executed. */ +static volatile unsigned portBASE_TYPE xRunIndicator; + +/* The two test tasks. Their behaviour is commented within the files. */ +static void vPrimaryBlockTimeTestTask( void *pvParameters ); +static void vSecondaryBlockTimeTestTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +void vCreateBlockTimeTasks( void ) +{ + /* Create the queue on which the two tasks block. */ + xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) ); + + /* Create the two test tasks. */ + xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL ); + xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary ); +} +/*-----------------------------------------------------------*/ + +static void vPrimaryBlockTimeTestTask( void *pvParameters ) +{ +portBASE_TYPE xItem, xData; +portTickType xTimeWhenBlocking; +portTickType xTimeToBlock, xBlockedTime; + + for( ;; ) + { + /********************************************************************* + Test 1 + + Simple block time wakeup test on queue receives. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is empty. Attempt to read from the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem; + + /* A critical section is used to minimise the jitter in the time + measurements. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + /********************************************************************* + Test 2 + + Simple block time wakeup test on queue sends. + + First fill the queue. It should be empty so all sends should pass. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is full. Attempt to write to the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem; + + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + + /********************************************************************* + Test 3 + + Wake the other task, it will block attempting to post to the queue. + When we read from the queue the other task will wake, but before it + can run we will post to the queue again. When the other task runs it + will find the queue still full, even though it was woken. It should + recognise that its block time has not expired and return to block for + the remains of its block time. + + Wake the other task so it blocks attempting to post to the already + full queue. */ + xRunIndicator = 0; + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + /* The other task has not yet executed. */ + vTaskDelay( bktSHORT_WAIT ); + } + /* Make sure the other task is blocked on the queue. */ + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we make space on the queue the other task should wake + but not execute as this task has higher priority. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now fill the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + full ourselves, and the other task have set xRunIndicator. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + + /* Set the priority back down. */ + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + + /********************************************************************* + Test 4 + + As per test 3 - but with the send and receive the other way around. + The other task blocks attempting to read from the queue. + + Empty the queue. We should find that it is full. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + /* Wake the other task so it blocks attempting to read from the + already empty queue. */ + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we place an item on the queue the other task should + wake but not execute as this task has higher priority. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now empty the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + empty ourselves, and the other task would be suspended. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + + xPrimaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void vSecondaryBlockTimeTestTask( void *pvParameters ) +{ +portTickType xTimeWhenBlocking, xBlockedTime; +portBASE_TYPE xData; + + for( ;; ) + { + /********************************************************************* + Test 1 and 2 + + This task does does not participate in these tests. */ + vTaskSuspend( NULL ); + + /********************************************************************* + Test 3 + + The first thing we do is attempt to read from the queue. It should be + full so we block. Note the time before we block so we can check the + wake time is as per that expected. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + anything on the queue. */ + xData = 0; + xRunIndicator = bktRUN_INDICATOR; + if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we inside the send function? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as + soon as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + /* Suspend ready for test 3. */ + xRunIndicator = bktRUN_INDICATOR; + vTaskSuspend( NULL ); + + /********************************************************************* + Test 4 + + As per test three, but with the send and receive reversed. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + anything on the queue. */ + xRunIndicator = bktRUN_INDICATOR; + if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as soon + as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + xRunIndicator = bktRUN_INDICATOR; + + xSecondaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void ) +{ +static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0; +portBASE_TYPE xReturn = pdPASS; + + /* Have both tasks performed at least one cycle since this function was + last called? */ + if( xPrimaryCycles == xLastPrimaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xSecondaryCycles == xLastSecondaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xErrorOccurred == pdTRUE ) + { + xReturn = pdFAIL; + } + + xLastSecondaryCycleCount = xSecondaryCycles; + xLastPrimaryCycleCount = xPrimaryCycles; + + return xReturn; +} diff --git a/Demo/Common/include/blocktim.h b/Demo/Common/include/blocktim.h new file mode 100644 index 000000000..762f29e55 --- /dev/null +++ b/Demo/Common/include/blocktim.h @@ -0,0 +1,41 @@ +/* + FreeRTOS.org V4.0.5 - Copyright (C) 2003-2006 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + *************************************************************************** +*/ + +#ifndef BLOCK_TIME_TEST_H +#define BLOCK_TIME_TEST_H + +void vCreateBlockTimeTasks( void ); +portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void ); + +#endif + + diff --git a/Demo/PC/main.c b/Demo/PC/main.c index fa64a4e31..3bd08d586 100644 --- a/Demo/PC/main.c +++ b/Demo/PC/main.c @@ -115,6 +115,7 @@ Changes from V3.2.4 #include "dynamic.h" #include "mevents.h" #include "crhook.h" +#include "blocktim.h" /* Priority definitions for the tasks in the demo application. */ #define mainLED_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) @@ -171,6 +172,7 @@ portSHORT main( void ) vStartComTestTasks( mainCOM_TEST_PRIORITY, serCOM1, ser115200 ); vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY ); vStartBlockingQueueTasks( mainQUEUE_BLOCK_PRIORITY ); + vCreateBlockTimeTasks(); vStartSemaphoreTasks( mainSEMAPHORE_TASK_PRIORITY ); vStartDynamicPriorityTasks(); @@ -380,6 +382,12 @@ static portSHORT sErrorHasOccurred = pdFALSE; sErrorHasOccurred = pdTRUE; } + if( xAreBlockTimeTestTasksStillRunning() != pdTRUE ) + { + vDisplayMessage( "Error in block time test tasks!\r\n" ); + sErrorHasOccurred = pdTRUE; + } + if( sErrorHasOccurred == pdFALSE ) { vDisplayMessage( "OK " ); diff --git a/Demo/PC/rtosdemo.tgt b/Demo/PC/rtosdemo.tgt index 469fba3a4..7b5f2d66d 100644 --- a/Demo/PC/rtosdemo.tgt +++ b/Demo/PC/rtosdemo.tgt @@ -75,7 +75,7 @@ WVList 0 19 WPickList -48 +50 20 MItem 3 @@ -706,8 +706,8 @@ WVList 0 168 MItem -27 -..\COMMON\MINIMAL\crflash.c +28 +..\COMMON\MINIMAL\blocktim.c 169 WString 4 @@ -724,8 +724,8 @@ WVList 0 172 MItem -26 -..\COMMON\MINIMAL\crhook.c +27 +..\COMMON\MINIMAL\crflash.c 173 WString 4 @@ -742,8 +742,8 @@ WVList 0 176 MItem -15 -fileio\fileio.c +26 +..\COMMON\MINIMAL\crhook.c 177 WString 4 @@ -760,8 +760,8 @@ WVList 0 180 MItem -6 -main.c +15 +fileio\fileio.c 181 WString 4 @@ -778,8 +778,8 @@ WVList 0 184 MItem -17 -partest\partest.c +6 +main.c 185 WString 4 @@ -796,8 +796,8 @@ WVList 0 188 MItem -15 -serial\serial.c +17 +partest\partest.c 189 WString 4 @@ -814,26 +814,26 @@ WVList 0 192 MItem -3 -*.h +15 +serial\serial.c 193 WString -3 -NIL +4 +COBJ 194 WVList 0 195 WVList 0 --1 +20 1 1 0 196 MItem -31 -..\..\SOURCE\INCLUDE\croutine.h +3 +*.h 197 WString 3 @@ -844,14 +844,14 @@ WVList 199 WVList 0 -192 -1 +-1 1 0 +0 200 MItem -27 -..\..\source\include\list.h +31 +..\..\SOURCE\INCLUDE\croutine.h 201 WString 3 @@ -862,14 +862,14 @@ WVList 203 WVList 0 -192 +196 1 1 0 204 MItem -31 -..\..\source\include\portable.h +27 +..\..\source\include\list.h 205 WString 3 @@ -880,14 +880,14 @@ WVList 207 WVList 0 -192 +196 1 1 0 208 MItem 31 -..\..\source\include\projdefs.h +..\..\source\include\portable.h 209 WString 3 @@ -898,14 +898,14 @@ WVList 211 WVList 0 -192 +196 1 1 0 212 MItem -28 -..\..\source\include\queue.h +31 +..\..\source\include\projdefs.h 213 WString 3 @@ -916,14 +916,14 @@ WVList 215 WVList 0 -192 +196 1 1 0 216 MItem -29 -..\..\source\include\semphr.h +28 +..\..\source\include\queue.h 217 WString 3 @@ -934,14 +934,14 @@ WVList 219 WVList 0 -192 +196 1 1 0 220 MItem -27 -..\..\source\include\task.h +29 +..\..\source\include\semphr.h 221 WString 3 @@ -952,14 +952,14 @@ WVList 223 WVList 0 -192 +196 1 1 0 224 MItem -55 -..\..\source\portable\owatcom\16bitdos\common\portasm.h +27 +..\..\source\include\task.h 225 WString 3 @@ -970,14 +970,14 @@ WVList 227 WVList 0 -192 +196 1 1 0 228 MItem -53 -..\..\source\portable\owatcom\16bitdos\pc\portmacro.h +55 +..\..\source\portable\owatcom\16bitdos\common\portasm.h 229 WString 3 @@ -988,14 +988,14 @@ WVList 231 WVList 0 -192 +196 1 1 0 232 MItem -26 -..\common\include\blockq.h +53 +..\..\source\portable\owatcom\16bitdos\pc\portmacro.h 233 WString 3 @@ -1006,14 +1006,14 @@ WVList 235 WVList 0 -192 +196 1 1 0 236 MItem -27 -..\common\include\comtest.h +26 +..\common\include\blockq.h 237 WString 3 @@ -1024,14 +1024,14 @@ WVList 239 WVList 0 -192 +196 1 1 0 240 MItem -26 -..\COMMON\INCLUDE\crhook.h +28 +..\COMMON\INCLUDE\blocktim.h 241 WString 3 @@ -1042,14 +1042,14 @@ WVList 243 WVList 0 -192 +196 1 1 0 244 MItem -25 -..\common\include\death.h +27 +..\common\include\comtest.h 245 WString 3 @@ -1060,14 +1060,14 @@ WVList 247 WVList 0 -192 +196 1 1 0 248 MItem -27 -..\COMMON\INCLUDE\dynamic.h +26 +..\COMMON\INCLUDE\crhook.h 249 WString 3 @@ -1078,14 +1078,14 @@ WVList 251 WVList 0 -192 +196 1 1 0 252 MItem -26 -..\common\include\fileio.h +25 +..\common\include\death.h 253 WString 3 @@ -1096,14 +1096,14 @@ WVList 255 WVList 0 -192 +196 1 1 0 256 MItem -25 -..\common\include\flash.h +27 +..\COMMON\INCLUDE\dynamic.h 257 WString 3 @@ -1114,14 +1114,14 @@ WVList 259 WVList 0 -192 +196 1 1 0 260 MItem -24 -..\common\include\flop.h +26 +..\common\include\fileio.h 261 WString 3 @@ -1132,14 +1132,14 @@ WVList 263 WVList 0 -192 +196 1 1 0 264 MItem -27 -..\common\include\partest.h +25 +..\common\include\flash.h 265 WString 3 @@ -1150,14 +1150,14 @@ WVList 267 WVList 0 -192 +196 1 1 0 268 MItem -25 -..\common\include\pollq.h +24 +..\common\include\flop.h 269 WString 3 @@ -1168,14 +1168,14 @@ WVList 271 WVList 0 -192 +196 1 1 0 272 MItem -25 -..\common\include\print.h +27 +..\common\include\partest.h 273 WString 3 @@ -1186,14 +1186,14 @@ WVList 275 WVList 0 -192 +196 1 1 0 276 MItem -27 -..\common\include\semtest.h +25 +..\common\include\pollq.h 277 WString 3 @@ -1204,14 +1204,14 @@ WVList 279 WVList 0 -192 +196 1 1 0 280 MItem -26 -..\common\include\serial.h +25 +..\common\include\print.h 281 WString 3 @@ -1222,14 +1222,14 @@ WVList 283 WVList 0 -192 +196 1 1 0 284 MItem -16 -FreeRTOSConfig.h +27 +..\common\include\semtest.h 285 WString 3 @@ -1240,7 +1240,43 @@ WVList 287 WVList 0 -192 +196 +1 +1 +0 +288 +MItem +26 +..\common\include\serial.h +289 +WString +3 +NIL +290 +WVList +0 +291 +WVList +0 +196 +1 +1 +0 +292 +MItem +16 +FreeRTOSConfig.h +293 +WString +3 +NIL +294 +WVList +0 +295 +WVList +0 +196 1 1 0 diff --git a/Demo/PC/rtosdemo.wpj b/Demo/PC/rtosdemo.wpj index 4c9ff8628..1b02cfb2f 100644 --- a/Demo/PC/rtosdemo.wpj +++ b/Demo/PC/rtosdemo.wpj @@ -31,7 +31,7 @@ WRect 0 0 7168 -7353 +7168 0 0 9 @@ -39,5 +39,5 @@ WFileName 12 rtosdemo.tgt 0 -19 +25 7 diff --git a/Source/include/projdefs.h b/Source/include/projdefs.h index 36c80215f..ff28ca50e 100644 --- a/Source/include/projdefs.h +++ b/Source/include/projdefs.h @@ -39,16 +39,18 @@ typedef void (*pdTASK_CODE)( void * ); #define pdTRUE ( 1 ) #define pdFALSE ( 0 ) -#define pdPASS ( 1 ) -#define pdFAIL ( 0 ) +#define pdPASS ( 1 ) +#define pdFAIL ( 0 ) +#define errQUEUE_EMPTY ( 0 ) +#define errQUEUE_FULL ( 0 ) /* Error definitions. */ #define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) #define errNO_TASK_TO_RUN ( -2 ) -#define errQUEUE_FULL ( -3 ) #define errQUEUE_BLOCKED ( -4 ) #define errQUEUE_YIELD ( -5 ) -#endif +#endif /* PROJDEFS_H */ + diff --git a/Source/include/task.h b/Source/include/task.h index 2b1dac8db..56ca25350 100644 --- a/Source/include/task.h +++ b/Source/include/task.h @@ -54,6 +54,15 @@ */ typedef void * xTaskHandle; +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + portBASE_TYPE xOverflowCount; + portTickType xTimeOnEntering; +} xTimeOutType; + /* * Defines the priority used by the idle task. This must not be modified. * @@ -919,6 +928,22 @@ inline void vTaskSwitchContext( void ); */ xTaskHandle xTaskGetCurrentTaskHandle( void ); +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( xTimeOutType *pxTimeOut ); + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType *pxTimeOut, portTickType *pxTicksToWait ); + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ); #endif /* TASK_H */ diff --git a/Source/queue.c b/Source/queue.c index 9bb773414..28850485d 100644 --- a/Source/queue.c +++ b/Source/queue.c @@ -54,6 +54,12 @@ Changes from V3.2.3 + Added the queue functions that can be used from co-routines. +Changes from V4.0.5 + + + Added a loop within xQueueSend() and xQueueReceive() to prevent the + functions exiting when a block time remains and the function has + not completed. + */ #include @@ -68,6 +74,7 @@ Changes from V3.2.3 /* Constants used with the cRxLock and cTxLock structure members. */ #define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 ) +#define queueERRONEOUS_UNBLOCK ( -1 ) /* * Definition of the queue used by the scheduler. @@ -128,7 +135,7 @@ signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, * to indicate that a task may require unblocking. When the queue in unlocked * these lock counts are inspected, and the appropriate action taken. */ -static signed portBASE_TYPE prvUnlockQueue( xQueueHandle pxQueue ); +static void prvUnlockQueue( xQueueHandle pxQueue ); /* * Uses a critical section to determine if there is any data in a queue. @@ -230,10 +237,14 @@ size_t xQueueSizeInBytes; signed portBASE_TYPE xQueueSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn; +xTimeOutType xTimeOut; /* Make sure other tasks do not access the queue. */ vTaskSuspendAll(); + /* Capture the current time status for future reference. */ + vTaskSetTimeOutState( &xTimeOut ); + /* It is important that this is the only thread/ISR that modifies the ready or delayed lists until xTaskResumeAll() is called. Places where the ready/delayed lists are modified include: @@ -272,108 +283,105 @@ signed portBASE_TYPE xReturn; */ /* If the queue is already full we may have to block. */ - if( prvIsQueueFull( pxQueue ) ) + do { - /* The queue is full - do we want to block or just leave without - posting? */ - if( xTicksToWait > ( portTickType ) 0 ) + if( prvIsQueueFull( pxQueue ) ) { - /* We are going to place ourselves on the xTasksWaitingToSend event - list, and will get woken should the delay expire, or space become - available on the queue. - - As detailed above we do not require mutual exclusion on the event - list as nothing else can modify it or the ready lists while we - have the scheduler suspended and queue locked. - - It is possible that an ISR has removed data from the queue since we - checked if any was available. If this is the case then the data - will have been copied from the queue, and the queue variables - updated, but the event list will not yet have been checked to see if - anything is waiting as the queue is locked. */ - vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); - - /* Force a context switch now as we are blocked. We can do - this from within a critical section as the task we are - switching to has its own context. When we return here (i.e. we - unblock) we will leave the critical section as normal. - - It is possible that an ISR has caused an event on an unrelated and - unlocked queue. If this was the case then the event list for that - queue will have been updated but the ready lists left unchanged - - instead the readied task will have been added to the pending ready - list. */ - taskENTER_CRITICAL(); + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( portTickType ) 0 ) { - /* We can safely unlock the queue and scheduler here as - interrupts are disabled. We must not yield with anything - locked, but we can yield from within a critical section. + /* We are going to place ourselves on the xTasksWaitingToSend event + list, and will get woken should the delay expire, or space become + available on the queue. + + As detailed above we do not require mutual exclusion on the event + list as nothing else can modify it or the ready lists while we + have the scheduler suspended and queue locked. + + It is possible that an ISR has removed data from the queue since we + checked if any was available. If this is the case then the data + will have been copied from the queue, and the queue variables + updated, but the event list will not yet have been checked to see if + anything is waiting as the queue is locked. */ + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Force a context switch now as we are blocked. We can do + this from within a critical section as the task we are + switching to has its own context. When we return here (i.e. we + unblock) we will leave the critical section as normal. - Tasks that have been placed on the pending ready list cannot - be tasks that are waiting for events on this queue. See - in comment xTaskRemoveFromEventList(). */ - prvUnlockQueue( pxQueue ); - - /* Resuming the scheduler may cause a yield. If so then there - is no point yielding again here. */ - if( !xTaskResumeAll() ) + It is possible that an ISR has caused an event on an unrelated and + unlocked queue. If this was the case then the event list for that + queue will have been updated but the ready lists left unchanged - + instead the readied task will have been added to the pending ready + list. */ + taskENTER_CRITICAL(); { - taskYIELD(); + /* We can safely unlock the queue and scheduler here as + interrupts are disabled. We must not yield with anything + locked, but we can yield from within a critical section. + + Tasks that have been placed on the pending ready list cannot + be tasks that are waiting for events on this queue. See + in comment xTaskRemoveFromEventList(). */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler may cause a yield. If so then there + is no point yielding again here. */ + if( !xTaskResumeAll() ) + { + taskYIELD(); + } + + /* Before leaving the critical section we have to ensure + exclusive access again. */ + vTaskSuspendAll(); + prvLockQueue( pxQueue ); } - - /* Before leaving the critical section we have to ensure - exclusive access again. */ - vTaskSuspendAll(); - prvLockQueue( pxQueue ); + taskEXIT_CRITICAL(); } - taskEXIT_CRITICAL(); } - } - - /* When we are here it is possible that we unblocked as space became - available on the queue. It is also possible that an ISR posted to the - queue since we left the critical section, so it may be that again there - is no space. This would only happen if a task and ISR post onto the - same queue. */ - taskENTER_CRITICAL(); - { - if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) - { - /* There is room in the queue, copy the data into the queue. */ - prvCopyQueueData( pxQueue, pvItemToQueue ); - xReturn = pdPASS; - - /* Update the TxLock count so prvUnlockQueue knows to check for - tasks waiting for data to become available in the queue. */ - ++( pxQueue->xTxLock ); - } - else + + /* When we are here it is possible that we unblocked as space became + available on the queue. It is also possible that an ISR posted to the + queue since we left the critical section, so it may be that again there + is no space. This would only happen if a task and ISR post onto the + same queue. */ + taskENTER_CRITICAL(); { - xReturn = errQUEUE_FULL; + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyQueueData( pxQueue, pvItemToQueue ); + xReturn = pdPASS; + + /* Update the TxLock count so prvUnlockQueue knows to check for + tasks waiting for data to become available in the queue. */ + ++( pxQueue->xTxLock ); + } + else + { + xReturn = errQUEUE_FULL; + } } - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); - /* We no longer require exclusive access to the queue. prvUnlockQueue - will remove any tasks suspended on a receive if either this function - or an ISR has posted onto the queue. */ - if( prvUnlockQueue( pxQueue ) ) - { - /* Resume the scheduler - making ready any tasks that were woken - by an event while the scheduler was locked. Resuming the - scheduler may cause a yield, in which case there is no point - yielding again here. */ - if( !xTaskResumeAll() ) + if( xReturn == errQUEUE_FULL ) { - taskYIELD(); + if( xTicksToWait > 0 ) + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + xReturn = queueERRONEOUS_UNBLOCK; + } + } } } - else - { - /* Resume the scheduler - making ready any tasks that were woken - by an event while the scheduler was locked. */ - xTaskResumeAll(); - } + while( xReturn == queueERRONEOUS_UNBLOCK ); + + prvUnlockQueue( pxQueue ); + xTaskResumeAll(); return xReturn; } @@ -424,6 +432,7 @@ signed portBASE_TYPE xQueueSendFromISR( xQueueHandle pxQueue, const void *pvItem signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn; +xTimeOutType xTimeOut; /* This function is very similar to xQueueSend(). See comments within xQueueSend() for a more detailed explanation. @@ -431,68 +440,76 @@ signed portBASE_TYPE xReturn; Make sure other tasks do not access the queue. */ vTaskSuspendAll(); + /* Capture the current time status for future reference. */ + vTaskSetTimeOutState( &xTimeOut ); + /* Make sure interrupts do not access the queue. */ prvLockQueue( pxQueue ); - /* If there are no messages in the queue we may have to block. */ - if( prvIsQueueEmpty( pxQueue ) ) + do { - /* There are no messages in the queue, do we want to block or just - leave with nothing? */ - if( xTicksToWait > ( portTickType ) 0 ) + /* If there are no messages in the queue we may have to block. */ + if( prvIsQueueEmpty( pxQueue ) ) { - vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); - taskENTER_CRITICAL(); + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( portTickType ) 0 ) { - prvUnlockQueue( pxQueue ); - if( !xTaskResumeAll() ) + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + taskENTER_CRITICAL(); { - taskYIELD(); + prvUnlockQueue( pxQueue ); + if( !xTaskResumeAll() ) + { + taskYIELD(); + } + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); } - - vTaskSuspendAll(); - prvLockQueue( pxQueue ); + taskEXIT_CRITICAL(); } - taskEXIT_CRITICAL(); } - } - - taskENTER_CRITICAL(); - { - if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + + taskENTER_CRITICAL(); { - pxQueue->pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) { - pxQueue->pcReadFrom = pxQueue->pcHead; + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + /* Increment the lock count so prvUnlockQueue knows to check for + tasks waiting for space to become available on the queue. */ + ++( pxQueue->xRxLock ); + xReturn = pdPASS; + } + else + { + xReturn = errQUEUE_EMPTY; } - --( pxQueue->uxMessagesWaiting ); - memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); - - /* Increment the lock count so prvUnlockQueue knows to check for - tasks waiting for space to become available on the queue. */ - ++( pxQueue->xRxLock ); - xReturn = pdPASS; } - else + taskEXIT_CRITICAL(); + + if( xReturn == errQUEUE_EMPTY ) { - xReturn = pdFAIL; + if( xTicksToWait > 0 ) + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + xReturn = queueERRONEOUS_UNBLOCK; + } + } } - } - taskEXIT_CRITICAL(); + } while( xReturn == queueERRONEOUS_UNBLOCK ); /* We no longer require exclusive access to the queue. */ - if( prvUnlockQueue( pxQueue ) ) - { - if( !xTaskResumeAll() ) - { - taskYIELD(); - } - } - else - { - xTaskResumeAll(); - } + prvUnlockQueue( pxQueue ); + xTaskResumeAll(); return xReturn; } @@ -571,10 +588,8 @@ void vQueueDelete( xQueueHandle pxQueue ) } /*-----------------------------------------------------------*/ -static signed portBASE_TYPE prvUnlockQueue( xQueueHandle pxQueue ) +static void prvUnlockQueue( xQueueHandle pxQueue ) { -signed portBASE_TYPE xYieldRequired = pdFALSE; - /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ /* The lock counts contains the number of extra data items placed or @@ -600,7 +615,7 @@ signed portBASE_TYPE xYieldRequired = pdFALSE; { /* The task waiting has a higher priority so record that a context switch is required. */ - xYieldRequired = pdTRUE; + vTaskMissedYield(); } } } @@ -620,14 +635,12 @@ signed portBASE_TYPE xYieldRequired = pdFALSE; { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) { - xYieldRequired = pdTRUE; + vTaskMissedYield(); } } } } taskEXIT_CRITICAL(); - - return xYieldRequired; } /*-----------------------------------------------------------*/ diff --git a/Source/tasks.c b/Source/tasks.c index 74c593035..30e00f942 100644 --- a/Source/tasks.c +++ b/Source/tasks.c @@ -176,6 +176,11 @@ Changed from V4.0.4 This has not been necessary since V4.0.1 when the xMissedYield handling was added. + Implement xTaskResumeFromISR(). + +Changes from V4.0.5 + + + Added utility functions and xOverflowCount variable to facilitate the + queue.c changes. */ #include @@ -261,13 +266,13 @@ static xList xPendingReadyList; /*< Tasks that have been readied while the /* File private variables. --------------------------------*/ static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks = ( unsigned portBASE_TYPE ) 0; static volatile portTickType xTickCount = ( portTickType ) 0; -static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY; +static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY; static volatile unsigned portBASE_TYPE uxTopReadyPriority = tskIDLE_PRIORITY; static volatile signed portBASE_TYPE xSchedulerRunning = pdFALSE; static volatile unsigned portBASE_TYPE uxSchedulerSuspended = ( unsigned portBASE_TYPE ) pdFALSE; static volatile unsigned portBASE_TYPE uxMissedTicks = ( unsigned portBASE_TYPE ) 0; static volatile portBASE_TYPE xMissedYield = ( portBASE_TYPE ) pdFALSE; - +static volatile portBASE_TYPE xNumOfOverflows = ( portBASE_TYPE ) 0; /* Debugging and trace facilities private variables and macros. ------------*/ /* @@ -1281,6 +1286,7 @@ inline void vTaskIncrementTick( void ) pxTemp = pxDelayedTaskList; pxDelayedTaskList = pxOverflowDelayedTaskList; pxOverflowDelayedTaskList = pxTemp; + xNumOfOverflows++; } /* See if this tick has made a timeout expire. */ @@ -1481,10 +1487,47 @@ portBASE_TYPE xReturn; return xReturn; } +/*-----------------------------------------------------------*/ +void vTaskSetTimeOutState( xTimeOutType *pxTimeOut ) +{ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType *pxTimeOut, portTickType *pxTicksToWait ) +{ +portBASE_TYPE xReturn; + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xTickCount > pxTimeOut->xTimeOnEntering ) ) + { + /* The tick count is greater than the time at which vTaskSetTimeout() + was called, but has also overflowed since vTaskSetTimeOut() was called. + It must have wrapped all the way around and gone past us again. This + passed since vTaskSetTimeout() was called. */ + xReturn = pdTRUE; + } + else if( ( xTickCount - pxTimeOut->xTimeOnEntering ) < *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= ( xTickCount - pxTimeOut->xTimeOnEntering ); + vTaskSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ +void vTaskMissedYield( void ) +{ + xMissedYield = pdTRUE; +} /* * ----------------------------------------------------------- -- 2.39.5