2 * FreeRTOS Task Pool v1.0.0
\r
3 * Copyright (C) 2019 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
24 * @file iot_taskpool_freertos.c
\r
25 * @brief Implements the task pool functions in iot_taskpool.h for FreeRTOS.
\r
30 * The full IoT Task Pool Library has many use cases, including Linux development.
\r
31 * Typical FreeRTOS use cases do not require the full functionality so an optimized
\r
32 * implementation is provided specifically for use with FreeRTOS. The optimized
\r
33 * version has a fixed number of tasks in the pool, each of which uses statically
\r
34 * allocated memory to ensure creation of the pool is guaranteed (it does not run out
\r
35 * of heap space). The constant IOT_TASKPOOL_NUMBER_OF_WORKERS sets the number of
\r
36 * tasks in the pool.
\r
38 * Unlike the full version, this optimized version:
\r
39 * + Only supports a single task pool (system task pool) at a time.
\r
40 * + Does not auto-scale by dynamically adding more tasks if the number of
\r
41 * tasks in the pool becomes exhausted. The number of tasks in the pool
\r
42 * are fixed at compile time. See the task pool configuration options for
\r
44 * + Cannot be shut down - it exists for the lifetime of the application.
\r
46 * Users who are interested in the functionality of the full IoT Task Pool
\r
47 * library can us it in place of this optimized version.
\r
51 /* Kernel includes. */
\r
52 #include "FreeRTOS.h"
\r
55 /* IoT libraries includes. */
\r
56 #include "iot_config.h"
\r
58 /* Standard includes. */
\r
59 #include <stdbool.h>
\r
65 #if !defined( configSUPPORT_STATIC_ALLOCATION ) || ( configSUPPORT_STATIC_ALLOCATION != 1 )
\r
66 #error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.
\r
69 /* Task pool internal include. */
\r
70 #include "private/iot_taskpool_internal_freertos.h"
\r
73 * @brief Maximum semaphore value for wait operations.
\r
75 #define TASKPOOL_MAX_SEM_VALUE ( ( UBaseType_t ) 0xFFFF )
\r
78 * @brief Reschedule delay in milliseconds for deferred jobs.
\r
80 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS ( 10ULL )
\r
82 /* ---------------------------------------------------------------------------------- */
\r
85 * @brief Get the job associated with a timer.
\r
87 * @warning This only works on a _taskPoolTimerEvent_t within a _taskPoolJob_t.
\r
89 #define GET_JOB_FROM_TIMER(t) ((_taskPoolJob_t *)((uint8_t*)(t) - offsetof(_taskPoolJob_t, timer)))
\r
92 * brief The maximum time to wait when attempting to obtain an internal semaphore.
\r
93 * Don't wait indefinitely to give the application a chance to recover in the case
\r
96 #define MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ( pdMS_TO_TICKS( 10000UL ) )
\r
97 /* ---------------------------------------------------------------------------------- */
\r
100 * Doxygen should ignore this section.
\r
102 * @brief The system task pool handle for all libraries to use.
\r
103 * User application can use the system task pool as well knowing that the usage will be shared with
\r
104 * the system libraries as well. The system task pool needs to be initialized before any library is used or
\r
105 * before any code that posts jobs to the task pool runs.
\r
107 static _taskPool_t _IotSystemTaskPool = { 0 };
\r
109 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */
\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 to be passed to the callback.
\r
118 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
119 IotTaskPoolRoutine_t userCallback,
\r
120 void * pUserContext );
\r
122 /* -------------- The worker thread procedure for a task pool thread -------------- */
\r
125 * The procedure for a task pool worker thread.
\r
127 * @param[in] pUserContext The user context.
\r
130 static void _taskPoolWorker( void * pUserContext );
\r
132 /* -------------- Convenience functions to handle timer events -------------- */
\r
135 * Comparer for the time list.
\r
137 * param[in] pTimerEventLink1 The link to the first timer event.
\r
138 * param[in] pTimerEventLink1 The link to the first timer event.
\r
140 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
141 const IotLink_t * const pTimerEventLink2 );
\r
144 * Reschedules the timer for handling deferred jobs to the next timeout.
\r
146 * param[in] timer The timer to reschedule.
\r
147 * param[in] pFirstTimerEvent The timer event that carries the timeout and job information.
\r
149 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
150 _taskPoolTimerEvent_t * const pFirstTimerEvent );
\r
153 * The task pool timer procedure for scheduling deferred jobs.
\r
155 * param[in] timer The timer to handle.
\r
157 static void _timerCallback( TimerHandle_t xTimer );
\r
159 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */
\r
162 * Initializes a pre-allocated instance of a task pool.
\r
164 static void _initTaskPoolControlStructures( void );
\r
167 * Initializes a pre-allocated instance of a task pool.
\r
169 * @param[in] pInfo The initialization information for the task pool.
\r
172 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo );
\r
175 * Places a job in the dispatch queue.
\r
177 * @param[in] pJob The job to schedule.
\r
180 static void _scheduleInternal( _taskPoolJob_t * const pJob );
\r
182 * Matches a deferred job in the timer queue with its timer event wrapper.
\r
184 * @param[in] pLink A pointer to the timer event link in the timer queue.
\r
185 * @param[in] pMatch A pointer to the job to match.
\r
188 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
192 * Tries to cancel a job.
\r
194 * @param[in] pJob The job to cancel.
\r
195 * @param[out] pStatus The status of the job at the time of cancellation.
\r
198 static IotTaskPoolError_t _tryCancelInternal( _taskPoolJob_t * const pJob,
\r
199 IotTaskPoolJobStatus_t * const pStatus );
\r
201 /* ---------------------------------------------------------------------------------------------- */
\r
203 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )
\r
205 IotTaskPoolError_t status;
\r
206 static BaseType_t isInitialized = pdFALSE;
\r
208 configASSERT( pInfo );
\r
209 configASSERT( pInfo->minThreads >= 1UL );
\r
211 /* This version of the task pool does not auto-scale so the specified minimum
\r
212 number of threads in the pool must match the specified maximum number of threads
\r
214 configASSERT( pInfo->maxThreads == pInfo->minThreads );
\r
216 /* Guard against multiple attempts to create the system task pool in case
\r
217 this function is called by more than one library initialization routine. */
\r
218 taskENTER_CRITICAL();
\r
220 /* If the task pool has already been initialized then
\r
221 IOT_TASKPOOL_ILLEGAL_OPERATION will be returned - but that does not guarantee
\r
222 the initialization operation is complete as the task to which
\r
223 IOT_TASKPOOL_ILLEGAL_OPERATION is returned may have preempted the task that
\r
224 was performing the initialization before the initialization was complete -
\r
225 hence the assert to catch this occurrence during development and debug. */
\r
226 configASSERT( isInitialized == pdFALSE );
\r
228 if( isInitialized == pdFALSE )
\r
230 /* The task pool has not been initialized already so will be initialized
\r
232 status = IOT_TASKPOOL_SUCCESS;
\r
233 isInitialized = pdTRUE;
\r
237 /* This function has already been called but executing this path does
\r
238 not guarantee the task pool has already been initialized as the task
\r
239 to which this error is returned may have preempted the task that was
\r
240 performing the initialization before the initialization was complete. */
\r
241 status = IOT_TASKPOOL_ILLEGAL_OPERATION;
\r
244 taskEXIT_CRITICAL();
\r
246 if( status == IOT_TASKPOOL_SUCCESS )
\r
248 /* Create the system task pool. Note in this version _createTaskPool()
\r
249 cannot fail because it is using statically allocated memory. Therefore the
\r
250 return value can be safely ignored and there is no need to consider resetting
\r
251 isInitialized in a failure case. */
\r
252 ( void ) _createTaskPool( pInfo );
\r
257 /*-----------------------------------------------------------*/
\r
259 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,
\r
260 void * pUserContext,
\r
261 IotTaskPoolJobStorage_t * const pJobStorage,
\r
262 IotTaskPoolJob_t * const ppJob )
\r
264 /* Parameter checking. */
\r
265 configASSERT( userCallback != NULL );
\r
266 configASSERT( pJobStorage != NULL );
\r
267 configASSERT( ppJob != NULL );
\r
269 /* Build a job around the user-provided storage. */
\r
270 _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext );
\r
272 *ppJob = ( IotTaskPoolJob_t ) pJobStorage;
\r
274 return IOT_TASKPOOL_SUCCESS;
\r
277 /*-----------------------------------------------------------*/
\r
279 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,
\r
280 IotTaskPoolJob_t pJob,
\r
283 IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;
\r
285 /* Task pool must have been created. */
\r
286 configASSERT( _IotSystemTaskPool.running != false );
\r
288 /* This lean version of the task pool only supports the task pool created
\r
289 by this library (the system task pool). NULL means use the system task
\r
290 pool - no other values are allowed. Use the full implementation of this
\r
291 library if you want multiple task pools (there is more than one task in
\r
293 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
295 /* Avoid compiler warnings about unused parameters if configASSERT() is not
\r
297 ( void ) taskPoolHandle;
\r
299 configASSERT( pJob != NULL );
\r
300 configASSERT( ( flags == 0UL ) || ( flags == IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );
\r
302 /* Acquire the mutex for manipulating the job timer queue. */
\r
303 if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ) == pdTRUE )
\r
305 _scheduleInternal( pJob );
\r
306 if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )
\r
308 /* This can only be reached if semaphores are configured incorrectly. */
\r
309 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
311 /* Signal a worker task that a job was queued. */
\r
312 if ( xSemaphoreGive( _IotSystemTaskPool.dispatchSignal ) == pdFALSE )
\r
314 /* This can only be reached if semaphores are configured incorrectly. */
\r
315 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
320 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
326 /*-----------------------------------------------------------*/
\r
328 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,
\r
329 IotTaskPoolJob_t job,
\r
333 IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;
\r
335 /* This lean version of the task pool only supports the task pool created
\r
336 by this library (the system task pool). NULL means use the system task
\r
337 pool - no other values are allowed. Use the full implementation of this
\r
338 library if you want multiple task pools (there is more than one task in
\r
340 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
342 configASSERT( job != NULL );
\r
344 /* If the timer period is zero, just immediately queue the job for execution. */
\r
345 if( timeMs == 0UL )
\r
347 status = IotTaskPool_Schedule( &_IotSystemTaskPool, job, 0 );
\r
351 _taskPoolTimerEvent_t* pTimerEvent = &(job->timer);
\r
353 configASSERT( job->timer.link.pNext == NULL );
\r
355 IotLink_t* pTimerEventLink;
\r
357 pTimerEvent->link.pNext = NULL;
\r
358 pTimerEvent->link.pPrevious = NULL;
\r
360 now = xTaskGetTickCount();
\r
361 pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );
\r
363 if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ) == pdTRUE )
\r
366 /* Append the timer event to the timer list. */
\r
367 IotListDouble_InsertSorted( &( _IotSystemTaskPool.timerEventsList ), &( pTimerEvent->link ), _timerEventCompare );
\r
369 /* Update the job status to 'scheduled'. */
\r
370 job->status = IOT_TASKPOOL_STATUS_DEFERRED;
\r
372 /* Peek the first event in the timer event list. There must be at least one,
\r
373 * since we just inserted it. */
\r
374 pTimerEventLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );
\r
375 configASSERT( pTimerEventLink != NULL );
\r
377 /* If the event we inserted is at the front of the queue, then
\r
378 * we need to reschedule the underlying timer. */
\r
379 if ( pTimerEventLink == &( pTimerEvent->link ) )
\r
381 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );
\r
383 _rescheduleDeferredJobsTimer( _IotSystemTaskPool.timer, pTimerEvent );
\r
386 if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )
\r
388 /* This can only be reached if semaphores are configured incorrectly. */
\r
389 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
395 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
402 /*-----------------------------------------------------------*/
\r
404 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,
\r
405 IotTaskPoolJob_t job,
\r
406 IotTaskPoolJobStatus_t * const pStatus )
\r
409 /* This lean version of the task pool only supports the task pool created by
\r
410 this library (the system task pool). NULL means use the system task pool -
\r
411 no other values are allowed. Use the full implementation of this library if you
\r
412 want multiple task pools (there is more than one task in each pool. */
\r
413 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
415 /* Remove warning about unused parameter. */
\r
416 ( void ) taskPoolHandle;
\r
418 /* Parameter checking. */
\r
419 configASSERT( job != NULL );
\r
420 configASSERT( pStatus != NULL );
\r
422 taskENTER_CRITICAL();
\r
424 *pStatus = job->status;
\r
426 taskEXIT_CRITICAL();
\r
428 return IOT_TASKPOOL_SUCCESS;
\r
431 /*-----------------------------------------------------------*/
\r
433 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,
\r
434 IotTaskPoolJob_t job,
\r
435 IotTaskPoolJobStatus_t * const pStatus )
\r
437 IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;
\r
438 const TickType_t dontBlock = ( TickType_t ) 0;
\r
440 /* This lean version of the task pool only supports the task pool created
\r
441 by this library (the system task pool). NULL means use the system task
\r
442 pool - no other values are allowed. Use the full implementation of this
\r
443 library if you want multiple task pools (there is more than one task in
\r
445 configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );
\r
449 if( pStatus != NULL )
\r
451 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
454 if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, dontBlock ) != pdFALSE )
\r
456 status = _tryCancelInternal( job, pStatus );
\r
457 if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )
\r
459 /* This can only be reached if semaphores are configured incorrectly. */
\r
460 status = IOT_TASKPOOL_GENERAL_FAILURE;
\r
465 /* If we fail to take the semaphore, just abort the cancel. */
\r
466 status = IOT_TASKPOOL_CANCEL_FAILED;
\r
471 status = IOT_TASKPOOL_BAD_PARAMETER;
\r
477 /*-----------------------------------------------------------*/
\r
479 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )
\r
481 return ( IotTaskPoolJobStorage_t * ) pJob;
\r
484 /*-----------------------------------------------------------*/
\r
486 const char * IotTaskPool_strerror( IotTaskPoolError_t status )
\r
488 const char * pMessage = NULL;
\r
492 case IOT_TASKPOOL_SUCCESS:
\r
493 pMessage = "SUCCESS";
\r
496 case IOT_TASKPOOL_BAD_PARAMETER:
\r
497 pMessage = "BAD PARAMETER";
\r
500 case IOT_TASKPOOL_ILLEGAL_OPERATION:
\r
501 pMessage = "ILLEGAL OPERATION";
\r
504 case IOT_TASKPOOL_NO_MEMORY:
\r
505 pMessage = "NO MEMORY";
\r
508 case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:
\r
509 pMessage = "SHUTDOWN IN PROGRESS";
\r
512 case IOT_TASKPOOL_CANCEL_FAILED:
\r
513 pMessage = "CANCEL FAILED";
\r
516 case IOT_TASKPOOL_GENERAL_FAILURE:
\r
517 pMessage = "GENERAL FAILURE";
\r
521 pMessage = "INVALID STATUS";
\r
528 /* ---------------------------------------------------------------------------------------------- */
\r
529 /* ---------------------------------------------------------------------------------------------- */
\r
530 /* ---------------------------------------------------------------------------------------------- */
\r
532 static void _initTaskPoolControlStructures( void )
\r
534 /* Initialize a job data structures that require no de-initialization.
\r
535 * All other data structures carry a value of 'NULL' before initialization.
\r
537 IotDeQueue_Create( &( _IotSystemTaskPool.dispatchQueue ) );
\r
538 IotListDouble_Create( &( _IotSystemTaskPool.timerEventsList ) );
\r
540 /* Initialize the semaphore for waiting for incoming work. Cannot fail as
\r
541 statically allocated. */
\r
542 _IotSystemTaskPool.dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &( _IotSystemTaskPool.dispatchSignalBuffer ) );
\r
543 _IotSystemTaskPool.xTimerEventMutex = xSemaphoreCreateMutexStatic( &( _IotSystemTaskPool.xTimerEventMutexBuffer ) );
\r
546 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo )
\r
548 /* The taskpool will create a number of threads equal to the minThreads
\r
549 setting. The number of workers should be equal to avoid over/under
\r
551 configASSERT( IOT_TASKPOOL_NUMBER_OF_WORKERS == pInfo->minThreads );
\r
553 /* Static TCB structures and arrays to be used by statically allocated worker
\r
555 static StaticTask_t workerTaskTCBs[ IOT_TASKPOOL_NUMBER_OF_WORKERS ];
\r
556 static StackType_t workerTaskStacks[ IOT_TASKPOOL_NUMBER_OF_WORKERS ][ IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ) ];
\r
558 /* Static structure to hold the software timer. */
\r
559 static StaticTimer_t staticTimer;
\r
561 uint32_t threadsCreated = 0; /* Although initialized before use removing the initializer here results in compiler warnings. */
\r
562 char taskName[ 10 ];
\r
565 /* This assert is primarily to catch the function being called more than once,
\r
566 but will also ensure the C start up code has zeroed out the structure
\r
568 #if( configASSERT_DEFINED == 1 )
\r
571 uint8_t *pucNextByte = ( uint8_t * ) &_IotSystemTaskPool;
\r
573 for( x = 0; x < sizeof( _taskPool_t ); x++ )
\r
575 configASSERT( pucNextByte[ x ] == ( uint8_t ) 0x00 );
\r
578 #endif /* configASSERT_DEFINED */
\r
582 /* Initialize all internal data structure prior to creating all threads. */
\r
583 _initTaskPoolControlStructures();
\r
585 /* Create the FreeRTOS timer for managing Task Pool timers. */
\r
586 _IotSystemTaskPool.timer = xTimerCreateStatic( NULL, /* Text name for the timer, only used for debugging. */
\r
587 portMAX_DELAY, /* Timer period in ticks. */
\r
588 pdFALSE, /* pdFALSE means its a one-shot timer. */
\r
589 ( void * ) &_IotSystemTaskPool, /* Parameter passed into callback. */
\r
590 _timerCallback, /* Callback that executes when the timer expires. */
\r
591 &staticTimer ); /* Static storage for the timer's data structure. */
\r
593 /* The task pool will initialize the minimum number of threads requested by the user upon start.
\r
594 Note this tailored version of the task pool does not auto-scale, but fixes the number of tasks
\r
595 in the pool to the originally specified minimum, and the specified maximum value is ignored. */
\r
596 /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */
\r
597 for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )
\r
599 /* Generate a unique name for the task. */
\r
600 snprintf( taskName, sizeof( taskName ), "pool%d", ( int ) threadsCreated );
\r
602 xTaskCreateStatic( _taskPoolWorker, /* Function that implements the task. */
\r
603 taskName, /* Text name for the task, used for debugging only. */
\r
604 IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ), /* xTaskCreate() expects the stack size to be specified in words. */
\r
605 &_IotSystemTaskPool, /* Parameter passed into the task. */
\r
606 pInfo->priority, /* Priority at which the task starts running. */
\r
607 &( workerTaskStacks[ threadsCreated ][ 0 ] ), /* Pointer to static storage for the task's stack. */
\r
608 &( workerTaskTCBs[ threadsCreated ] ) ); /* Pointer to static storage for the task's TCB. */
\r
612 _IotSystemTaskPool.running = true;
\r
614 /* This version of this function cannot fail as all the memory is allocated
\r
615 statically at compile time. */
\r
616 return IOT_TASKPOOL_SUCCESS;
\r
619 /* ---------------------------------------------------------------------------------------------- */
\r
621 static void _taskPoolWorker( void * pUserContext )
\r
623 configASSERT( pUserContext != NULL );
\r
625 IotTaskPoolRoutine_t userCallback = NULL;
\r
627 /* Extract pTaskPool pointer from context. */
\r
628 _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;
\r
630 /* OUTER LOOP: it controls the lifetime of the worker thread. */
\r
633 IotLink_t * pFirst = NULL;
\r
634 _taskPoolJob_t * pJob = NULL;
\r
636 /* Wait on incoming notifications... */
\r
637 configASSERT( pTaskPool->dispatchSignal );
\r
639 /* If the semaphore for job dispatch expires without a job, a critical
\r
640 precondition of this task has not been met. See the xBlockTime
\r
641 parameter of xSemaphoreTake for details. */
\r
642 configASSERT( xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY ) );
\r
644 /* Acquire the lock to check for incoming notifications. This call
\r
645 should not expire. See the xBlockTime parameter of xSemaphoreTake
\r
647 configASSERT( xSemaphoreTake( pTaskPool->xTimerEventMutex, portMAX_DELAY ) );
\r
649 /* Dequeue the first job in FIFO order. */
\r
650 pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
652 /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */
\r
653 if( pFirst != NULL )
\r
655 /* Extract the job from its link. */
\r
656 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );
\r
658 /* Update status to 'completed' to indicate it is queued for execution. */
\r
659 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
660 userCallback = pJob->userCallback;
\r
663 /* Release the lock now that the job dispatch queue has been checked.
\r
664 This call should not expire. See the xBlockTime parameter of
\r
665 xSemaphoreTake for details. */
\r
666 configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );
\r
668 /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */
\r
669 while( pJob != NULL )
\r
671 /* Process the job by invoking the associated callback with the user context.
\r
672 * This task pool thread will not be available until the user callback returns.
\r
675 configASSERT( IotLink_IsLinked( &pJob->link ) == false );
\r
676 configASSERT( userCallback != NULL );
\r
678 userCallback( pTaskPool, pJob, pJob->pUserContext );
\r
680 /* This job is finished, clear its pointer. */
\r
682 userCallback = NULL;
\r
685 /* Acquire the lock to check for incoming notifications. This call
\r
686 should not expire. See the xBlockTime parameter of xSemaphoreTake
\r
688 configASSERT( xSemaphoreTake( pTaskPool->xTimerEventMutex, portMAX_DELAY ) );
\r
690 /* Try and dequeue the next job in the dispatch queue. */
\r
691 IotLink_t * pItem = NULL;
\r
693 /* Dequeue the next job from the dispatch queue. */
\r
694 pItem = IotDeQueue_DequeueHead( &( pTaskPool->dispatchQueue ) );
\r
696 /* If there is no job left in the dispatch queue, update the worker status and leave. */
\r
697 if( pItem == NULL )
\r
699 /* Release the lock before exiting the loop. */
\r
700 configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );
\r
702 /* Abandon the INNER LOOP. Execution will transfer back to the OUTER LOOP condition. */
\r
707 pJob = IotLink_Container( _taskPoolJob_t, pItem, link );
\r
709 userCallback = pJob->userCallback;
\r
712 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
714 /* Release the lock now that the job dispatch queue has been checked. */
\r
715 configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );
\r
720 /* ---------------------------------------------------------------------------------------------- */
\r
722 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
723 IotTaskPoolRoutine_t userCallback,
\r
724 void * pUserContext )
\r
726 memset( ( void * ) pJob, 0x00, sizeof( _taskPoolJob_t ) );
\r
728 pJob->userCallback = userCallback;
\r
729 pJob->pUserContext = pUserContext;
\r
730 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
733 /* ---------------------------------------------------------------------------------------------- */
\r
735 static void _scheduleInternal( _taskPoolJob_t * const pJob )
\r
737 /* Update the job status to 'scheduled'. */
\r
738 pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;
\r
740 /* Append the job to the dispatch queue. */
\r
741 IotDeQueue_EnqueueTail( &( _IotSystemTaskPool.dispatchQueue ), &( pJob->link ) );
\r
743 /* NOTE: Every call to this function must be followed by giving the
\r
744 dispatchSignal semaphore - but do not give the semaphore directly in
\r
745 this function as giving the semaphore will result in the execution of
\r
746 a task pool worker task (depending on relative priorities) and we don't
\r
747 want the worker task to execute until all semaphores obtained before calling
\r
748 this function have been released. */
\r
751 /*-----------------------------------------------------------*/
\r
753 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
756 const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;
\r
758 const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
760 if( pJob == GET_JOB_FROM_TIMER( pTimerEvent ) )
\r
768 /*-----------------------------------------------------------*/
\r
770 static IotTaskPoolError_t _tryCancelInternal( _taskPoolJob_t * const pJob,
\r
771 IotTaskPoolJobStatus_t * const pStatus )
\r
773 IotTaskPoolError_t result = IOT_TASKPOOL_SUCCESS;
\r
775 bool cancelable = false;
\r
777 /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */
\r
779 IotTaskPoolJobStatus_t currentStatus = pJob->status;
\r
781 switch( currentStatus )
\r
783 case IOT_TASKPOOL_STATUS_READY:
\r
784 case IOT_TASKPOOL_STATUS_DEFERRED:
\r
785 case IOT_TASKPOOL_STATUS_SCHEDULED:
\r
786 case IOT_TASKPOOL_STATUS_CANCELED:
\r
790 case IOT_TASKPOOL_STATUS_COMPLETED:
\r
791 /* Log message for debug purposes. */
\r
792 IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );
\r
796 /* Log message for debug purposes. */
\r
797 IotLogError( "Attempt to cancel a job with an undefined state." );
\r
801 /* Update the returned status to the current status of the job. */
\r
802 if( pStatus != NULL )
\r
804 *pStatus = currentStatus;
\r
807 if( cancelable == false )
\r
809 result = IOT_TASKPOOL_CANCEL_FAILED;
\r
813 /* Update the status of the job. */
\r
814 pJob->status = IOT_TASKPOOL_STATUS_CANCELED;
\r
816 /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch
\r
817 * queue and signal any waiting threads. */
\r
818 if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )
\r
820 /* A scheduled work items must be in the dispatch queue. */
\r
821 configASSERT( IotLink_IsLinked( &pJob->link ) );
\r
823 IotDeQueue_Remove( &pJob->link );
\r
826 /* If the job current status is 'deferred' then the job has to be pending
\r
827 * in the timeouts queue. */
\r
828 else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )
\r
830 /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */
\r
831 IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &( _IotSystemTaskPool.timerEventsList ), NULL, _matchJobByPointer, pJob );
\r
832 configASSERT( pTimerEventLink != NULL );
\r
834 if( pTimerEventLink != NULL )
\r
836 bool shouldReschedule = false;
\r
838 /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer
\r
839 * with the next job timeout */
\r
840 IotLink_t * pHeadLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );
\r
842 if( pHeadLink == pTimerEventLink )
\r
844 shouldReschedule = true;
\r
847 /* Remove the timer event associated with the canceled job and free the associated memory. */
\r
848 IotListDouble_Remove( pTimerEventLink );
\r
849 memset( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ), 0, sizeof( IotLink_t ) );
\r
851 if( shouldReschedule )
\r
853 IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );
\r
855 if( pNextTimerEventLink != NULL )
\r
857 _rescheduleDeferredJobsTimer( _IotSystemTaskPool.timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );
\r
864 /* A cancelable job status should be either 'scheduled' or 'deferred'. */
\r
865 configASSERT( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );
\r
872 /*-----------------------------------------------------------*/
\r
874 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
875 const IotLink_t * const pTimerEventLink2 )
\r
877 const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,
\r
880 const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,
\r
884 if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )
\r
889 if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )
\r
897 /*-----------------------------------------------------------*/
\r
899 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
900 _taskPoolTimerEvent_t * const pFirstTimerEvent )
\r
902 uint64_t delta = 0;
\r
903 TickType_t now = xTaskGetTickCount();
\r
905 configASSERT( pFirstTimerEvent != NULL );
\r
906 configASSERT( timer != NULL );
\r
908 /* Determine how much time is left for the deferred job. */
\r
909 if( pFirstTimerEvent->expirationTime > now )
\r
911 delta = pFirstTimerEvent->expirationTime - now;
\r
914 /* If the job timer has exceeded it's period, schedule it to be executed shortly. */
\r
915 if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )
\r
917 delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */
\r
920 /* Change the period of the task pools timer to be the period of this
\r
921 timer. A precondition of this function is that this TimerEvent is the
\r
922 timer event with the shortest deadline.
\r
924 if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )
\r
926 IotLogWarn( "Failed to re-arm timer for task pool" );
\r
930 /*-----------------------------------------------------------*/
\r
932 static void _timerCallback( TimerHandle_t xTimer )
\r
934 _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );
\r
936 configASSERT( pTaskPool );
\r
938 _taskPoolTimerEvent_t * pTimerEvent = NULL;
\r
939 BaseType_t numberOfSchedules = 0;
\r
941 IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );
\r
943 /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.
\r
944 * If this mutex cannot be locked it means that another thread is manipulating the
\r
945 * timeouts list, and will reset the timer to fire again, although it will be late.
\r
947 if ( xSemaphoreTake( pTaskPool->xTimerEventMutex, 0 ) == pdPASS )
\r
950 /* Dispatch all deferred job whose timer expired, then reset the timer for the next
\r
951 * job down the line. */
\r
954 /* Peek the first event in the timer event list. */
\r
955 IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
957 /* Check if the timer misfired for any reason. */
\r
958 if( pLink != NULL )
\r
960 /* Record the current time. */
\r
961 TickType_t now = xTaskGetTickCount();
\r
963 /* Extract the job from its envelope. */
\r
964 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
966 /* Check if the first event should be processed now. */
\r
967 if( pTimerEvent->expirationTime <= now )
\r
969 /* Remove the timer event for immediate processing. */
\r
970 IotListDouble_Remove( &( pTimerEvent->link ) );
\r
974 /* The first element in the timer queue shouldn't be processed yet.
\r
975 * Arm the timer for when it should be processed and leave altogether. */
\r
976 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
980 /* If there are no timer events to process, terminate this thread. */
\r
983 IotLogDebug( "Finished scheduling deferred jobs." );
\r
987 /* Queue the job associated with the received timer event. */
\r
988 _scheduleInternal( GET_JOB_FROM_TIMER( pTimerEvent ) );
\r
989 numberOfSchedules++;
\r
990 IotLogDebug( "Scheduled a job." );
\r
992 /* Free the timer event. */
\r
993 memset( &( pTimerEvent->link ), 0, sizeof( pTimerEvent->link ) );
\r
996 /* Release mutex guarding the timer list. */
\r
997 configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) == pdPASS );
\r
999 for (; numberOfSchedules > 0; numberOfSchedules--)
\r
1001 /* Signal a worker task that a job was queued. */
\r
1002 configASSERT( xSemaphoreGive( pTaskPool->dispatchSignal ) );
\r