diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/.settings/language.settings.xml b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/.settings/language.settings.xml index 782b97ca1..aa36f4810 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/.settings/language.settings.xml +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/main.c b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/main.c index bde06ef93..a18b36663 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/main.c +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_IoT_SDK/TaskPool/main.c @@ -29,14 +29,24 @@ #include "FreeRTOS.h" #include "task.h" +/* Standard includes. */ +#include + /* IoT SDK includes. */ #include "iot_taskpool.h" +/* The priority at which that tasks in the task pool (the worker tasks) get +created. */ +#define tpTASK_POOL_WORKER_PRIORITY 1 + /* * Prototypes for the functions that demonstrate the task pool API. */ static void prvExample_BasicSingleJob( void ); +static void prvExample_DeferredSingleJob( void ); static void prvExample_BasicRecyclableJob( void ); +static void prvExample_ReuseRecyclableJobFromLowPriorityTask( void ); +static void prvExample_ReuseRecyclableJobFromHighPriorityTask( void ); /* Prototypes of the callback functions used in the examples. */ static void prvSimpleTaskNotifyCallback( IotTaskPool_t pTaskPool, IotTaskPoolJob_t pJob, void *pUserContext ); @@ -65,7 +75,7 @@ static const IotTaskPoolInfo_t xTaskPoolParameters = { /* Stack size for every task pool thread - in words, not bytes. */ configMINIMAL_STACK_SIZE, /* Priority for every task pool thread. */ - tskIDLE_PRIORITY, + tpTASK_POOL_WORKER_PRIORITY, }; /*-----------------------------------------------------------*/ @@ -74,12 +84,12 @@ int main( void ) { /* This example uses a single application task, which in turn is used to create and send jobs to task pool tasks. */ - xTaskCreate( prvTaskPoolDemoTask, - "PoolDemo", - configMINIMAL_STACK_SIZE, - NULL, - tskIDLE_PRIORITY, - NULL ); + xTaskCreate( prvTaskPoolDemoTask, /* Function that implements the task. */ + "PoolDemo", /* Text name for the task - only used for debugging. */ + configMINIMAL_STACK_SIZE, /* Size of stack (in words, not bytes) to allocate for the task. */ + NULL, /* Task parameter - not used in this case. */ + tskIDLE_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */ + NULL ); /* Used to pass out a handle to the created tsak - not used in this case. */ vTaskStartScheduler(); @@ -93,27 +103,61 @@ int main( void ) static void prvTaskPoolDemoTask( void *pvParameters ) { IotTaskPoolError_t xResult; +uint32_t ulLoops; /* Remove compiler warnings about unused parameters. */ ( void ) pvParameters; /* The task pool must be created before it can be used. */ -// xResult = IotTaskPool_CreateSystemTaskPool( &xTaskPoolParameters ); -// configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + xResult = IotTaskPool_CreateSystemTaskPool( &xTaskPoolParameters ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); /* Attempting to create the task pool again should then appear to succeed (in case it is initialised by more than one library), but have no effect. */ -// xResult = IotTaskPool_CreateSystemTaskPool( &xTaskPoolParameters ); -// configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + xResult = IotTaskPool_CreateSystemTaskPool( &xTaskPoolParameters ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); for( ;; ) { - /* Run through each task pool example in turn. See the comments in the - below functions for details of their behaviour. */ + /* Demonstrate the most basic use case where a non persistent job is + created and scheduled to run immediately. The task pool worker tasks + (in which the job callback function executes) have a priority above the + priority of this task so the job's callback executes as soon as it is + scheduled. */ prvExample_BasicSingleJob(); + + /* Demonstrate a job being scheduled to run at some time in the + future, and how a job scheduled to run in the future can be cancelled if + it has not yet started executing. */ + prvExample_DeferredSingleJob(); + + /* Demonstrate the most basic use of a recyclable job. This is similar + to prvExample_BasicSingleJob() but using a recyclable job. Creating a + recyclable job will re-use a previously created and now spare job from + the task pool's job cache if one is available, or otherwise dynamically + create a new job if a spare job is not available in the cache but space + remains in the cache. */ prvExample_BasicRecyclableJob(); - vTaskDelete( NULL ); + /* Demonstrate multiple recyclable jobs being created, used, and then + re-used. In this the task pool worker tasks (in which the job callback + functions execute) have a priority above the priority of this task so + the job's callback functions execute as soon as they are scheduled. */ + prvExample_ReuseRecyclableJobFromLowPriorityTask(); + + /* Again demonstrate multiple recyclable jobs being used, but this time + the priority of the task pool worker tasks (in which the job callback + functions execute) are lower than the priority of this task so the job's + callback functions don't execute until this task enteres the blocked + state. */ + prvExample_ReuseRecyclableJobFromHighPriorityTask(); + + ulLoops++; + if( ( ulLoops % 10UL ) == 0 ) + { + printf( "Performed %u successful iterations.\r\n", ulLoops ); + fflush( stdout ); + } } } /*-----------------------------------------------------------*/ @@ -137,10 +181,13 @@ IotTaskPoolJobStorage_t xJobStorage; IotTaskPoolJob_t xJob; IotTaskPoolError_t xResult; uint32_t ulReturn; -const TickType_t xShortDelay = pdMS_TO_TICKS( 200 ); +const uint32_t ulNoFlags = 0UL; +const TickType_t xNoDelay = ( TickType_t ) 0; +size_t xFreeHeapBeforeCreatingJob = xPortGetFreeHeapSize(); +IotTaskPoolJobStatus_t xJobStatus; - /* Ensure the notification count is 0 before scheduling the job. */ - while( ulTaskNotifyTake( pdTRUE, 0 ) != 0 ); + /* Don't expect any notifications to be pending yet. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); /* Create and schedule a job using the handle of this task as the job's context and the function that sends a notification to the task handle as @@ -152,12 +199,111 @@ const TickType_t xShortDelay = pdMS_TO_TICKS( 200 ); &xJob ); configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); - xResult = IotTaskPool_ScheduleSystemJob( xJob, 0 ); + /* The job has been created but not scheduled so is now ready. */ + IotTaskPool_GetStatus( NULL, xJob, &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_READY ); + + /* This is not a persistent (recyclable) job and its storage is on the + stack of this function, so the amount of heap space available should not + have chanced since entering this function. */ + configASSERT( xFreeHeapBeforeCreatingJob == xPortGetFreeHeapSize() ); + + /* In the full task pool implementation the first parameter is used to + pass the handle of the task pool to schedule. The lean task pool + implementation used in this demo only supports a single task pool, which + is created internally within the library, so the first parameter is NULL. */ + xResult = IotTaskPool_Schedule( NULL, xJob, ulNoFlags ); configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); - /* Wait for the notification coming from the job's callback function. */ - ulReturn = ulTaskNotifyTake( pdTRUE, xShortDelay ); + /* Look for the notification coming from the job's callback function. The + priority of the task pool worker task that executes the callback is higher + than the priority of this task so a block time is not needed - the task pool + worker task pre-empts this task and sends the notification (from the job's + callback) as soon as the job is scheduled. */ + ulReturn = ulTaskNotifyTake( pdTRUE, xNoDelay ); configASSERT( ulReturn ); + + /* The job's callback has executed so the job has now completed. */ + IotTaskPool_GetStatus( NULL, xJob, &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_COMPLETED ); +} +/*-----------------------------------------------------------*/ + +static void prvExample_DeferredSingleJob( void ) +{ +IotTaskPoolJobStorage_t xJobStorage; +IotTaskPoolJob_t xJob; +IotTaskPoolError_t xResult; +uint32_t ulReturn; +const uint32_t ulShortDelay_ms = 100UL; +const TickType_t xNoDelay = ( TickType_t ) 0, xAllowableMargin = ( TickType_t ) 5; /* Large margin for Windows port, which is not real time. */ +TickType_t xTimeBefore, xElapsedTime, xShortDelay_ticks; +size_t xFreeHeapBeforeCreatingJob = xPortGetFreeHeapSize(); +IotTaskPoolJobStatus_t xJobStatus; + + /* Don't expect any notifications to be pending yet. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); + + /* Create a job using the handle of this task as the job's context and the + function that sends a notification to the task handle as the jobs callback + function. The job is created using storage allocated on the stack of this + function - so no memory is allocated. */ + xResult = IotTaskPool_CreateJob( prvSimpleTaskNotifyCallback, /* Callback function. */ + ( void * ) xTaskGetCurrentTaskHandle(), /* Job context. */ + &xJobStorage, + &xJob ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* The job has been created but not scheduled so is now ready. */ + IotTaskPool_GetStatus( NULL, xJob, &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_READY ); + + /* This is not a persistent (recyclable) job and its storage is on the + stack of this function, so the amount of heap space available should not + have chanced since entering this function. */ + configASSERT( xFreeHeapBeforeCreatingJob == xPortGetFreeHeapSize() ); + + /* Schedule the job to run its callback in xShortDelay_ms milliseconds time. + In the full task pool implementation the first parameter is used to pass the + handle of the task pool to schedule. The lean task pool implementation used + in this demo only supports a single task pool, which is created internally + within the library, so the first parameter is NULL. */ + xResult = IotTaskPool_ScheduleDeferred( NULL, xJob, ulShortDelay_ms ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* The scheduled job should not have executed yet, so don't expect any + notifications and expect the job's status to be 'deferred'. */ + ulReturn = ulTaskNotifyTake( pdTRUE, xNoDelay ); + configASSERT( ulReturn == 0 ); + IotTaskPool_GetStatus( NULL, xJob, &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_DEFERRED ); + + /* As the job has not yet been executed it can be stopped. */ + xResult = IotTaskPool_TryCancel( NULL, xJob, &xJobStatus ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + IotTaskPool_GetStatus( NULL, xJob, &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_CANCELED ); + + /* Schedule the job again, and this time wait until its callback is + executed (the callback function sends a notification to this task) to see + that it executes at the right time. */ + xTimeBefore = xTaskGetTickCount(); + xResult = IotTaskPool_ScheduleDeferred( NULL, xJob, ulShortDelay_ms ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* Wait twice the deferred execution time to ensure the callback is executed + before the call below times out. */ + ulReturn = ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( ulShortDelay_ms * 2UL ) ); + xElapsedTime = xTaskGetTickCount() - xTimeBefore; + + /* A single notification should not have been received... */ + configASSERT( ulReturn == 1 ); + + /* ...and the time since scheduling the job should be greater than or + equal to the deferred execution time - which is converted to ticks for + comparison. */ + xShortDelay_ticks = pdMS_TO_TICKS( ulShortDelay_ms ); + configASSERT( ( xElapsedTime >= xShortDelay_ticks ) && ( xElapsedTime < ( xShortDelay_ticks + xAllowableMargin ) ) ); } /*-----------------------------------------------------------*/ @@ -166,27 +312,269 @@ static void prvExample_BasicRecyclableJob( void ) IotTaskPoolJob_t xJob; IotTaskPoolError_t xResult; uint32_t ulReturn; -const TickType_t xShortDelay = pdMS_TO_TICKS( 200 ); +const uint32_t ulNoFlags = 0UL; +const TickType_t xNoDelay = ( TickType_t ) 0; +size_t xFreeHeapBeforeCreatingJob = xPortGetFreeHeapSize(); - /* Ensure the notification count is 0 before scheduling the job. */ - while( ulTaskNotifyTake( pdTRUE, 0 ) != 0 ); + /* Don't expect any notifications to be pending yet. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); /* Create and schedule a job using the handle of this task as the job's context and the function that sends a notification to the task handle as - the jobs callback function. The job is created as a recyclable job - so it - is allocated inside the create function, but can then be used again and - again. */ - xResult = IotTaskPool_CreateRecyclableSystemJob( prvSimpleTaskNotifyCallback, - ( void * ) xTaskGetCurrentTaskHandle(), - &xJob ); + the jobs callback function. The job is created as a recyclable job and in + this case the memory used to hold the job status is allocated inside the + create function. As the job is persistent it can be used multiple times, + as demonstrated in other examples within this demo. In the full task pool + implementation the first parameter is used to pass the handle of the task + pool this recyclable job is to be associated with. In the lean + implementation of the task pool used by this demo there is only one task + pool (the system task pool created within the task pool library) so the + first parameter is NULL. */ + xResult = IotTaskPool_CreateRecyclableJob( NULL, + prvSimpleTaskNotifyCallback, + (void * ) xTaskGetCurrentTaskHandle(), + &xJob ); configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); - xResult = IotTaskPool_ScheduleSystemJob( xJob, 0 ); + /* This recyclable job is persistent, and in this case created dynamically, + so expect there to be less heap space then when entering the function. */ + configASSERT( xPortGetFreeHeapSize() < xFreeHeapBeforeCreatingJob ); + + /* In the full task pool implementation the first parameter is used to + pass the handle of the task pool to schedule. The lean task pool + implementation used in this demo only supports a single task pool, which + is created internally within the library, so the first parameter is NULL. */ + xResult = IotTaskPool_Schedule( NULL, xJob, ulNoFlags ); configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); - /* Wait for the notification coming from the job's callback function. */ - ulReturn = ulTaskNotifyTake( pdTRUE, xShortDelay ); + /* Look for the notification coming from the job's callback function. The + priority of the task pool worker task that executes the callback is higher + than the priority of this task so a block time is not needed - the task pool + worker task pre-empts this task and sends the notification (from the job's + callback) as soon as the job is scheduled. */ + ulReturn = ulTaskNotifyTake( pdTRUE, xNoDelay ); configASSERT( ulReturn ); + + /* Clean up recyclable job. In the full implementation of the task pool + the first parameter is used to pass a handle to the task pool the job is + associated with. In the lean implementation of the task pool used by this + demo there is only one task pool (the system task pool created in the + task pool library itself) so the first parameter is NULL. */ + IotTaskPool_DestroyRecyclableJob( NULL, xJob ); + + /* Once the job has been deleted the memory used to hold the job is + returned, so the available heap should be exactly as when entering this + function. */ + configASSERT( xPortGetFreeHeapSize() == xFreeHeapBeforeCreatingJob ); +} +/*-----------------------------------------------------------*/ + +static void prvExample_ReuseRecyclableJobFromLowPriorityTask( void ) +{ +IotTaskPoolError_t xResult; +uint32_t x, xIndex, ulNotificationValue; +const uint32_t ulJobsToCreate = 5UL, ulNoFlags = 0UL; +IotTaskPoolJob_t xJobs[ ulJobsToCreate ]; +size_t xFreeHeapBeforeCreatingJob = xPortGetFreeHeapSize(); +IotTaskPoolJobStatus_t xJobStatus; + + /* Don't expect any notifications to be pending yet. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); + + /* Create ulJobsToCreate jobs using the handle of this task as the job's + context and the function that sends a notification to the task handle as + the jobs callback function. The jobs are created as a recyclable job and + in this case the memory to store the job information is allocated within + the create function as at this time there are no recyclable jobs in the + task pool jobs cache. As the jobs are persistent they can be used multiple + times. In the full task pool implementation the first parameter is used to + pass the handle of the task pool this recyclable job is to be associated + with. In the lean implementation of the task pool used by this demo there + is only one task pool (the system task pool created within the task pool + library) so the first parameter is NULL. */ + for( x = 0; x < ulJobsToCreate; x++ ) + { + xResult = IotTaskPool_CreateRecyclableJob( NULL, + prvSimpleTaskNotifyCallback, + (void * ) xTaskGetCurrentTaskHandle(), + &( xJobs[ x ] ) ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* The job has been created but not scheduled so is now ready. */ + IotTaskPool_GetStatus( NULL, xJobs[ x ], &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_READY ); + } + + /* Demonstrate that the jobs can be recycled by performing twice the number + of iterations of scheduling jobs than there actually are created jobs. This + works because the task pool task priorities are above the priority of this + task, so the tasks that run the jobs pre-empt this task as soon as a job is + ready. */ + for( x = 0; x < ( ulJobsToCreate * 2UL ); x++ ) + { + /* Make sure array index does not go out of bounds. */ + xIndex = x % ulJobsToCreate; + + xResult = IotTaskPool_Schedule( NULL, xJobs[ xIndex ], ulNoFlags ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* The priority of the task pool task(s) is higher than the priority + of this task, so the job's callback function should have already + executed, sending a notification to this task, and incrementing this + task's notification value. */ + xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */ + 0UL, /* Don't clear any bits on exit. */ + &ulNotificationValue, /* Obtain the notification value. */ + 0UL ); /* No block time, return immediately. */ + configASSERT( ulNotificationValue == ( x + 1 ) ); + + /* The job's callback has executed so the job is now completed. */ + IotTaskPool_GetStatus( NULL, xJobs[ xIndex ], &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_COMPLETED ); + + /* To leave the list of jobs empty we can stop re-creating jobs half + way through iterations of this loop. */ + if( x < ulJobsToCreate ) + { + /* Recycle the job so it can be used again. In the full task pool + implementation the first parameter is used to pass the handle of the + task pool this job will be associated with. In this lean task pool + implementation only the system task pool exists (the task pool created + internally to the task pool library) so the first parameter is just + passed as NULL. *//*_RB_ Why not recycle it automatically? */ + IotTaskPool_RecycleJob( NULL, xJobs[ xIndex ] ); + xResult = IotTaskPool_CreateRecyclableJob( NULL, + prvSimpleTaskNotifyCallback, + (void * ) xTaskGetCurrentTaskHandle(), + &( xJobs[ xIndex ] ) ); + } + } + + /* Clear all the notification value bits again. */ + xTaskNotifyWait( portMAX_DELAY, /* Clear all bits on entry - portMAX_DELAY is used as it is a portable way of having all bits set. */ + 0UL, /* Don't clear any bits on exit. */ + NULL, /* Don't need the notification value this time. */ + 0UL ); /* No block time, return immediately. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); + + /* Clean up all the recyclable job. In the full implementation of the task + pool the first parameter is used to pass a handle to the task pool the job + is associated with. In the lean implementation of the task pool used by + this demo there is only one task pool (the system task pool created in the + task pool library itself) so the first parameter is NULL. */ + for( x = 0; x < ulJobsToCreate; x++ ) + { + xResult = IotTaskPool_DestroyRecyclableJob( NULL, xJobs[ x ] ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* Attempting to destroy the same job twice will fail. */ +//_RB_ vPortFree() asserts because it attempts to free memory again. xResult = IotTaskPool_DestroyRecyclableJob( NULL, xJobs[ x ] ); +// configASSERT( xResult != IOT_TASKPOOL_SUCCESS ); + } + + /* Once the job has been deleted the memory used to hold the job is + returned, so the available heap should be exactly as when entering this + function. */ + configASSERT( xPortGetFreeHeapSize() == xFreeHeapBeforeCreatingJob ); +} +/*-----------------------------------------------------------*/ + +static void prvExample_ReuseRecyclableJobFromHighPriorityTask( void ) +{ +IotTaskPoolError_t xResult; +uint32_t x, ulNotificationValue; +const uint32_t ulJobsToCreate = 5UL; +const uint32_t ulNoFlags = 0UL; +IotTaskPoolJob_t xJobs[ ulJobsToCreate ]; +IotTaskPoolJobStorage_t xJobStorage[ ulJobsToCreate ]; +size_t xFreeHeapBeforeCreatingJob = xPortGetFreeHeapSize(); +TickType_t xShortDelay = pdMS_TO_TICKS( 150 ); +IotTaskPoolJobStatus_t xJobStatus; + + /* Don't expect any notifications to be pending yet. */ + configASSERT( ulTaskNotifyTake( pdTRUE, 0 ) == 0 ); + + /* prvExample_ReuseRecyclableJobFromLowPriorityTask() executes in a task + that has a lower [task] priority than the task pool's worker tasks. + Therefore a talk pool worker preempts the task that calls + prvExample_ReuseRecyclableJobFromHighPriorityTask() as soon as the job is + scheduled. prvExample_ReuseRecyclableJobFromHighPriorityTask() reverses the + priorities - prvExample_ReuseRecyclableJobFromHighPriorityTask() raises its + priority to above the task pool's worker tasks, so the worker tasks do not + execute until the calling task enters the blocked state. First raise the + priority - passing NULL means raise the priority of the calling task. */ + vTaskPrioritySet( NULL, tpTASK_POOL_WORKER_PRIORITY + 1 ); + + /* Create ulJobsToCreate jobs using the handle of this task as the job's + context and the function that sends a notification to the task handle as + the jobs callback function. */ + for( x = 0; x < ulJobsToCreate; x++ ) + { + xResult = IotTaskPool_CreateJob( prvSimpleTaskNotifyCallback, /* Callback function. */ + ( void * ) xTaskGetCurrentTaskHandle(), /* Job context. */ + &( xJobStorage[ x ] ), + &( xJobs[ x ] ) ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* This is not a persistent (recyclable) job and its storage is on the + stack of this function, so the amount of heap space available should not + have chanced since entering this function. */ + configASSERT( xFreeHeapBeforeCreatingJob == xPortGetFreeHeapSize() ); + } + + for( x = 0; x < ulJobsToCreate; x++ ) + { + /* Schedule the next job. */ + xResult = IotTaskPool_Schedule( NULL, xJobs[ x ], ulNoFlags ); + configASSERT( xResult == IOT_TASKPOOL_SUCCESS ); + + /* Although scheduled, the job's callback has not executed, so the job + reports itself as scheduled. */ + IotTaskPool_GetStatus( NULL, xJobs[ x ], &xJobStatus ); + configASSERT( xJobStatus == IOT_TASKPOOL_STATUS_SCHEDULED ); + + /* The priority of the task pool task(s) is lower than the priority + of this task, so the job's callback function should not have executed + yes, so don't expect the notification value for this task to have + changed. */ + xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */ + 0UL, /* Don't clear any bits on exit. */ + &ulNotificationValue, /* Obtain the notification value. */ + 0UL ); /* No block time, return immediately. */ + configASSERT( ulNotificationValue == 0 ); + } + + /* At this point there are ulJobsToCreate scheduled, but none have executed + their callbacks because the priority of this task is higher than the + priority of the task pool worker threads. When this task blocks to wait for + a notification a worker thread will be able to executes - but as soon as its + callback function sends a notification to this task this task will + preempt it (because it has a higher priority) so this task only expects to + receive one notification at a time. */ + for( x = 0; x < ulJobsToCreate; x++ ) + { + xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */ + 0UL, /* Don't clear any bits on exit. */ + &ulNotificationValue, /* Obtain the notification value. */ + xShortDelay ); /* Short delay to allow a task pool worker to execute. */ + configASSERT( ulNotificationValue == ( x + 1 ) ); + } + + /* All the scheduled jobs have now executed, so waiting for another + notification should timeout without the notification value changing. */ + xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */ + 0UL, /* Don't clear any bits on exit. */ + &ulNotificationValue, /* Obtain the notification value. */ + xShortDelay ); /* Short delay to allow a task pool worker to execute. */ + configASSERT( ulNotificationValue == x ); + + /* Reset the priority of this task and clear the notifications ready for the + next example. */ + vTaskPrioritySet( NULL, tskIDLE_PRIORITY ); + xTaskNotifyWait( portMAX_DELAY, /* Clear all bits on entry - portMAX_DELAY is used as it is a portable way of having all bits set. */ + 0UL, /* Don't clear any bits on exit. */ + NULL, /* Don't need the notification value this time. */ + 0UL ); /* No block time, return immediately. */ } /*-----------------------------------------------------------*/ @@ -269,6 +657,9 @@ volatile uint32_t ulSetToNonZeroInDebuggerToContinue = 0; taskENTER_CRITICAL(); { + printf( "Assert hit on line %lu of %s\r\n", ulLine, pcFileName ); + fflush( stdout ); + /* You can step out of this function to debug the assertion by using the debugger to set ulSetToNonZeroInDebuggerToContinue to a non-zero value. */ diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/include/iot_taskpool.h b/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/include/iot_taskpool.h index 7e94e7264..a91ccf152 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/include/iot_taskpool.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/include/iot_taskpool.h @@ -53,11 +53,9 @@ * - @functionname{taskpool_function_setmaxthreads} * - @functionname{taskpool_function_createjob} * - @functionname{taskpool_function_createrecyclablejob} - * - @functionname{taskpool_function_createrecyclablesystemjob} * - @functionname{taskpool_function_destroyrecyclablejob} * - @functionname{taskpool_function_recyclejob} * - @functionname{taskpool_function_schedule} - * - @functionname{taskpool_function_schedulesystemjob} * - @functionname{taskpool_function_scheduledeferred} * - @functionname{taskpool_function_getstatus} * - @functionname{taskpool_function_trycancel} @@ -73,11 +71,9 @@ * @functionpage{IotTaskPool_SetMaxThreads,taskpool,setmaxthreads} * @functionpage{IotTaskPool_CreateJob,taskpool,createjob} * @functionpage{IotTaskPool_CreateRecyclableJob,taskpool,createrecyclablejob} - * @functionpage{IotTaskPool_CreateRecyclableSystemJob,taskpool,createrecyclablesystemjob} * @functionpage{IotTaskPool_DestroyRecyclableJob,taskpool,destroyrecyclablejob} * @functionpage{IotTaskPool_RecycleJob,taskpool,recyclejob} * @functionpage{IotTaskPool_Schedule,taskpool,schedule} - * @functionpage{IotTaskPool_ScheduleSystemJob,taskpool,schedule} * @functionpage{IotTaskPool_ScheduleDeferred,taskpool,scheduledeferred} * @functionpage{IotTaskPool_GetStatus,taskpool,getstatus} * @functionpage{IotTaskPool_TryCancel,taskpool,trycancel} @@ -164,7 +160,7 @@ IotTaskPoolError_t IotTaskPool_Create( const IotTaskPoolInfo_t * const pInfo, * This function should be called to destroy one instance of a task pool previously created with a call * to @ref IotTaskPool_Create or @ref IotTaskPool_CreateSystemTaskPool. * Calling this fuction release all underlying resources. After calling this function, any job scheduled but not yet executed - * will be cancelled and destroyed. + * will be canceled and destroyed. * The `taskPool` instance will no longer be valid after this function returns. * * @param[in] taskPool A handle to the task pool, e.g. as returned by a call to @ref IotTaskPool_Create or @@ -247,7 +243,7 @@ IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback, * - #IOT_TASKPOOL_NO_MEMORY * - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS * - * @note This function will not allocate memory. + * @note This function will not allocate memory. //_RB_ Incorrect comment. * * @warning A recyclable job should be recycled with a call to @ref IotTaskPool_RecycleJob rather than destroyed. * @@ -259,36 +255,6 @@ IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPool, IotTaskPoolJob_t * const pJob ); /* @[declare_taskpool_createrecyclablejob] */ -/** - * brief Creates a job for the system task pool by allocating the job dynamically. - * The system task pool is the task pool created by @ref IotTaskPool_CreateSystemTaskPool. - * - * A recyclable job does not need to be allocated twice, but it can rather be reused through - * subsequent calls to @ref IotTaskPool_CreateRecyclableJob. - * - * @param[in] userCallback A user-specified callback for the job. - * @param[in] pUserContext A user-specified context for the callback. - * @param[out] pJob A pointer to an instance of @ref IotTaskPoolJob_t that will be initialized when this - * function returns successfully. This handle can be used to inspect the job status with - * @ref IotTaskPool_GetStatus or cancel the job with @ref IotTaskPool_TryCancel, etc.... - * - * @return One of the following: - * - #IOT_TASKPOOL_SUCCESS - * - #IOT_TASKPOOL_BAD_PARAMETER - * - #IOT_TASKPOOL_NO_MEMORY - * - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS - * - * @note This function will not allocate memory. - * - * @warning A recyclable job should be recycled with a call to @ref IotTaskPool_RecycleJob rather than destroyed. - * - */ -/* @[declare_taskpool_createrecyclablesystemjob] */ -IotTaskPoolError_t IotTaskPool_CreateRecyclableSystemJob( IotTaskPoolRoutine_t userCallback, - void * pUserContext, - IotTaskPoolJob_t * const pJob ); -/* @[declare_taskpool_createrecyclablesystemjob] */ - /** * @brief This function un-initializes a job. * @@ -445,88 +411,7 @@ IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPool, IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPool, IotTaskPoolJob_t job, uint32_t flags ); - -/** - * @brief This function schedules a job created with @ref IotTaskPool_CreateJob or @ref IotTaskPool_CreateRecyclableJob @ref IotTaskPool_CreateRecyclableSystemJob - * against the system task pool. The system task pool is the task pool created by @ref IotTaskPool_CreateSystemTaskPool. - * - * See @ref taskpool_design for a description of the jobs lifetime and interaction with the threads used in the task pool - * library. - * - * @param[in] job A job to schedule for execution. This must be first initialized with a call to @ref IotTaskPool_CreateJob. - * @param[in] flags Flags to be passed by the user, e.g. to identify the job as high priority by specifying #IOT_TASKPOOL_JOB_HIGH_PRIORITY. - * - * @return One of the following: - * - #IOT_TASKPOOL_SUCCESS - * - #IOT_TASKPOOL_BAD_PARAMETER - * - #IOT_TASKPOOL_ILLEGAL_OPERATION - * - #IOT_TASKPOOL_NO_MEMORY - * - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS - * - * - * @note This function will not allocate memory, so it is guaranteed to succeed if the parameters are correct and the task pool - * was correctly initialized, and not yet destroyed. - * - * Example - * @code{c} - * // An example of a user context to pass to a callback through a task pool thread. - * typedef struct JobUserContext - * { - * uint32_t counter; - * } JobUserContext_t; - * - * // An example of a user callback to invoke through a task pool thread. - * static void ExecutionCb( IotTaskPool_t taskPool, IotTaskPoolJob_t job, void * context ) - * { - * ( void )taskPool; - * ( void )job; - * - * JobUserContext_t * pUserContext = ( JobUserContext_t * )context; - * - * pUserContext->counter++; - * } - * - * void TaskPoolExample( ) - * { - * JobUserContext_t userContext = { 0 }; - * IotTaskPoolJob_t job; - * - * // Create the system task pool. This example assumes the task pool is created successfully. - * // It is recommended to test the function's return value in production code. - * IotTaskPool_CreateSystemTaskPool( &xTaskPoolParameters ); - * - * // Statically allocate one job, schedule it. - * IotTaskPool_CreateJob( &ExecutionCb, &userContext, &job ); - * - * IotTaskPoolError_t errorSchedule = IotTaskPool_ScheduleSystemJob( &job, 0 ); - * - * switch ( errorSchedule ) - * { - * case IOT_TASKPOOL_SUCCESS: - * break; - * case IOT_TASKPOOL_BAD_PARAMETER: // Invalid parameters, such as a NULL handle, can trigger this error. - * case IOT_TASKPOOL_ILLEGAL_OPERATION: // Scheduling a job that was previously scheduled or destroyed could trigger this error. - * case IOT_TASKPOOL_NO_MEMORY: // Scheduling a with flag #IOT_TASKPOOL_JOB_HIGH_PRIORITY could trigger this error. - * case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS: // Scheduling a job after trying to destroy the task pool could trigger this error. - * // ASSERT - * break; - * default: - * // ASSERT - * } - * - * // - * // ... Perform other operations ... - * // - * - * IotTaskPool_Destroy( taskPool ); - * } - * @endcode - */ -/* @[declare_taskpool_schedulesystemjob] */ -IotTaskPoolError_t IotTaskPool_ScheduleSystemJob( IotTaskPoolJob_t pJob, - uint32_t flags ); - -/* @[declare_taskpool_schedulesystemjob] */ +/* @[declare_taskpool_schedule] */ /** * @brief This function schedules a job created with @ref IotTaskPool_CreateJob against the task pool diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c index fff69ca83..a01e6423c 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c @@ -32,6 +32,7 @@ /* Standard includes. */ #include +#include #include #include #include @@ -490,15 +491,6 @@ IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback, /*-----------------------------------------------------------*/ -IotTaskPoolError_t IotTaskPool_CreateRecyclableSystemJob( IotTaskPoolRoutine_t userCallback, - void * pUserContext, - IotTaskPoolJob_t * const pJob ) -{ - return IotTaskPool_CreateRecyclableJob ( &_IotSystemTaskPool, userCallback, pUserContext, pJob ); -} - -/*-----------------------------------------------------------*/ - IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle, IotTaskPoolRoutine_t userCallback, void * pUserContext, @@ -506,11 +498,21 @@ IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle; + _taskPool_t * const pTaskPool = &_IotSystemTaskPool; _taskPoolJob_t * pTempJob = NULL; + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); + + /* Avoid compiler warnings about unused parameters if configASSERT() is not + defined. */ + ( void ) taskPoolHandle; + /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob ); @@ -542,12 +544,21 @@ IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandl { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - ( void ) taskPoolHandle; - _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle; + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ +#warning could use a TASKPOOL macro to check value and return error. + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); + + /* Avoid compiler warnings about unused parameters if configASSERT() is not + defined. */ + ( void ) taskPoolHandle; + /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle ); IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false ); @@ -564,10 +575,19 @@ IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle, { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle; + _taskPool_t * pTaskPool = ( _taskPool_t * ) &_IotSystemTaskPool; + + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); + + /* Ensure unused parameters do not cause compiler warnings in case + configASSERT() is not defined. */ + ( void ) taskPoolHandle; - /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob ); taskENTER_CRITICAL(); @@ -589,12 +609,23 @@ IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle, { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle; + _taskPool_t * const pTaskPool = &_IotSystemTaskPool; + /* Task pool must have been created. */ configASSERT( pTaskPool->running != false ); + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); + + /* Avoid compiler warnings about unused parameters if configASSERT() is not + defined. */ + ( void ) taskPoolHandle; + /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob ); TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( ( flags != 0UL ) && ( flags != IOT_TASKPOOL_JOB_HIGH_PRIORITY ) ); @@ -609,24 +640,21 @@ IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle, /*-----------------------------------------------------------*/ -IotTaskPoolError_t IotTaskPool_ScheduleSystemJob( IotTaskPoolJob_t pJob, - uint32_t flags ) -{ - return IotTaskPool_Schedule( &_IotSystemTaskPool, pJob, flags ); -} - -/*-----------------------------------------------------------*/ - IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle, IotTaskPoolJob_t job, uint32_t timeMs ) { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle; + _taskPool_t * pTaskPool = &_IotSystemTaskPool; + + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); - /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job ); if( timeMs == 0UL ) @@ -688,7 +716,8 @@ IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle, TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); +//_RB_ TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); /* What is the point of this parameter? */ + ( void ) taskPoolHandle; TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus ); *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED; @@ -710,10 +739,14 @@ IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle, { TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS ); - _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle; + _taskPool_t * pTaskPool = &_IotSystemTaskPool; - /* Parameter checking. */ - TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); + /* This lean version of the task pool only supports the task pool created + by this library (the system task pool). NULL means use the system task + pool - no other values are allowed. Use the full implementation of this + library if you want multiple task pools (there is more than one task in + each pool. */ + configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) ); TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job ); if( pStatus != NULL ) @@ -859,6 +892,7 @@ static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo uint32_t count; uint32_t threadsCreated; + char cTaskName[ 10 ]; /* Check input values for consistency. */ TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool ); @@ -887,10 +921,12 @@ static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */ for( threadsCreated = 0; threadsCreated < pInfo->minThreads; ) { - TaskHandle_t task = NULL; + TaskHandle_t task = NULL; //_RB_ need to check compiles with C89 + + snprintf( cTaskName, sizeof( cTaskName ), "pool%d", ( int ) threadsCreated ); BaseType_t res = xTaskCreate( _taskPoolWorker, - NULL, + cTaskName, pInfo->stackSize, pTaskPool, pInfo->priority, @@ -1019,7 +1055,7 @@ static void _taskPoolWorker( void * pUserContext ) pJob = IotLink_Container( _taskPoolJob_t, pFirst, link ); /* Update status to 'executing'. */ - pJob->status = IOT_TASKPOOL_STATUS_COMPLETED; + pJob->status = IOT_TASKPOOL_STATUS_COMPLETED; /*_RB_ Should this be 'executing'? */ userCallback = pJob->userCallback; } } @@ -1156,7 +1192,7 @@ static void _recycleJob( _taskPoolCache_t * const pCache, _taskPoolJob_t * const pJob ) { /* We should never try and recycling a job that is linked into some queue. */ - IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false ); + IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );//_RB_ Seems to be duplicate of test before this is called. /* We will recycle the job if there is space in the cache. */ if( pCache->freeCount < IOT_TASKPOOL_JOBS_RECYCLE_LIMIT ) @@ -1232,15 +1268,11 @@ static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool, /* Update the job status to 'scheduled'. */ pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED; - BaseType_t higherPriorityTaskWoken; - /* Append the job to the dispatch queue. */ IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link ); /* Signal a worker to pick up the job. */ - ( void ) xSemaphoreGiveFromISR( pTaskPool->dispatchSignal, &higherPriorityTaskWoken ); - - portYIELD_FROM_ISR( higherPriorityTaskWoken ); + xSemaphoreGive( pTaskPool->dispatchSignal ); TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL(); } @@ -1333,7 +1365,7 @@ static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool, { bool shouldReschedule = false; - /* If the job being cancelled was at the head of the timeouts queue, then we need to reschedule the timer + /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer * with the next job timeout */ IotLink_t * pHeadLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );