2 * Amazon FreeRTOS Common V1.0.0
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
26 * @file iot_taskpool.c
\r
27 * @brief Implements the task pool functions in iot_taskpool.h
\r
32 * The full IoT Task Pool Library has many use cases, including Linux
\r
33 * development. Typical FreeRTOS use cases do not require the full
\r
34 * functionality so an optimised implementation is provided specifically for use
\r
35 * with FreeRTOS. The optimised version has a fixed number of tasks in the
\r
36 * pool, each of which uses statically allocated memory to ensure creation of
\r
37 * the pool is guaranteed (it does not run out of heap space). The constant
\r
38 * IOT_TASKPOOL_NUMBER_OF_WORKERS sets the number of tasks in the pool.
\r
40 * Unlike the full version, this optimised version:
\r
41 * + Only supports a single task pool (system task pool) at a time.
\r
42 * + Does not auto-scale by dynamically adding more tasks if the number of
\r
43 * + tasks in the pool becomes exhausted. The number of tasks in the pool
\r
44 * are fixed at compile time. See the task pool configuration options for
\r
46 * + Cannot be shut down - it exists for the lifetime of the application.
\r
48 * As such this file does not implement the following API functions:
\r
49 * + IotTaskPool_GetSystemTaskPool()
\r
50 * + IotTaskPool_Create()
\r
51 * + IotTaskPool_Destroy()
\r
52 * + IotTaskPool_SetMaxThreads()
\r
54 * Users who are interested in the functionality of the full IoT Task Pool
\r
55 * library can us it in place of this optimised version.
\r
59 /* Kernel includes. */
\r
60 #include "FreeRTOS.h"
\r
63 /* IoT libraries includes. */
\r
64 #include "iot_config.h"
\r
66 /* Standard includes. */
\r
67 #include <stdbool.h>
\r
73 #if !defined( configSUPPORT_STATIC_ALLOCATION ) || ( configSUPPORT_STATIC_ALLOCATION != 1 )
\r
74 #error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.
\r
77 /* Task pool internal include. */
\r
78 #include "private/iot_taskpool_internal.h"
\r
81 * @brief Maximum semaphore value for wait operations.
\r
83 #define TASKPOOL_MAX_SEM_VALUE 0xFFFF
\r
86 * @brief Reschedule delay in milliseconds for deferred jobs.
\r
88 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS ( 10ULL )
\r
90 /* ---------------------------------------------------------------------------------- */
\r
93 * Doxygen should ignore this section.
\r
95 * @brief The system task pool handle for all libraries to use.
\r
96 * User application can use the system task pool as well knowing that the usage will be shared with
\r
97 * the system libraries as well. The system task pool needs to be initialized before any library is used or
\r
98 * before any code that posts jobs to the task pool runs.
\r
100 static _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };
\r
102 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */
\r
105 * @brief Initializes one instance of a Task pool cache.
\r
107 * @param[in] pCache The pre-allocated instance of the cache to initialize.
\r
109 static void _initJobsCache( _taskPoolCache_t * const pCache );
\r
112 * @brief Initialize a job.
\r
114 * @param[in] pJob The job to initialize.
\r
115 * @param[in] userCallback The user callback for the job.
\r
116 * @param[in] pUserContext The context tp be passed to the callback.
\r
117 * @param[in] isStatic A flag to indicate whether the job is statically or synamically allocated.
\r
119 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
120 IotTaskPoolRoutine_t userCallback,
\r
121 void * pUserContext,
\r
125 * @brief Extracts and initializes one instance of a job from the cache or, if there is none available, it allocates and initialized a new one.
\r
127 * @param[in] pCache The instance of the cache to extract the job from.
\r
129 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache );
\r
132 * Recycles one instance of a job into the cache or, if the cache is full, it destroys it.
\r
134 * @param[in] pCache The instance of the cache to recycle the job into.
\r
135 * @param[in] pJob The job to recycle.
\r
138 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
139 _taskPoolJob_t * const pJob );
\r
142 * Destroys one instance of a job.
\r
144 * @param[in] pJob The job to destroy.
\r
147 static void _destroyJob( _taskPoolJob_t * const pJob );
\r
149 /* -------------- The worker thread procedure for a task pool thread -------------- */
\r
152 * The procedure for a task pool worker thread.
\r
154 * @param[in] pUserContext The user context.
\r
157 static void _taskPoolWorker( void * pUserContext );
\r
159 /* -------------- Convenience functions to handle timer events -------------- */
\r
162 * Comparer for the time list.
\r
164 * param[in] pTimerEventLink1 The link to the first timer event.
\r
165 * param[in] pTimerEventLink1 The link to the first timer event.
\r
167 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
168 const IotLink_t * const pTimerEventLink2 );
\r
171 * Reschedules the timer for handling deferred jobs to the next timeout.
\r
173 * param[in] timer The timer to reschedule.
\r
174 * param[in] pFirstTimerEvent The timer event that carries the timeout and job inforamtion.
\r
176 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
177 _taskPoolTimerEvent_t * const pFirstTimerEvent );
\r
180 * The task pool timer procedure for scheduling deferred jobs.
\r
182 * param[in] timer The timer to handle.
\r
184 static void _timerCallback( TimerHandle_t xTimer );
\r
186 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */
\r
189 * Parameter validation for a task pool initialization.
\r
191 * @param[in] pInfo The initialization information for the task pool.
\r
194 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo );
\r
197 * Initializes a pre-allocated instance of a task pool.
\r
199 * @param[in] pTaskPool The pre-allocated instance of the task pool to initialize.
\r
202 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool );
\r
205 * Initializes a pre-allocated instance of a task pool.
\r
207 * @param[in] pInfo The initialization information for the task pool.
\r
208 * @param[out] pTaskPool A pointer to the task pool data structure to initialize.
\r
211 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
212 _taskPool_t * const pTaskPool );
\r
215 * Destroys one instance of a task pool.
\r
217 * @param[in] pTaskPool The task pool to destroy.
\r
220 static void _destroyTaskPool( _taskPool_t * const pTaskPool );
\r
223 * Places a job in the dispatch queue.
\r
225 * @param[in] pTaskPool The task pool to scheduel the job with.
\r
226 * @param[in] pJob The job to schedule.
\r
227 * @param[in] flags The job flags.
\r
230 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
231 _taskPoolJob_t * const pJob );
\r
233 * Matches a deferred job in the timer queue with its timer event wrapper.
\r
235 * @param[in] pLink A pointer to the timer event link in the timer queue.
\r
236 * @param[in] pMatch A pointer to the job to match.
\r
239 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
243 * Tries to cancel a job.
\r
245 * @param[in] pTaskPool The task pool to cancel an operation against.
\r
246 * @param[in] pJob The job to cancel.
\r
247 * @param[out] pStatus The status of the job at the time of cancellation.
\r
250 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
251 _taskPoolJob_t * const pJob,
\r
252 IotTaskPoolJobStatus_t * const pStatus );
\r
254 /* ---------------------------------------------------------------------------------------------- */
\r
256 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )
\r
258 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
260 /* At this time the task pool cannot be created before the scheduler has
\r
261 started because the function attempts to block on synchronization
\r
262 primitives (although I'm not sure why). */
\r
263 configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED );
\r
265 /* Guard against multiple attempts to create the system task pool in case
\r
266 this function is called by more than one library initialization routine. */
\r
267 if( _IotSystemTaskPool.running == false )
\r
269 /* Parameter checking. */
\r
270 TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );
\r
272 /* Create the system task pool pool. */
\r
273 TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, &_IotSystemTaskPool ) );
\r
276 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
278 /*-----------------------------------------------------------*/
\r
280 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,
\r
281 void * pUserContext,
\r
282 IotTaskPoolJobStorage_t * const pJobStorage,
\r
283 IotTaskPoolJob_t * const ppJob )
\r
285 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
287 /* Parameter checking. */
\r
288 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
289 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobStorage );
\r
290 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
292 /* Build a job around the user-provided storage. */
\r
293 _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext, true );
\r
295 *ppJob = ( IotTaskPoolJob_t ) pJobStorage;
\r
297 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
300 /*-----------------------------------------------------------*/
\r
302 IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
303 IotTaskPoolRoutine_t userCallback,
\r
304 void * pUserContext,
\r
305 IotTaskPoolJob_t * const ppJob )
\r
307 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
309 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
310 _taskPoolJob_t * pTempJob = NULL;
\r
312 /* This lean version of the task pool only supports the task pool created
\r
313 by this library (the system task pool). NULL means use the system task
\r
314 pool - no other values are allowed. Use the full implementation of this
\r
315 library if you want multiple task pools (there is more than one task in
\r
317 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
319 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
321 ( void ) taskPoolHandle;
\r
323 /* Parameter checking. */
\r
324 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
325 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
327 taskENTER_CRITICAL();
\r
329 /* Bail out early if this task pool is shutting down. */
\r
330 pTempJob = _fetchOrAllocateJob( &pTaskPool->jobsCache );
\r
332 taskEXIT_CRITICAL();
\r
334 if( pTempJob == NULL )
\r
336 IotLogInfo( "Failed to allocate a job." );
\r
338 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
341 _initializeJob( pTempJob, userCallback, pUserContext, false );
\r
345 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
348 /*-----------------------------------------------------------*/
\r
350 IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
351 IotTaskPoolJob_t pJobHandle )
\r
353 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
355 _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle;
\r
357 /* This lean version of the task pool only supports the task pool created
\r
358 by this library (the system task pool). NULL means use the system task
\r
359 pool - no other values are allowed. Use the full implementation of this
\r
360 library if you want multiple task pools (there is more than one task in
\r
362 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
364 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
366 ( void ) taskPoolHandle;
\r
368 /* Parameter checking. */
\r
369 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle );
\r
371 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
373 _destroyJob( pJob );
\r
375 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
378 /*-----------------------------------------------------------*/
\r
380 IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle,
\r
381 IotTaskPoolJob_t pJob )
\r
383 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
385 _taskPool_t * pTaskPool = ( _taskPool_t * ) &_IotSystemTaskPool;
\r
387 /* This lean version of the task pool only supports the task pool created
\r
388 by this library (the system task pool). NULL means use the system task
\r
389 pool - no other values are allowed. Use the full implementation of this
\r
390 library if you want multiple task pools (there is more than one task in
\r
392 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
394 /* Ensure unused parameters do not cause compiler warnings in case
\r
395 configASSERT() is not defined. */
\r
396 ( void ) taskPoolHandle;
\r
398 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
400 taskENTER_CRITICAL();
\r
402 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
404 _recycleJob( &pTaskPool->jobsCache, pJob );
\r
406 taskEXIT_CRITICAL();
\r
408 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
411 /*-----------------------------------------------------------*/
\r
413 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,
\r
414 IotTaskPoolJob_t pJob,
\r
417 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
419 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
421 /* Task pool must have been created. */
\r
422 configASSERT( pTaskPool->running != false );
\r
424 /* This lean version of the task pool only supports the task pool created
\r
425 by this library (the system task pool). NULL means use the system task
\r
426 pool - no other values are allowed. Use the full implementation of this
\r
427 library if you want multiple task pools (there is more than one task in
\r
429 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
431 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
433 ( void ) taskPoolHandle;
\r
435 /* Parameter checking. */
\r
436 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
437 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( ( flags != 0UL ) && ( flags != IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );
\r
439 taskENTER_CRITICAL(); //_RB_ Critical section is too long - does the whole thing need to be protected?
\r
441 _scheduleInternal( pTaskPool, pJob );
\r
443 taskEXIT_CRITICAL();
\r
445 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
448 /*-----------------------------------------------------------*/
\r
450 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,
\r
451 IotTaskPoolJob_t job,
\r
454 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
456 _taskPool_t * pTaskPool = &_IotSystemTaskPool;
\r
458 /* This lean version of the task pool only supports the task pool created
\r
459 by this library (the system task pool). NULL means use the system task
\r
460 pool - no other values are allowed. Use the full implementation of this
\r
461 library if you want multiple task pools (there is more than one task in
\r
463 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
465 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
467 if( timeMs == 0UL )
\r
469 TASKPOOL_SET_AND_GOTO_CLEANUP( IotTaskPool_Schedule( pTaskPool, job, 0 ) );
\r
472 taskENTER_CRITICAL();
\r
474 _taskPoolTimerEvent_t * pTimerEvent = IotTaskPool_MallocTimerEvent( sizeof( _taskPoolTimerEvent_t ) );
\r
476 if( pTimerEvent == NULL )
\r
478 taskEXIT_CRITICAL();
\r
480 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
483 IotLink_t * pTimerEventLink;
\r
485 TickType_t now = xTaskGetTickCount();
\r
487 pTimerEvent->link.pNext = NULL;
\r
488 pTimerEvent->link.pPrevious = NULL;
\r
489 pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );
\r
490 pTimerEvent->job = job; //_RB_ Think up to here can be outside the critical section.
\r
492 /* Append the timer event to the timer list. */
\r
493 IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );
\r
495 /* Update the job status to 'scheduled'. */
\r
496 job->status = IOT_TASKPOOL_STATUS_DEFERRED;
\r
498 /* Peek the first event in the timer event list. There must be at least one,
\r
499 * since we just inserted it. */
\r
500 pTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
501 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
503 /* If the event we inserted is at the front of the queue, then
\r
504 * we need to reschedule the underlying timer. */
\r
505 if( pTimerEventLink == &pTimerEvent->link )
\r
507 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );
\r
509 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
512 taskEXIT_CRITICAL();
\r
514 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
517 /*-----------------------------------------------------------*/
\r
519 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,
\r
520 IotTaskPoolJob_t job,
\r
521 IotTaskPoolJobStatus_t * const pStatus )
\r
523 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
525 /* This lean version of the task pool only supports the task pool created
\r
526 by this library (the system task pool). NULL means use the system task
\r
527 pool - no other values are allowed. Use the full implementation of this
\r
528 library if you want multiple task pools (there is more than one task in
\r
530 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
531 ( void ) taskPoolHandle;
\r
533 /* Parameter checking. */
\r
534 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
535 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus );
\r
536 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
538 taskENTER_CRITICAL();
\r
540 *pStatus = job->status;
\r
542 taskEXIT_CRITICAL();
\r
544 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
547 /*-----------------------------------------------------------*/
\r
549 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,
\r
550 IotTaskPoolJob_t job,
\r
551 IotTaskPoolJobStatus_t * const pStatus )
\r
553 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
555 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
557 /* This lean version of the task pool only supports the task pool created
\r
558 by this library (the system task pool). NULL means use the system task
\r
559 pool - no other values are allowed. Use the full implementation of this
\r
560 library if you want multiple task pools (there is more than one task in
\r
562 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
563 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
565 if( pStatus != NULL )
\r
567 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
570 taskENTER_CRITICAL();
\r
572 status = _tryCancelInternal( pTaskPool, job, pStatus );
\r
574 taskEXIT_CRITICAL();
\r
576 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
579 /*-----------------------------------------------------------*/
\r
581 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )
\r
583 return ( IotTaskPoolJobStorage_t * ) pJob;
\r
586 /*-----------------------------------------------------------*/
\r
588 const char * IotTaskPool_strerror( IotTaskPoolError_t status )
\r
590 const char * pMessage = NULL;
\r
594 case IOT_TASKPOOL_SUCCESS:
\r
595 pMessage = "SUCCESS";
\r
598 case IOT_TASKPOOL_BAD_PARAMETER:
\r
599 pMessage = "BAD PARAMETER";
\r
602 case IOT_TASKPOOL_ILLEGAL_OPERATION:
\r
603 pMessage = "ILLEGAL OPERATION";
\r
606 case IOT_TASKPOOL_NO_MEMORY:
\r
607 pMessage = "NO MEMORY";
\r
610 case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:
\r
611 pMessage = "SHUTDOWN IN PROGRESS";
\r
614 case IOT_TASKPOOL_CANCEL_FAILED:
\r
615 pMessage = "CANCEL FAILED";
\r
619 pMessage = "INVALID STATUS";
\r
626 /* ---------------------------------------------------------------------------------------------- */
\r
627 /* ---------------------------------------------------------------------------------------------- */
\r
628 /* ---------------------------------------------------------------------------------------------- */
\r
630 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo )
\r
632 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
634 /* Check input values for consistency. */
\r
635 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pInfo );
\r
636 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads > pInfo->maxThreads );
\r
637 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads < 1UL );
\r
638 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->maxThreads < 1UL );
\r
640 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
643 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool )
\r
645 /* Initialize a job data structures that require no de-initialization.
\r
646 * All other data structures carry a value of 'NULL' before initailization.
\r
648 IotDeQueue_Create( &pTaskPool->dispatchQueue );
\r
649 IotListDouble_Create( &pTaskPool->timerEventsList );
\r
651 _initJobsCache( &pTaskPool->jobsCache );
\r
653 /* Initialize the semaphore for waiting for incoming work. Cannot fail as
\r
654 statically allocated. */
\r
655 pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );
\r
658 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
659 _taskPool_t * const pTaskPool )
\r
661 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
663 /* Static TCB structures and arrays to be used by statically allocated
\r
665 static StaticTask_t workerTaskTCBs[ IOT_TASKPOOL_NUMBER_OF_WORKERS ];
\r
666 static StackType_t workerTaskStacks[ IOT_TASKPOOL_NUMBER_OF_WORKERS ][ IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ) ];
\r
668 /* Static structure to hold te software timer. */
\r
669 static StaticTimer_t staticTimer;
\r
671 uint32_t threadsCreated = 0; /* Although initialised before use removing the initialiser here results in compiler warnings. */
\r
672 char taskName[ 10 ];
\r
674 /* Check input values for consistency. */
\r
675 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
677 /* Zero out all data structures. */
\r
678 memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );
\r
680 /* Initialize all internal data structure prior to creating all threads. */
\r
681 _initTaskPoolControlStructures( pTaskPool );
\r
683 /* Create the timer for a new connection. */
\r
684 pTaskPool->timer = xTimerCreateStatic( NULL, /* Text name for the timer, only used for debugging. */
\r
685 portMAX_DELAY, /* Timer period in ticks. */
\r
686 pdFALSE, /* pdFALSE means its a one-shot timer. */
\r
687 ( void * ) pTaskPool, /* Parameter passed into callback. */
\r
688 _timerCallback, /* Callback that executes when the timer expires. */
\r
689 &staticTimer ); /* Static storage for the timer's data structure. */
\r
691 /* The task pool will initialize the minimum number of threads requested by the user upon start.
\r
692 Note this tailored version of the task pool does not autoscale, but fixes the number of tasks
\r
693 in the pool to the originally specified minimum, and the specified maximum value is ignored. */
\r
694 /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */
\r
695 for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )
\r
697 /* Generate a unique name for the task. */
\r
698 snprintf( taskName, sizeof( taskName ), "pool%d", ( int ) threadsCreated );
\r
700 xTaskCreateStatic( _taskPoolWorker, /* Function that implements the task. */
\r
701 taskName, /* Text name for the task, used for debugging only. */
\r
702 IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ), /* xTaskCreate() expects the stack size to be specified in words. */
\r
703 pTaskPool, /* Parameter passed into the task. */
\r
704 pInfo->priority, /* Priority at which the task starts running. */
\r
705 &( workerTaskStacks[ threadsCreated ][ 0 ] ), /* Pointer to static storage for the task's stack. */
\r
706 &( workerTaskTCBs[ threadsCreated ] ) ); /* Pointer to static storage for te task's TCB. */
\r
708 /* Upon successful thread creation, increase the number of active threads. */
\r
709 pTaskPool->activeThreads++;
\r
712 pTaskPool->running = true;
\r
714 TASKPOOL_FUNCTION_CLEANUP();
\r
716 TASKPOOL_FUNCTION_CLEANUP_END();
\r
719 /*-----------------------------------------------------------*/
\r
721 static void _destroyTaskPool( _taskPool_t * const pTaskPool )
\r
723 if( pTaskPool->timer != NULL )
\r
725 xTimerDelete( pTaskPool->timer, 0 );
\r
729 /* ---------------------------------------------------------------------------------------------- */
\r
731 static void _taskPoolWorker( void * pUserContext )
\r
733 IotTaskPool_Assert( pUserContext != NULL );
\r
735 IotTaskPoolRoutine_t userCallback = NULL;
\r
737 /* Extract pTaskPool pointer from context. */
\r
738 _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;
\r
740 /* OUTER LOOP: it controls the lifetime of the worker thread. */
\r
743 IotLink_t * pFirst = NULL;
\r
744 _taskPoolJob_t * pJob = NULL;
\r
746 /* Wait on incoming notifications... */
\r
747 configASSERT( pTaskPool->dispatchSignal );
\r
748 xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY );
\r
750 /* Acquire the lock to check for incoming notifications. */
\r
751 taskENTER_CRITICAL();
\r
753 /* Dequeue the first job in FIFO order. */
\r
754 pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
756 /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */
\r
757 if( pFirst != NULL )
\r
759 /* Extract the job from its link. */
\r
760 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );
\r
762 /* Update status to 'executing'. */
\r
763 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED; /*_RB_ Should this be 'executing'? */
\r
764 userCallback = pJob->userCallback;
\r
767 taskEXIT_CRITICAL();
\r
769 /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */
\r
770 while( pJob != NULL )
\r
772 /* Process the job by invoking the associated callback with the user context.
\r
773 * This task pool thread will not be available until the user callback returns.
\r
776 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
777 IotTaskPool_Assert( userCallback != NULL );
\r
779 userCallback( pTaskPool, pJob, pJob->pUserContext );
\r
781 /* This job is finished, clear its pointer. */
\r
783 userCallback = NULL;
\r
786 /* Acquire the lock before updating the job status. */
\r
787 taskENTER_CRITICAL();
\r
789 /* Try and dequeue the next job in the dispatch queue. */
\r
790 IotLink_t * pItem = NULL;
\r
792 /* Dequeue the next job from the dispatch queue. */
\r
793 pItem = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
795 /* If there is no job left in the dispatch queue, update the worker status and leave. */
\r
796 if( pItem == NULL )
\r
798 taskEXIT_CRITICAL();
\r
800 /* Abandon the INNER LOOP. Execution will transfer back to the OUTER LOOP condition. */
\r
805 pJob = IotLink_Container( _taskPoolJob_t, pItem, link );
\r
807 userCallback = pJob->userCallback;
\r
810 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
812 taskEXIT_CRITICAL();
\r
817 /* ---------------------------------------------------------------------------------------------- */
\r
819 static void _initJobsCache( _taskPoolCache_t * const pCache )
\r
821 IotDeQueue_Create( &pCache->freeList );
\r
823 pCache->freeCount = 0;
\r
826 /*-----------------------------------------------------------*/
\r
828 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
829 IotTaskPoolRoutine_t userCallback,
\r
830 void * pUserContext,
\r
833 pJob->link.pNext = NULL;
\r
834 pJob->link.pPrevious = NULL;
\r
835 pJob->userCallback = userCallback;
\r
836 pJob->pUserContext = pUserContext;
\r
840 pJob->flags = IOT_TASK_POOL_INTERNAL_STATIC;
\r
841 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
845 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
849 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache )
\r
851 _taskPoolJob_t * pJob = NULL;
\r
852 IotLink_t * pLink = IotListDouble_RemoveHead( &( pCache->freeList ) );
\r
854 if( pLink != NULL )
\r
856 pJob = IotLink_Container( _taskPoolJob_t, pLink, link );
\r
859 /* If there is no available job in the cache, then allocate one. */
\r
862 pJob = ( _taskPoolJob_t * ) IotTaskPool_MallocJob( sizeof( _taskPoolJob_t ) );
\r
866 memset( pJob, 0x00, sizeof( _taskPoolJob_t ) );
\r
870 /* Log allocation failure for troubleshooting purposes. */
\r
871 IotLogInfo( "Failed to allocate job." );
\r
874 /* If there was a job in the cache, then make sure we keep the counters up-to-date. */
\r
877 IotTaskPool_Assert( pCache->freeCount > 0 );
\r
879 pCache->freeCount--;
\r
885 /*-----------------------------------------------------------*/
\r
887 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
888 _taskPoolJob_t * const pJob )
\r
890 /* We should never try and recycling a job that is linked into some queue. */
\r
891 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );//_RB_ Seems to be duplicate of test before this is called.
\r
893 /* We will recycle the job if there is space in the cache. */
\r
894 if( pCache->freeCount < IOT_TASKPOOL_JOBS_RECYCLE_LIMIT )
\r
896 /* Destroy user data, for added safety&security. */
\r
897 pJob->userCallback = NULL;
\r
898 pJob->pUserContext = NULL;
\r
900 /* Reset the status for added debuggability. */
\r
901 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
903 IotListDouble_InsertTail( &pCache->freeList, &pJob->link );
\r
905 pCache->freeCount++;
\r
907 IotTaskPool_Assert( pCache->freeCount >= 1 );
\r
911 _destroyJob( pJob );
\r
915 /*-----------------------------------------------------------*/
\r
917 static void _destroyJob( _taskPoolJob_t * const pJob )
\r
919 /* Destroy user data, for added safety & security. */
\r
920 pJob->userCallback = NULL;
\r
921 pJob->pUserContext = NULL;
\r
923 /* Reset the status for added debuggability. */
\r
924 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
926 /* Only dispose of dynamically allocated jobs. */
\r
927 if( ( pJob->flags & IOT_TASK_POOL_INTERNAL_STATIC ) == 0UL )
\r
929 IotTaskPool_FreeJob( pJob );
\r
933 /* ---------------------------------------------------------------------------------------------- */
\r
935 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
936 _taskPoolJob_t * const pJob )
\r
938 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
940 /* Update the job status to 'scheduled'. */
\r
941 pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;
\r
943 /* Append the job to the dispatch queue. */
\r
944 IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link );
\r
946 /* Signal a worker to pick up the job. */
\r
947 xSemaphoreGive( pTaskPool->dispatchSignal );
\r
949 TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL();
\r
952 /*-----------------------------------------------------------*/
\r
954 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
957 const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;
\r
959 const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
961 if( pJob == pTimerEvent->job )
\r
969 /*-----------------------------------------------------------*/
\r
971 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
972 _taskPoolJob_t * const pJob,
\r
973 IotTaskPoolJobStatus_t * const pStatus )
\r
975 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
977 bool cancelable = false;
\r
979 /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */
\r
981 IotTaskPoolJobStatus_t currentStatus = pJob->status;
\r
983 switch( currentStatus )
\r
985 case IOT_TASKPOOL_STATUS_READY:
\r
986 case IOT_TASKPOOL_STATUS_DEFERRED:
\r
987 case IOT_TASKPOOL_STATUS_SCHEDULED:
\r
988 case IOT_TASKPOOL_STATUS_CANCELED:
\r
992 case IOT_TASKPOOL_STATUS_COMPLETED:
\r
993 /* Log message for debug purposes. */
\r
994 IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );
\r
998 /* Log message for debug purposes purposes. */
\r
999 IotLogError( "Attempt to cancel a job with an undefined state." );
\r
1003 /* Update the returned status to the current status of the job. */
\r
1004 if( pStatus != NULL )
\r
1006 *pStatus = currentStatus;
\r
1009 if( cancelable == false )
\r
1011 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_CANCEL_FAILED );
\r
1015 /* Update the status of the job. */
\r
1016 pJob->status = IOT_TASKPOOL_STATUS_CANCELED;
\r
1018 /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch
\r
1019 * queue and signal any waiting threads. */
\r
1020 if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )
\r
1022 /* A scheduled work items must be in the dispatch queue. */
\r
1023 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) );
\r
1025 IotDeQueue_Remove( &pJob->link );
\r
1028 /* If the job current status is 'deferred' then the job has to be pending
\r
1029 * in the timeouts queue. */
\r
1030 else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )
\r
1032 /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */
\r
1033 IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &pTaskPool->timerEventsList, NULL, _matchJobByPointer, pJob );
\r
1034 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
1036 if( pTimerEventLink != NULL )
\r
1038 bool shouldReschedule = false;
\r
1040 /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer
\r
1041 * with the next job timeout */
\r
1042 IotLink_t * pHeadLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1044 if( pHeadLink == pTimerEventLink )
\r
1046 shouldReschedule = true;
\r
1049 /* Remove the timer event associated with the canceled job and free the associated memory. */
\r
1050 IotListDouble_Remove( pTimerEventLink );
\r
1051 IotTaskPool_FreeTimerEvent( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ) );
\r
1053 if( shouldReschedule )
\r
1055 IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1057 if( pNextTimerEventLink != NULL )
\r
1059 _rescheduleDeferredJobsTimer( pTaskPool->timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );
\r
1066 /* A cancelable job status should be either 'scheduled' or 'deferrred'. */
\r
1067 IotTaskPool_Assert( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );
\r
1071 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
1074 /*-----------------------------------------------------------*/
\r
1076 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
1077 const IotLink_t * const pTimerEventLink2 )
\r
1079 const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1082 const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1086 if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )
\r
1091 if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )
\r
1099 /*-----------------------------------------------------------*/
\r
1101 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
1102 _taskPoolTimerEvent_t * const pFirstTimerEvent )
\r
1104 uint64_t delta = 0;
\r
1105 TickType_t now = xTaskGetTickCount();
\r
1107 if( pFirstTimerEvent->expirationTime > now )
\r
1109 delta = pFirstTimerEvent->expirationTime - now;
\r
1112 if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )
\r
1114 delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */
\r
1117 IotTaskPool_Assert( delta > 0 );
\r
1119 if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )
\r
1121 IotLogWarn( "Failed to re-arm timer for task pool" );
\r
1125 /*-----------------------------------------------------------*/
\r
1127 static void _timerCallback( TimerHandle_t xTimer )
\r
1129 _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );
\r
1131 IotTaskPool_Assert( pTaskPool );
\r
1133 _taskPoolTimerEvent_t * pTimerEvent = NULL;
\r
1135 IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );
\r
1137 /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.
\r
1138 * If this mutex cannot be locked it means that another thread is manipulating the
\r
1139 * timeouts list, and will reset the timer to fire again, although it will be late.
\r
1141 taskENTER_CRITICAL(); //_RB_ Critical section is too long.
\r
1143 /* Dispatch all deferred job whose timer expired, then reset the timer for the next
\r
1144 * job down the line. */
\r
1147 /* Peek the first event in the timer event list. */
\r
1148 IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1150 /* Check if the timer misfired for any reason. */
\r
1151 if( pLink != NULL )
\r
1153 /* Record the current time. */
\r
1154 TickType_t now = xTaskGetTickCount();
\r
1156 /* Extract the job from its envelope. */
\r
1157 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
1159 /* Check if the first event should be processed now. */
\r
1160 if( pTimerEvent->expirationTime <= now )
\r
1162 /* Remove the timer event for immediate processing. */
\r
1163 IotListDouble_Remove( &( pTimerEvent->link ) );
\r
1167 /* The first element in the timer queue shouldn't be processed yet.
\r
1168 * Arm the timer for when it should be processed and leave altogether. */
\r
1169 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
1174 /* If there are no timer events to process, terminate this thread. */
\r
1177 IotLogDebug( "No further timer events to process. Exiting timer thread." );
\r
1182 IotLogDebug( "Scheduling job from timer event." );
\r
1184 /* Queue the job associated with the received timer event. */
\r
1185 ( void ) _scheduleInternal( pTaskPool, pTimerEvent->job );
\r
1187 /* Free the timer event. */
\r
1188 IotTaskPool_FreeTimerEvent( pTimerEvent );
\r
1191 taskEXIT_CRITICAL();
\r