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
30 /* Kernel includes. */
\r
31 #include "FreeRTOS.h"
\r
34 /* IoT libraries includes. */
\r
35 #include "iot_config.h"
\r
37 /* Standard includes. */
\r
38 #include <stdbool.h>
\r
44 #if !defined( configSUPPORT_STATIC_ALLOCATION ) || ( configSUPPORT_STATIC_ALLOCATION != 1 )
\r
45 #error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.
\r
48 /* Task pool internal include. */
\r
49 #include "private/iot_taskpool_internal.h"
\r
52 * @brief Maximum semaphore value for wait operations.
\r
54 #define TASKPOOL_MAX_SEM_VALUE 0xFFFF
\r
57 * @brief Reschedule delay in milliseconds for deferred jobs.
\r
59 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS ( 10ULL )
\r
61 /* ---------------------------------------------------------------------------------- */
\r
64 * Doxygen should ignore this section.
\r
66 * @brief The system task pool handle for all libraries to use.
\r
67 * User application can use the system task pool as well knowing that the usage will be shared with
\r
68 * the system libraries as well. The system task pool needs to be initialized before any library is used or
\r
69 * before any code that posts jobs to the task pool runs.
\r
71 static _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };
\r
73 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */
\r
76 * @brief Initializes one instance of a Task pool cache.
\r
78 * @param[in] pCache The pre-allocated instance of the cache to initialize.
\r
80 static void _initJobsCache( _taskPoolCache_t * const pCache );
\r
83 * @brief Initialize a job.
\r
85 * @param[in] pJob The job to initialize.
\r
86 * @param[in] userCallback The user callback for the job.
\r
87 * @param[in] pUserContext The context tp be passed to the callback.
\r
88 * @param[in] isStatic A flag to indicate whether the job is statically or synamically allocated.
\r
90 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
91 IotTaskPoolRoutine_t userCallback,
\r
92 void * pUserContext,
\r
96 * @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
98 * @param[in] pCache The instance of the cache to extract the job from.
\r
100 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache );
\r
103 * Recycles one instance of a job into the cache or, if the cache is full, it destroys it.
\r
105 * @param[in] pCache The instance of the cache to recycle the job into.
\r
106 * @param[in] pJob The job to recycle.
\r
109 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
110 _taskPoolJob_t * const pJob );
\r
113 * Destroys one instance of a job.
\r
115 * @param[in] pJob The job to destroy.
\r
118 static void _destroyJob( _taskPoolJob_t * const pJob );
\r
120 /* -------------- The worker thread procedure for a task pool thread -------------- */
\r
123 * The procedure for a task pool worker thread.
\r
125 * @param[in] pUserContext The user context.
\r
128 static void _taskPoolWorker( void * pUserContext );
\r
130 /* -------------- Convenience functions to handle timer events -------------- */
\r
133 * Comparer for the time list.
\r
135 * param[in] pTimerEventLink1 The link to the first timer event.
\r
136 * param[in] pTimerEventLink1 The link to the first timer event.
\r
138 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
139 const IotLink_t * const pTimerEventLink2 );
\r
142 * Reschedules the timer for handling deferred jobs to the next timeout.
\r
144 * param[in] timer The timer to reschedule.
\r
145 * param[in] pFirstTimerEvent The timer event that carries the timeout and job inforamtion.
\r
147 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
148 _taskPoolTimerEvent_t * const pFirstTimerEvent );
\r
151 * The task pool timer procedure for scheduling deferred jobs.
\r
153 * param[in] timer The timer to handle.
\r
155 static void _timerCallback( TimerHandle_t xTimer );
\r
157 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */
\r
160 * Parameter validation for a task pool initialization.
\r
162 * @param[in] pInfo The initialization information for the task pool.
\r
165 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo );
\r
168 * Initializes a pre-allocated instance of a task pool.
\r
170 * @param[in] pTaskPool The pre-allocated instance of the task pool to initialize.
\r
173 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool );
\r
176 * Initializes a pre-allocated instance of a task pool.
\r
178 * @param[in] pInfo The initialization information for the task pool.
\r
179 * @param[out] pTaskPool A pointer to the task pool data structure to initialize.
\r
182 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
183 _taskPool_t * const pTaskPool );
\r
186 * Destroys one instance of a task pool.
\r
188 * @param[in] pTaskPool The task pool to destroy.
\r
191 static void _destroyTaskPool( _taskPool_t * const pTaskPool );
\r
194 * Places a job in the dispatch queue.
\r
196 * @param[in] pTaskPool The task pool to scheduel the job with.
\r
197 * @param[in] pJob The job to schedule.
\r
198 * @param[in] flags The job flags.
\r
201 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
202 _taskPoolJob_t * const pJob );
\r
204 * Matches a deferred job in the timer queue with its timer event wrapper.
\r
206 * @param[in] pLink A pointer to the timer event link in the timer queue.
\r
207 * @param[in] pMatch A pointer to the job to match.
\r
210 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
214 * Tries to cancel a job.
\r
216 * @param[in] pTaskPool The task pool to cancel an operation against.
\r
217 * @param[in] pJob The job to cancel.
\r
218 * @param[out] pStatus The status of the job at the time of cancellation.
\r
221 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
222 _taskPoolJob_t * const pJob,
\r
223 IotTaskPoolJobStatus_t * const pStatus );
\r
225 /* ---------------------------------------------------------------------------------------------- */
\r
228 * The full IoT Task Pool Library has many use cases, including Linux
\r
229 * development. Â Typical FreeRTOS use cases do not require the full
\r
230 * functionality so optimised version is provided specifically for use with
\r
231 * FreeRTOS. Unlike the full version, this optimised version:
\r
232 * + Only supports a single task pool (system task pool) at a time.
\r
233 * + Does not auto-scale by dynamically adding more tasks if the number of
\r
234 * + tasks in the pool becomes exhausted. Â The number of tasks in the pool
\r
235 * are fixed at compile time. See the task pool configuration options for
\r
236 * more information.
\r
237 * + Cannot be shut down - it exists for the lifetime of the application.
\r
239 * As such this file does not implement the following API functions:
\r
240 * + IotTaskPool_GetSystemTaskPool()
\r
241 * + IotTaskPool_Create()
\r
242 * + IotTaskPool_Destroy()
\r
243 * + IotTaskPool_SetMaxThreads()
\r
245 * Users who are interested in the functionality of the full IoT Task Pool
\r
246 * library can us it in place of this optimised version.
\r
249 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )
\r
251 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
253 /* At this time the task pool cannot be created before the scheduler has
\r
254 started because the function attempts to block on synchronization
\r
255 primitives (although I'm not sure why). */
\r
256 configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED );
\r
258 /* Guard against multiple attempts to create the system task pool in case
\r
259 this function is called by more than one library initialization routine. */
\r
260 if( _IotSystemTaskPool.running == false )
\r
262 /* Parameter checking. */
\r
263 TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );
\r
265 /* Create the system task pool pool. */
\r
266 TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, &_IotSystemTaskPool ) );
\r
269 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
271 /*-----------------------------------------------------------*/
\r
273 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,
\r
274 void * pUserContext,
\r
275 IotTaskPoolJobStorage_t * const pJobStorage,
\r
276 IotTaskPoolJob_t * const ppJob )
\r
278 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
280 /* Parameter checking. */
\r
281 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
282 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobStorage );
\r
283 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
285 /* Build a job around the user-provided storage. */
\r
286 _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext, true );
\r
288 *ppJob = ( IotTaskPoolJob_t ) pJobStorage;
\r
290 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
293 /*-----------------------------------------------------------*/
\r
295 IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
296 IotTaskPoolRoutine_t userCallback,
\r
297 void * pUserContext,
\r
298 IotTaskPoolJob_t * const ppJob )
\r
300 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
302 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
303 _taskPoolJob_t * pTempJob = NULL;
\r
305 /* This lean version of the task pool only supports the task pool created
\r
306 by this library (the system task pool). NULL means use the system task
\r
307 pool - no other values are allowed. Use the full implementation of this
\r
308 library if you want multiple task pools (there is more than one task in
\r
310 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
312 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
314 ( void ) taskPoolHandle;
\r
316 /* Parameter checking. */
\r
317 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
318 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
320 taskENTER_CRITICAL();
\r
322 /* Bail out early if this task pool is shutting down. */
\r
323 pTempJob = _fetchOrAllocateJob( &pTaskPool->jobsCache );
\r
325 taskEXIT_CRITICAL();
\r
327 if( pTempJob == NULL )
\r
329 IotLogInfo( "Failed to allocate a job." );
\r
331 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
334 _initializeJob( pTempJob, userCallback, pUserContext, false );
\r
338 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
341 /*-----------------------------------------------------------*/
\r
343 IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
344 IotTaskPoolJob_t pJobHandle )
\r
346 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
348 _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle;
\r
350 /* This lean version of the task pool only supports the task pool created
\r
351 by this library (the system task pool). NULL means use the system task
\r
352 pool - no other values are allowed. Use the full implementation of this
\r
353 library if you want multiple task pools (there is more than one task in
\r
355 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
357 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
359 ( void ) taskPoolHandle;
\r
361 /* Parameter checking. */
\r
362 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle );
\r
364 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
366 _destroyJob( pJob );
\r
368 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
371 /*-----------------------------------------------------------*/
\r
373 IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle,
\r
374 IotTaskPoolJob_t pJob )
\r
376 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
378 _taskPool_t * pTaskPool = ( _taskPool_t * ) &_IotSystemTaskPool;
\r
380 /* This lean version of the task pool only supports the task pool created
\r
381 by this library (the system task pool). NULL means use the system task
\r
382 pool - no other values are allowed. Use the full implementation of this
\r
383 library if you want multiple task pools (there is more than one task in
\r
385 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
387 /* Ensure unused parameters do not cause compiler warnings in case
\r
388 configASSERT() is not defined. */
\r
389 ( void ) taskPoolHandle;
\r
391 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
393 taskENTER_CRITICAL();
\r
395 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
397 _recycleJob( &pTaskPool->jobsCache, pJob );
\r
399 taskEXIT_CRITICAL();
\r
401 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
404 /*-----------------------------------------------------------*/
\r
406 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,
\r
407 IotTaskPoolJob_t pJob,
\r
410 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
412 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
414 /* Task pool must have been created. */
\r
415 configASSERT( pTaskPool->running != false );
\r
417 /* This lean version of the task pool only supports the task pool created
\r
418 by this library (the system task pool). NULL means use the system task
\r
419 pool - no other values are allowed. Use the full implementation of this
\r
420 library if you want multiple task pools (there is more than one task in
\r
422 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
424 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
426 ( void ) taskPoolHandle;
\r
428 /* Parameter checking. */
\r
429 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
430 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( ( flags != 0UL ) && ( flags != IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );
\r
432 taskENTER_CRITICAL(); //_RB_ Critical section is too long - does the whole thing need to be protected?
\r
434 _scheduleInternal( pTaskPool, pJob );
\r
436 taskEXIT_CRITICAL();
\r
438 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
441 /*-----------------------------------------------------------*/
\r
443 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,
\r
444 IotTaskPoolJob_t job,
\r
447 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
449 _taskPool_t * pTaskPool = &_IotSystemTaskPool;
\r
451 /* This lean version of the task pool only supports the task pool created
\r
452 by this library (the system task pool). NULL means use the system task
\r
453 pool - no other values are allowed. Use the full implementation of this
\r
454 library if you want multiple task pools (there is more than one task in
\r
456 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
458 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
460 if( timeMs == 0UL )
\r
462 TASKPOOL_SET_AND_GOTO_CLEANUP( IotTaskPool_Schedule( pTaskPool, job, 0 ) );
\r
465 taskENTER_CRITICAL();
\r
467 _taskPoolTimerEvent_t * pTimerEvent = IotTaskPool_MallocTimerEvent( sizeof( _taskPoolTimerEvent_t ) );
\r
469 if( pTimerEvent == NULL )
\r
471 taskEXIT_CRITICAL();
\r
473 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
476 IotLink_t * pTimerEventLink;
\r
478 TickType_t now = xTaskGetTickCount();
\r
480 pTimerEvent->link.pNext = NULL;
\r
481 pTimerEvent->link.pPrevious = NULL;
\r
482 pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );
\r
483 pTimerEvent->job = job; //_RB_ Think up to here can be outside the critical section.
\r
485 /* Append the timer event to the timer list. */
\r
486 IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );
\r
488 /* Update the job status to 'scheduled'. */
\r
489 job->status = IOT_TASKPOOL_STATUS_DEFERRED;
\r
491 /* Peek the first event in the timer event list. There must be at least one,
\r
492 * since we just inserted it. */
\r
493 pTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
494 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
496 /* If the event we inserted is at the front of the queue, then
\r
497 * we need to reschedule the underlying timer. */
\r
498 if( pTimerEventLink == &pTimerEvent->link )
\r
500 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );
\r
502 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
505 taskEXIT_CRITICAL();
\r
507 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
510 /*-----------------------------------------------------------*/
\r
512 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,
\r
513 IotTaskPoolJob_t job,
\r
514 IotTaskPoolJobStatus_t * const pStatus )
\r
516 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
518 /* This lean version of the task pool only supports the task pool created
\r
519 by this library (the system task pool). NULL means use the system task
\r
520 pool - no other values are allowed. Use the full implementation of this
\r
521 library if you want multiple task pools (there is more than one task in
\r
523 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
524 ( void ) taskPoolHandle;
\r
526 /* Parameter checking. */
\r
527 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
528 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus );
\r
529 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
531 taskENTER_CRITICAL();
\r
533 *pStatus = job->status;
\r
535 taskEXIT_CRITICAL();
\r
537 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
540 /*-----------------------------------------------------------*/
\r
542 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,
\r
543 IotTaskPoolJob_t job,
\r
544 IotTaskPoolJobStatus_t * const pStatus )
\r
546 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
548 _taskPool_t * const pTaskPool = &_IotSystemTaskPool;
\r
550 /* This lean version of the task pool only supports the task pool created
\r
551 by this library (the system task pool). NULL means use the system task
\r
552 pool - no other values are allowed. Use the full implementation of this
\r
553 library if you want multiple task pools (there is more than one task in
\r
555 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
556 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
558 if( pStatus != NULL )
\r
560 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
563 taskENTER_CRITICAL();
\r
565 status = _tryCancelInternal( pTaskPool, job, pStatus );
\r
567 taskEXIT_CRITICAL();
\r
569 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
572 /*-----------------------------------------------------------*/
\r
574 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )
\r
576 return ( IotTaskPoolJobStorage_t * ) pJob;
\r
579 /*-----------------------------------------------------------*/
\r
581 const char * IotTaskPool_strerror( IotTaskPoolError_t status )
\r
583 const char * pMessage = NULL;
\r
587 case IOT_TASKPOOL_SUCCESS:
\r
588 pMessage = "SUCCESS";
\r
591 case IOT_TASKPOOL_BAD_PARAMETER:
\r
592 pMessage = "BAD PARAMETER";
\r
595 case IOT_TASKPOOL_ILLEGAL_OPERATION:
\r
596 pMessage = "ILLEGAL OPERATION";
\r
599 case IOT_TASKPOOL_NO_MEMORY:
\r
600 pMessage = "NO MEMORY";
\r
603 case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:
\r
604 pMessage = "SHUTDOWN IN PROGRESS";
\r
607 case IOT_TASKPOOL_CANCEL_FAILED:
\r
608 pMessage = "CANCEL FAILED";
\r
612 pMessage = "INVALID STATUS";
\r
619 /* ---------------------------------------------------------------------------------------------- */
\r
620 /* ---------------------------------------------------------------------------------------------- */
\r
621 /* ---------------------------------------------------------------------------------------------- */
\r
623 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo )
\r
625 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
627 /* Check input values for consistency. */
\r
628 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pInfo );
\r
629 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads > pInfo->maxThreads );
\r
630 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads < 1UL );
\r
631 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->maxThreads < 1UL );
\r
633 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
636 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool )
\r
638 /* Initialize a job data structures that require no de-initialization.
\r
639 * All other data structures carry a value of 'NULL' before initailization.
\r
641 IotDeQueue_Create( &pTaskPool->dispatchQueue );
\r
642 IotListDouble_Create( &pTaskPool->timerEventsList );
\r
644 _initJobsCache( &pTaskPool->jobsCache );
\r
646 /* Initialize the semaphore for waiting for incoming work. Cannot fail as
\r
647 statically allocated. */
\r
648 pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );
\r
651 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
652 _taskPool_t * const pTaskPool )
\r
654 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
656 /* Static TCB structures and arrays to be used by statically allocated
\r
658 static StaticTask_t workerTaskTCBs[ IOT_TASKPOOL_NUMBER_OF_WORKERS ];
\r
659 static StackType_t workerTaskStacks[ IOT_TASKPOOL_NUMBER_OF_WORKERS ][ IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ) ];
\r
661 /* Static structure to hold te software timer. */
\r
662 static StaticTimer_t staticTimer;
\r
664 uint32_t threadsCreated = 0; /* Although initialised before use removing the initialiser here results in compiler warnings. */
\r
665 char taskName[ 10 ];
\r
667 /* Check input values for consistency. */
\r
668 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
670 /* Zero out all data structures. */
\r
671 memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );
\r
673 /* Initialize all internal data structure prior to creating all threads. */
\r
674 _initTaskPoolControlStructures( pTaskPool );
\r
676 /* Create the timer for a new connection. */
\r
677 pTaskPool->timer = xTimerCreateStatic( NULL, /* Text name for the timer, only used for debugging. */
\r
678 portMAX_DELAY, /* Timer period in ticks. */
\r
679 pdFALSE, /* pdFALSE means its a one-shot timer. */
\r
680 ( void * ) pTaskPool, /* Parameter passed into callback. */
\r
681 _timerCallback, /* Callback that executes when the timer expires. */
\r
682 &staticTimer ); /* Static storage for the timer's data structure. */
\r
684 /* The task pool will initialize the minimum number of threads requested by the user upon start.
\r
685 Note this tailored version of the task pool does not autoscale, but fixes the number of tasks
\r
686 in the pool to the originally specified minimum, and the specified maximum value is ignored. */
\r
687 /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */
\r
688 for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )
\r
690 /* Generate a unique name for the task. */
\r
691 snprintf( taskName, sizeof( taskName ), "pool%d", ( int ) threadsCreated );
\r
693 xTaskCreateStatic( _taskPoolWorker, /* Function that implements the task. */
\r
694 taskName, /* Text name for the task, used for debugging only. */
\r
695 IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ), /* xTaskCreate() expects the stack size to be specified in words. */
\r
696 pTaskPool, /* Parameter passed into the task. */
\r
697 pInfo->priority, /* Priority at which the task starts running. */
\r
698 &( workerTaskStacks[ threadsCreated ][ 0 ] ), /* Pointer to static storage for the task's stack. */
\r
699 &( workerTaskTCBs[ threadsCreated ] ) ); /* Pointer to static storage for te task's TCB. */
\r
701 /* Upon successful thread creation, increase the number of active threads. */
\r
702 pTaskPool->activeThreads++;
\r
705 pTaskPool->running = true;
\r
707 TASKPOOL_FUNCTION_CLEANUP();
\r
709 TASKPOOL_FUNCTION_CLEANUP_END();
\r
712 /*-----------------------------------------------------------*/
\r
714 static void _destroyTaskPool( _taskPool_t * const pTaskPool )
\r
716 if( pTaskPool->timer != NULL )
\r
718 xTimerDelete( pTaskPool->timer, 0 );
\r
722 /* ---------------------------------------------------------------------------------------------- */
\r
724 static void _taskPoolWorker( void * pUserContext )
\r
726 IotTaskPool_Assert( pUserContext != NULL );
\r
728 IotTaskPoolRoutine_t userCallback = NULL;
\r
730 /* Extract pTaskPool pointer from context. */
\r
731 _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;
\r
733 /* OUTER LOOP: it controls the lifetime of the worker thread. */
\r
736 IotLink_t * pFirst = NULL;
\r
737 _taskPoolJob_t * pJob = NULL;
\r
739 /* Wait on incoming notifications... */
\r
740 configASSERT( pTaskPool->dispatchSignal );
\r
741 xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY );
\r
743 /* Acquire the lock to check for incoming notifications. */
\r
744 taskENTER_CRITICAL();
\r
746 /* Dequeue the first job in FIFO order. */
\r
747 pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
749 /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */
\r
750 if( pFirst != NULL )
\r
752 /* Extract the job from its link. */
\r
753 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );
\r
755 /* Update status to 'executing'. */
\r
756 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED; /*_RB_ Should this be 'executing'? */
\r
757 userCallback = pJob->userCallback;
\r
760 taskEXIT_CRITICAL();
\r
762 /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */
\r
763 while( pJob != NULL )
\r
765 /* Process the job by invoking the associated callback with the user context.
\r
766 * This task pool thread will not be available until the user callback returns.
\r
769 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
770 IotTaskPool_Assert( userCallback != NULL );
\r
772 userCallback( pTaskPool, pJob, pJob->pUserContext );
\r
774 /* This job is finished, clear its pointer. */
\r
776 userCallback = NULL;
\r
779 /* Acquire the lock before updating the job status. */
\r
780 taskENTER_CRITICAL();
\r
782 /* Try and dequeue the next job in the dispatch queue. */
\r
783 IotLink_t * pItem = NULL;
\r
785 /* Dequeue the next job from the dispatch queue. */
\r
786 pItem = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
788 /* If there is no job left in the dispatch queue, update the worker status and leave. */
\r
789 if( pItem == NULL )
\r
791 taskEXIT_CRITICAL();
\r
793 /* Abandon the INNER LOOP. Execution will transfer back to the OUTER LOOP condition. */
\r
798 pJob = IotLink_Container( _taskPoolJob_t, pItem, link );
\r
800 userCallback = pJob->userCallback;
\r
803 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
805 taskEXIT_CRITICAL();
\r
810 /* ---------------------------------------------------------------------------------------------- */
\r
812 static void _initJobsCache( _taskPoolCache_t * const pCache )
\r
814 IotDeQueue_Create( &pCache->freeList );
\r
816 pCache->freeCount = 0;
\r
819 /*-----------------------------------------------------------*/
\r
821 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
822 IotTaskPoolRoutine_t userCallback,
\r
823 void * pUserContext,
\r
826 pJob->link.pNext = NULL;
\r
827 pJob->link.pPrevious = NULL;
\r
828 pJob->userCallback = userCallback;
\r
829 pJob->pUserContext = pUserContext;
\r
833 pJob->flags = IOT_TASK_POOL_INTERNAL_STATIC;
\r
834 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
838 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
842 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache )
\r
844 _taskPoolJob_t * pJob = NULL;
\r
845 IotLink_t * pLink = IotListDouble_RemoveHead( &( pCache->freeList ) );
\r
847 if( pLink != NULL )
\r
849 pJob = IotLink_Container( _taskPoolJob_t, pLink, link );
\r
852 /* If there is no available job in the cache, then allocate one. */
\r
855 pJob = ( _taskPoolJob_t * ) IotTaskPool_MallocJob( sizeof( _taskPoolJob_t ) );
\r
859 memset( pJob, 0x00, sizeof( _taskPoolJob_t ) );
\r
863 /* Log allocation failure for troubleshooting purposes. */
\r
864 IotLogInfo( "Failed to allocate job." );
\r
867 /* If there was a job in the cache, then make sure we keep the counters up-to-date. */
\r
870 IotTaskPool_Assert( pCache->freeCount > 0 );
\r
872 pCache->freeCount--;
\r
878 /*-----------------------------------------------------------*/
\r
880 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
881 _taskPoolJob_t * const pJob )
\r
883 /* We should never try and recycling a job that is linked into some queue. */
\r
884 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );//_RB_ Seems to be duplicate of test before this is called.
\r
886 /* We will recycle the job if there is space in the cache. */
\r
887 if( pCache->freeCount < IOT_TASKPOOL_JOBS_RECYCLE_LIMIT )
\r
889 /* Destroy user data, for added safety&security. */
\r
890 pJob->userCallback = NULL;
\r
891 pJob->pUserContext = NULL;
\r
893 /* Reset the status for added debuggability. */
\r
894 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
896 IotListDouble_InsertTail( &pCache->freeList, &pJob->link );
\r
898 pCache->freeCount++;
\r
900 IotTaskPool_Assert( pCache->freeCount >= 1 );
\r
904 _destroyJob( pJob );
\r
908 /*-----------------------------------------------------------*/
\r
910 static void _destroyJob( _taskPoolJob_t * const pJob )
\r
912 /* Destroy user data, for added safety & security. */
\r
913 pJob->userCallback = NULL;
\r
914 pJob->pUserContext = NULL;
\r
916 /* Reset the status for added debuggability. */
\r
917 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
919 /* Only dispose of dynamically allocated jobs. */
\r
920 if( ( pJob->flags & IOT_TASK_POOL_INTERNAL_STATIC ) == 0UL )
\r
922 IotTaskPool_FreeJob( pJob );
\r
926 /* ---------------------------------------------------------------------------------------------- */
\r
928 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
929 _taskPoolJob_t * const pJob )
\r
931 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
933 /* Update the job status to 'scheduled'. */
\r
934 pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;
\r
936 /* Append the job to the dispatch queue. */
\r
937 IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link );
\r
939 /* Signal a worker to pick up the job. */
\r
940 xSemaphoreGive( pTaskPool->dispatchSignal );
\r
942 TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL();
\r
945 /*-----------------------------------------------------------*/
\r
947 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
950 const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;
\r
952 const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
954 if( pJob == pTimerEvent->job )
\r
962 /*-----------------------------------------------------------*/
\r
964 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
965 _taskPoolJob_t * const pJob,
\r
966 IotTaskPoolJobStatus_t * const pStatus )
\r
968 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
970 bool cancelable = false;
\r
972 /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */
\r
974 IotTaskPoolJobStatus_t currentStatus = pJob->status;
\r
976 switch( currentStatus )
\r
978 case IOT_TASKPOOL_STATUS_READY:
\r
979 case IOT_TASKPOOL_STATUS_DEFERRED:
\r
980 case IOT_TASKPOOL_STATUS_SCHEDULED:
\r
981 case IOT_TASKPOOL_STATUS_CANCELED:
\r
985 case IOT_TASKPOOL_STATUS_COMPLETED:
\r
986 /* Log message for debug purposes. */
\r
987 IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );
\r
991 /* Log message for debug purposes purposes. */
\r
992 IotLogError( "Attempt to cancel a job with an undefined state." );
\r
996 /* Update the returned status to the current status of the job. */
\r
997 if( pStatus != NULL )
\r
999 *pStatus = currentStatus;
\r
1002 if( cancelable == false )
\r
1004 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_CANCEL_FAILED );
\r
1008 /* Update the status of the job. */
\r
1009 pJob->status = IOT_TASKPOOL_STATUS_CANCELED;
\r
1011 /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch
\r
1012 * queue and signal any waiting threads. */
\r
1013 if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )
\r
1015 /* A scheduled work items must be in the dispatch queue. */
\r
1016 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) );
\r
1018 IotDeQueue_Remove( &pJob->link );
\r
1021 /* If the job current status is 'deferred' then the job has to be pending
\r
1022 * in the timeouts queue. */
\r
1023 else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )
\r
1025 /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */
\r
1026 IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &pTaskPool->timerEventsList, NULL, _matchJobByPointer, pJob );
\r
1027 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
1029 if( pTimerEventLink != NULL )
\r
1031 bool shouldReschedule = false;
\r
1033 /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer
\r
1034 * with the next job timeout */
\r
1035 IotLink_t * pHeadLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1037 if( pHeadLink == pTimerEventLink )
\r
1039 shouldReschedule = true;
\r
1042 /* Remove the timer event associated with the canceled job and free the associated memory. */
\r
1043 IotListDouble_Remove( pTimerEventLink );
\r
1044 IotTaskPool_FreeTimerEvent( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ) );
\r
1046 if( shouldReschedule )
\r
1048 IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1050 if( pNextTimerEventLink != NULL )
\r
1052 _rescheduleDeferredJobsTimer( pTaskPool->timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );
\r
1059 /* A cancelable job status should be either 'scheduled' or 'deferrred'. */
\r
1060 IotTaskPool_Assert( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );
\r
1064 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
1067 /*-----------------------------------------------------------*/
\r
1069 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
1070 const IotLink_t * const pTimerEventLink2 )
\r
1072 const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1075 const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1079 if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )
\r
1084 if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )
\r
1092 /*-----------------------------------------------------------*/
\r
1094 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
1095 _taskPoolTimerEvent_t * const pFirstTimerEvent )
\r
1097 uint64_t delta = 0;
\r
1098 TickType_t now = xTaskGetTickCount();
\r
1100 if( pFirstTimerEvent->expirationTime > now )
\r
1102 delta = pFirstTimerEvent->expirationTime - now;
\r
1105 if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )
\r
1107 delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */
\r
1110 IotTaskPool_Assert( delta > 0 );
\r
1112 if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )
\r
1114 IotLogWarn( "Failed to re-arm timer for task pool" );
\r
1118 /*-----------------------------------------------------------*/
\r
1120 static void _timerCallback( TimerHandle_t xTimer )
\r
1122 _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );
\r
1124 IotTaskPool_Assert( pTaskPool );
\r
1126 _taskPoolTimerEvent_t * pTimerEvent = NULL;
\r
1128 IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );
\r
1130 /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.
\r
1131 * If this mutex cannot be locked it means that another thread is manipulating the
\r
1132 * timeouts list, and will reset the timer to fire again, although it will be late.
\r
1134 taskENTER_CRITICAL(); //_RB_ Critical section is too long.
\r
1136 /* Dispatch all deferred job whose timer expired, then reset the timer for the next
\r
1137 * job down the line. */
\r
1140 /* Peek the first event in the timer event list. */
\r
1141 IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1143 /* Check if the timer misfired for any reason. */
\r
1144 if( pLink != NULL )
\r
1146 /* Record the current time. */
\r
1147 TickType_t now = xTaskGetTickCount();
\r
1149 /* Extract the job from its envelope. */
\r
1150 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
1152 /* Check if the first event should be processed now. */
\r
1153 if( pTimerEvent->expirationTime <= now )
\r
1155 /* Remove the timer event for immediate processing. */
\r
1156 IotListDouble_Remove( &( pTimerEvent->link ) );
\r
1160 /* The first element in the timer queue shouldn't be processed yet.
\r
1161 * Arm the timer for when it should be processed and leave altogether. */
\r
1162 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
1167 /* If there are no timer events to process, terminate this thread. */
\r
1170 IotLogDebug( "No further timer events to process. Exiting timer thread." );
\r
1175 IotLogDebug( "Scheduling job from timer event." );
\r
1177 /* Queue the job associated with the received timer event. */
\r
1178 ( void ) _scheduleInternal( pTaskPool, pTimerEvent->job );
\r
1180 /* Free the timer event. */
\r
1181 IotTaskPool_FreeTimerEvent( pTimerEvent );
\r
1184 taskEXIT_CRITICAL();
\r