#error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.\r
#endif\r
\r
-/* Platform layer includes. */\r
-//_RB_#include "platform/iot_threads.h"\r
-//_RB_#include "platform/iot_clock.h"\r
-\r
/* Task pool internal include. */\r
#include "private/iot_taskpool_internal.h"\r
\r
* the system libraries as well. The system task pool needs to be initialized before any library is used or\r
* before any code that posts jobs to the task pool runs.\r
*/\r
-_taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };\r
+static _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };\r
\r
/* -------------- Convenience functions to create/recycle/destroy jobs -------------- */\r
\r
/**\r
* Initializes a pre-allocated instance of a task pool.\r
*\r
- * @param[in] pInfo The initialization information for the task pool.\r
* @param[in] pTaskPool The pre-allocated instance of the task pool to initialize.\r
*\r
*/\r
-static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,\r
- _taskPool_t * const pTaskPool );\r
+static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool );\r
\r
/**\r
* Initializes a pre-allocated instance of a task pool.\r
*/\r
static void _destroyTaskPool( _taskPool_t * const pTaskPool );\r
\r
-/**\r
- * Check for the exit condition.\r
- *\r
- * @param[in] pTaskPool The task pool to destroy.\r
- *\r
- */\r
-static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool );\r
-\r
/**\r
* Set the exit condition.\r
*\r
*/\r
static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,\r
_taskPoolJob_t * const pJob );\r
-\r
/**\r
* Matches a deferred job in the timer queue with its timer event wrapper.\r
*\r
\r
/* ---------------------------------------------------------------------------------------------- */\r
\r
-IotTaskPool_t IotTaskPool_GetSystemTaskPool( void )\r
-{\r
- return &_IotSystemTaskPool;\r
-}\r
-\r
-/*-----------------------------------------------------------*/\r
+/*\r
+ * The full IoT Task Pool Library has many use cases, including Linux\r
+ * development. Typical FreeRTOS use cases do not require the full\r
+ * functionality so optimised version is provided specifically for use with\r
+ * FreeRTOS. Unlike the full version, this optimised version:\r
+ * + Only supports a single task pool (system task pool) at a time.\r
+ * + Does not auto-scale by dynamically adding more tasks if the number of\r
+ * + tasks in the pool becomes exhausted. The number of tasks in the pool\r
+ * are fixed at compile time. See the task pool configuration options for\r
+ * more information.\r
+ * + Cannot be shut down - it exists for the lifetime of the application.\r
+ *\r
+ * As such this file does not implement the following API functions:\r
+ * + IotTaskPool_GetSystemTaskPool()\r
+ * + IotTaskPool_Create()\r
+ * + IotTaskPool_Destroy()\r
+ * + IotTaskPool_SetMaxThreads()\r
+ *\r
+ * Users who are interested in the functionality of the full IoT Task Pool\r
+ * library can us it in place of this optimised version.\r
+ */\r
\r
IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )\r
{\r
\r
TASKPOOL_NO_FUNCTION_CLEANUP();\r
}\r
-\r
-/*-----------------------------------------------------------*/\r
-\r
-IotTaskPoolError_t IotTaskPool_Create( const IotTaskPoolInfo_t * const pInfo,\r
- IotTaskPool_t * const pTaskPool )\r
-{\r
- TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
-\r
- _taskPool_t * pTempTaskPool = NULL;\r
-\r
- /* Verify that the task pool storage is valid. */\r
- TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
-\r
- /* Parameter checking. */\r
- TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );\r
-\r
- /* Allocate the memory for the task pool */\r
- pTempTaskPool = ( _taskPool_t * ) IotTaskPool_MallocTaskPool( sizeof( _taskPool_t ) );\r
-\r
- if( pTempTaskPool == NULL )\r
- {\r
- TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
- }\r
-\r
- memset( pTempTaskPool, 0x00, sizeof( _taskPool_t ) );\r
-\r
- TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, pTempTaskPool ) );\r
-\r
- TASKPOOL_FUNCTION_CLEANUP();\r
-\r
- if( TASKPOOL_FAILED( status ) )\r
- {\r
- if( pTempTaskPool != NULL )\r
- {\r
- IotTaskPool_FreeTaskPool( pTempTaskPool );\r
- }\r
- }\r
- else\r
- {\r
- *pTaskPool = pTempTaskPool;\r
- }\r
-\r
- TASKPOOL_FUNCTION_CLEANUP_END();\r
-}\r
-\r
-/*-----------------------------------------------------------*/\r
-\r
-IotTaskPoolError_t IotTaskPool_Destroy( IotTaskPool_t taskPoolHandle )\r
-{\r
- TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
-\r
- uint32_t count;\r
- bool completeShutdown = true;\r
-\r
- _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
-\r
- /* Track how many threads the task pool owns. */\r
- uint32_t activeThreads;\r
-\r
- /* Parameter checking. */\r
- TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
-\r
- /* Destroying the task pool should be safe, and therefore we will grab the task pool lock.\r
- * No worker thread or application thread should access any data structure\r
- * in the task pool while the task pool is being destroyed. */\r
- taskENTER_CRITICAL();\r
- {\r
- IotLink_t * pItemLink;\r
-\r
- /* Record how many active threads in the task pool. */\r
- activeThreads = pTaskPool->activeThreads;\r
-\r
- /* Destroying a Task pool happens in six (6) stages: First, (1) we clear the job queue and (2) the timer queue.\r
- * Then (3) we clear the jobs cache. We will then (4) wait for all worker threads to signal exit,\r
- * before (5) setting the exit condition and wake up all active worker threads. Finally (6) destroying\r
- * all task pool data structures and release the associated memory.\r
- */\r
-\r
- /* (1) Clear the job queue. */\r
- do\r
- {\r
- pItemLink = NULL;\r
-\r
- pItemLink = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
-\r
- if( pItemLink != NULL )\r
- {\r
- _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );\r
-\r
- _destroyJob( pJob );\r
- }\r
- } while( pItemLink );\r
-\r
- /* (2) Clear the timer queue. */\r
- {\r
- _taskPoolTimerEvent_t * pTimerEvent;\r
-\r
- /* A deferred job may have fired already. Since deferred jobs will go through the same mutex\r
- * the shutdown sequence is holding at this stage, there is no risk for race conditions. Yet, we\r
- * need to let the deferred job to destroy the task pool. */\r
-\r
- pItemLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
-\r
- if( pItemLink != NULL )\r
- {\r
- TickType_t now = xTaskGetTickCount();\r
-\r
- pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );\r
-\r
- if( pTimerEvent->expirationTime <= now )\r
- {\r
- IotLogDebug( "Shutdown will be deferred to the timer thread" );\r
-\r
- /* Timer may have fired already! Let the timer thread destroy\r
- * complete the taskpool destruction sequence. */\r
- completeShutdown = false;\r
- }\r
-\r
- /* Remove all timers from the timeout list. */\r
- for( ; ; )\r
- {\r
- pItemLink = IotListDouble_RemoveHead( &pTaskPool->timerEventsList );\r
-\r
- if( pItemLink == NULL )\r
- {\r
- break;\r
- }\r
-\r
- pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );\r
-\r
- _destroyJob( pTimerEvent->job );\r
-\r
- IotTaskPool_FreeTimerEvent( pTimerEvent );\r
- }\r
- }\r
- }\r
-\r
- /* (3) Clear the job cache. */\r
- do\r
- {\r
- pItemLink = NULL;\r
-\r
- pItemLink = IotListDouble_RemoveHead( &pTaskPool->jobsCache.freeList );\r
-\r
- if( pItemLink != NULL )\r
- {\r
- _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );\r
-\r
- _destroyJob( pJob );\r
- }\r
- } while( pItemLink );\r
-\r
- /* (4) Set the exit condition. */\r
- _signalShutdown( pTaskPool, activeThreads );\r
- }\r
- taskEXIT_CRITICAL();\r
-\r
- /* (5) Wait for all active threads to reach the end of their life-span. */\r
- for( count = 0; count < activeThreads; ++count )\r
- {\r
- xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );\r
- }\r
-\r
- IotTaskPool_Assert( uxSemaphoreGetCount( pTaskPool->startStopSignal ) == 0 );\r
- IotTaskPool_Assert( pTaskPool->activeThreads == 0 );\r
-\r
- /* (6) Destroy all signaling objects. */\r
- if( completeShutdown == true )\r
- {\r
- _destroyTaskPool( pTaskPool );\r
-\r
- /* Do not free the system task pool which is statically allocated. */\r
- if( pTaskPool != &_IotSystemTaskPool )\r
- {\r
- IotTaskPool_FreeTaskPool( pTaskPool );\r
- }\r
- }\r
-\r
- TASKPOOL_NO_FUNCTION_CLEANUP();\r
-}\r
-\r
-/*-----------------------------------------------------------*/\r
-\r
-IotTaskPoolError_t IotTaskPool_SetMaxThreads( IotTaskPool_t taskPoolHandle,\r
- uint32_t maxThreads )\r
-{\r
- TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
-\r
- _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
-\r
- /* Parameter checking. */\r
- TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
- TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( maxThreads < 1UL );\r
-\r
- TASKPOOL_NO_FUNCTION_CLEANUP();\r
-}\r
-\r
/*-----------------------------------------------------------*/\r
\r
IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,\r
pool - no other values are allowed. Use the full implementation of this\r
library if you want multiple task pools (there is more than one task in\r
each pool. */\r
-//_RB_could use a TASKPOOL macro to check value and return error.\r
configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
\r
/* Avoid compiler warnings about unused parameters if configASSERT() is not\r
pTimerEvent->link.pNext = NULL;\r
pTimerEvent->link.pPrevious = NULL;\r
pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );\r
- pTimerEvent->job = job;\r
+ pTimerEvent->job = job; //_RB_ Think up to here can be outside the critical section.\r
\r
/* Append the timer event to the timer list. */\r
IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );\r
{\r
TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
\r
- /* Parameter checking. */\r
-//_RB_ TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle ); /* What is the point of this parameter? */\r
+ /* This lean version of the task pool only supports the task pool created\r
+ by this library (the system task pool). NULL means use the system task\r
+ pool - no other values are allowed. Use the full implementation of this\r
+ library if you want multiple task pools (there is more than one task in\r
+ each pool. */\r
+ configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
( void ) taskPoolHandle;\r
+\r
+ /* Parameter checking. */\r
TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus );\r
*pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;\r
{\r
TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
\r
- _taskPool_t * pTaskPool = &_IotSystemTaskPool;\r
+ _taskPool_t * const pTaskPool = &_IotSystemTaskPool;\r
\r
/* This lean version of the task pool only supports the task pool created\r
by this library (the system task pool). NULL means use the system task\r
TASKPOOL_NO_FUNCTION_CLEANUP();\r
}\r
\r
-static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,\r
- _taskPool_t * const pTaskPool )\r
+static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool )\r
{\r
- TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
-\r
- bool semStartStopInit = false;\r
- bool semDispatchInit = false;\r
-\r
/* Initialize a job data structures that require no de-initialization.\r
* All other data structures carry a value of 'NULL' before initailization.\r
*/\r
\r
_initJobsCache( &pTaskPool->jobsCache );\r
\r
- /* Initialize the semaphore to ensure all threads have started. */\r
- pTaskPool->startStopSignal = xSemaphoreCreateCountingStatic( pInfo->minThreads, 0, &pTaskPool->startStopSignalBuffer );\r
-\r
- if( pTaskPool->startStopSignal != NULL )\r
- {\r
- semStartStopInit = true;\r
-\r
- /* Initialize the semaphore for waiting for incoming work. */\r
- pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );\r
-\r
- if( pTaskPool->dispatchSignal != NULL )\r
- {\r
- semDispatchInit = true;\r
- }\r
- else\r
- {\r
- TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
- }\r
- }\r
- else\r
- {\r
- TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
- }\r
-\r
- TASKPOOL_FUNCTION_CLEANUP();\r
-\r
- if( TASKPOOL_FAILED( status ) )\r
- {\r
- if( semStartStopInit )\r
- {\r
- vSemaphoreDelete( &pTaskPool->startStopSignal );\r
- }\r
-\r
- if( semDispatchInit )\r
- {\r
- vSemaphoreDelete( &pTaskPool->dispatchSignal );\r
- }\r
- }\r
-\r
- TASKPOOL_FUNCTION_CLEANUP_END();\r
+ /* Initialize the semaphore for waiting for incoming work. Cannot fail as\r
+ statically allocated. */\r
+ pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );\r
}\r
\r
static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,\r
{\r
TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
\r
- uint32_t count;\r
uint32_t threadsCreated = 0; /* Although initialised before use removing the initialiser here results in compiler warnings. */\r
char cTaskName[ 10 ];\r
\r
memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );\r
\r
/* Initialize all internal data structure prior to creating all threads. */\r
- TASKPOOL_ON_ERROR_GOTO_CLEANUP( _initTaskPoolControlStructures( pInfo, pTaskPool ) );\r
+ _initTaskPoolControlStructures( pTaskPool );\r
\r
/* Create the timer for a new connection. */\r
pTaskPool->timer = xTimerCreate( NULL, portMAX_DELAY, pdFALSE, ( void * ) pTaskPool, _timerCallback );\r
TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
}\r
\r
- /* The task pool will initialize the minimum number of threads requested by the user upon start. */\r
- /* When a thread is created, it will signal a semaphore to signify that it is about to wait on incoming */\r
- /* jobs. A thread can be woken up for exit or for new jobs only at that point in time. */\r
- /* The exit condition is setting the maximum number of threads to 0. */\r
-\r
+ /* The task pool will initialize the minimum number of threads requested by the user upon start.\r
+ Note this tailored version of the task pool does not autoscale, but fixes the number of tasks\r
+ in the pool to the originally specified minimum, and the specified maximum value is ignored. */\r
/* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */\r
for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )\r
{\r
- TaskHandle_t task = NULL; //_RB_ need to check compiles with C89\r
+ TaskHandle_t task = NULL;\r
\r
+ /* Generate a unique name for the task. */\r
snprintf( cTaskName, sizeof( cTaskName ), "pool%d", ( int ) threadsCreated );\r
\r
BaseType_t res = xTaskCreate( _taskPoolWorker,\r
&task );\r
\r
/* Create one thread. */\r
- if( res == pdFALSE )\r
+ if( res == pdFALSE ) //_RB_ would not be needed if tasks are created statically.\r
{\r
IotLogError( "Could not create worker thread! Exiting..." );\r
\r
\r
TASKPOOL_FUNCTION_CLEANUP();\r
\r
- /* Wait for threads to be ready to wait on the condition, so that threads are actually able to receive messages. */\r
- for( count = 0; count < threadsCreated; ++count )\r
- {\r
- xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY ); /*_RB_ Is waiting necessary, and if so, is a semaphore necessary? */\r
- }\r
-\r
/* In case of failure, wait on the created threads to exit. */\r
if( TASKPOOL_FAILED( status ) )\r
{\r
/* Set the exit condition for the newly created threads. */\r
_signalShutdown( pTaskPool, threadsCreated );\r
-\r
- /* Signal all threads to exit. */\r
- for( count = 0; count < threadsCreated; ++count )\r
- {\r
- xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );\r
- }\r
-\r
_destroyTaskPool( pTaskPool );\r
}\r
\r
{\r
vSemaphoreDelete( pTaskPool->dispatchSignal );\r
}\r
-\r
- if( pTaskPool->startStopSignal != NULL )\r
- {\r
- vSemaphoreDelete( pTaskPool->startStopSignal );\r
- }\r
}\r
\r
/* ---------------------------------------------------------------------------------------------- */\r
/* Extract pTaskPool pointer from context. */\r
_taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;\r
\r
- /* Signal that this worker completed initialization and it is ready to receive notifications. */\r
- ( void ) xSemaphoreGive( pTaskPool->startStopSignal );\r
-\r
/* OUTER LOOP: it controls the lifetiem of the worker thread: exit condition for a worker thread\r
* is setting maxThreads to zero. A worker thread is running until the maximum number of allowed\r
* threads is not zero and the active threads are less than the maximum number of allowed threads.\r
*/\r
taskENTER_CRITICAL();\r
{\r
- /* If the exit condition is verified, update the number of active threads and exit the loop. */\r
- if( _IsShutdownStarted( pTaskPool ) )\r
- {\r
- IotLogDebug( "Worker thread exiting because exit condition was set." );\r
-\r
- /* Decrease the number of active threads. */\r
- pTaskPool->activeThreads--;\r
-\r
- taskEXIT_CRITICAL();\r
-\r
- /* Signal that this worker is exiting. */\r
- xSemaphoreGive( pTaskPool->startStopSignal );\r
-\r
- /* On shutdown, abandon the OUTER LOOP immediately. */\r
- break;\r
- }\r
-\r
/* Dequeue the first job in FIFO order. */\r
pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
\r
*/\r
taskENTER_CRITICAL();\r
{\r
- /* Check again for shutdown and bail out early in case. */\r
- if( _IsShutdownStarted( pTaskPool ) )\r
- {\r
- taskEXIT_CRITICAL();\r
-\r
- /* Complete the shutdown sequence. */\r
- _destroyTaskPool( pTaskPool );\r
-\r
- return;\r
- }\r
-\r
/* Dispatch all deferred job whose timer expired, then reset the timer for the next\r
* job down the line. */\r
for( ; ; )\r