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 /* The config header is always included first. */
\r
31 #include "iot_config.h"
\r
33 /* Standard includes. */
\r
34 #include <stdbool.h>
\r
39 /* Platform layer includes. */
\r
40 #include "platform/iot_threads.h"
\r
41 #include "platform/iot_clock.h"
\r
43 /* Task pool internal include. */
\r
44 #include "private/iot_taskpool_internal.h"
\r
47 * @brief Maximum semaphore value for wait operations.
\r
49 #define TASKPOOL_MAX_SEM_VALUE 0xFFFF
\r
52 * @brief Reschedule delay in milliseconds for deferred jobs.
\r
54 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS ( 10ULL )
\r
56 /* ---------------------------------------------------------------------------------- */
\r
59 * Doxygen should ignore this section.
\r
61 * @brief The system task pool handle for all libraries to use.
\r
62 * User application can use the system task pool as well knowing that the usage will be shared with
\r
63 * the system libraries as well. The system task pool needs to be initialized before any library is used or
\r
64 * before any code that posts jobs to the task pool runs.
\r
66 _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };
\r
68 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */
\r
71 * @brief Initializes one instance of a Task pool cache.
\r
73 * @param[in] pCache The pre-allocated instance of the cache to initialize.
\r
75 static void _initJobsCache( _taskPoolCache_t * const pCache );
\r
78 * @brief Initialize a job.
\r
80 * @param[in] pJob The job to initialize.
\r
81 * @param[in] userCallback The user callback for the job.
\r
82 * @param[in] pUserContext The context tp be passed to the callback.
\r
83 * @param[in] isStatic A flag to indicate whether the job is statically or synamically allocated.
\r
85 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
86 IotTaskPoolRoutine_t userCallback,
\r
87 void * pUserContext,
\r
91 * @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
93 * @param[in] pCache The instance of the cache to extract the job from.
\r
95 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache );
\r
98 * Recycles one instance of a job into the cache or, if the cache is full, it destroys it.
\r
100 * @param[in] pCache The instance of the cache to recycle the job into.
\r
101 * @param[in] pJob The job to recycle.
\r
104 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
105 _taskPoolJob_t * const pJob );
\r
108 * Destroys one instance of a job.
\r
110 * @param[in] pJob The job to destroy.
\r
113 static void _destroyJob( _taskPoolJob_t * const pJob );
\r
115 /* -------------- The worker thread procedure for a task pool thread -------------- */
\r
118 * The procedure for a task pool worker thread.
\r
120 * @param[in] pUserContext The user context.
\r
123 static void _taskPoolWorker( void * pUserContext );
\r
125 /* -------------- Convenience functions to handle timer events -------------- */
\r
128 * Comparer for the time list.
\r
130 * param[in] pTimerEventLink1 The link to the first timer event.
\r
131 * param[in] pTimerEventLink1 The link to the first timer event.
\r
133 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
134 const IotLink_t * const pTimerEventLink2 );
\r
137 * Reschedules the timer for handling deferred jobs to the next timeout.
\r
139 * param[in] timer The timer to reschedule.
\r
140 * param[in] pFirstTimerEvent The timer event that carries the timeout and job inforamtion.
\r
142 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
143 _taskPoolTimerEvent_t * const pFirstTimerEvent );
\r
146 * The task pool timer procedure for scheduling deferred jobs.
\r
148 * param[in] timer The timer to handle.
\r
150 static void _timerCallback( TimerHandle_t xTimer );
\r
152 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */
\r
155 * Parameter validation for a task pool initialization.
\r
157 * @param[in] pInfo The initialization information for the task pool.
\r
160 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo );
\r
163 * Initializes a pre-allocated instance of a task pool.
\r
165 * @param[in] pInfo The initialization information for the task pool.
\r
166 * @param[in] pTaskPool The pre-allocated instance of the task pool to initialize.
\r
169 static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,
\r
170 _taskPool_t * const pTaskPool );
\r
173 * Initializes a pre-allocated instance of a task pool.
\r
175 * @param[in] pInfo The initialization information for the task pool.
\r
176 * @param[out] pTaskPool A pointer to the task pool data structure to initialize.
\r
179 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
180 _taskPool_t * const pTaskPool );
\r
183 * Destroys one instance of a task pool.
\r
185 * @param[in] pTaskPool The task pool to destroy.
\r
188 static void _destroyTaskPool( _taskPool_t * const pTaskPool );
\r
191 * Check for the exit condition.
\r
193 * @param[in] pTaskPool The task pool to destroy.
\r
196 static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool );
\r
199 * Set the exit condition.
\r
201 * @param[in] pTaskPool The task pool to destroy.
\r
202 * @param[in] threads The number of threads active in the task pool at shutdown time.
\r
205 static void _signalShutdown( _taskPool_t * const pTaskPool,
\r
206 uint32_t threads );
\r
209 * Places a job in the dispatch queue.
\r
211 * @param[in] pTaskPool The task pool to scheduel the job with.
\r
212 * @param[in] pJob The job to schedule.
\r
213 * @param[in] flags The job flags.
\r
216 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
217 _taskPoolJob_t * const pJob );
\r
220 * Matches a deferred job in the timer queue with its timer event wrapper.
\r
222 * @param[in] pLink A pointer to the timer event link in the timer queue.
\r
223 * @param[in] pMatch A pointer to the job to match.
\r
226 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
230 * Tries to cancel a job.
\r
232 * @param[in] pTaskPool The task pool to cancel an operation against.
\r
233 * @param[in] pJob The job to cancel.
\r
234 * @param[out] pStatus The status of the job at the time of cancellation.
\r
237 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
238 _taskPoolJob_t * const pJob,
\r
239 IotTaskPoolJobStatus_t * const pStatus );
\r
241 /* ---------------------------------------------------------------------------------------------- */
\r
243 IotTaskPool_t IotTaskPool_GetSystemTaskPool( void )
\r
245 return &_IotSystemTaskPool;
\r
248 /*-----------------------------------------------------------*/
\r
250 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )
\r
252 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
254 /* At this time the task pool cannot be created before the scheduler has
\r
255 started because the function attempts to block on synchronization
\r
256 primitives (although I'm not sure why). */
\r
257 configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED );
\r
259 /* Guard against multiple attempts to create the system task pool in case
\r
260 this function is called by more than one library initialization routine. */
\r
261 if( _IotSystemTaskPool.running == false )
\r
263 /* Parameter checking. */
\r
264 TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );
\r
266 /* Create the system task pool pool. */
\r
267 TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, &_IotSystemTaskPool ) );
\r
270 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
273 /*-----------------------------------------------------------*/
\r
275 IotTaskPoolError_t IotTaskPool_Create( const IotTaskPoolInfo_t * const pInfo,
\r
276 IotTaskPool_t * const pTaskPool )
\r
278 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
280 _taskPool_t * pTempTaskPool = NULL;
\r
282 /* Verify that the task pool storage is valid. */
\r
283 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
285 /* Parameter checking. */
\r
286 TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );
\r
288 /* Allocate the memory for the task pool */
\r
289 pTempTaskPool = ( _taskPool_t * ) IotTaskPool_MallocTaskPool( sizeof( _taskPool_t ) );
\r
291 if( pTempTaskPool == NULL )
\r
293 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
296 memset( pTempTaskPool, 0x00, sizeof( _taskPool_t ) );
\r
298 TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, pTempTaskPool ) );
\r
300 TASKPOOL_FUNCTION_CLEANUP();
\r
302 if( TASKPOOL_FAILED( status ) )
\r
304 if( pTempTaskPool != NULL )
\r
306 IotTaskPool_FreeTaskPool( pTempTaskPool );
\r
311 *pTaskPool = pTempTaskPool;
\r
314 TASKPOOL_FUNCTION_CLEANUP_END();
\r
317 /*-----------------------------------------------------------*/
\r
319 IotTaskPoolError_t IotTaskPool_Destroy( IotTaskPool_t taskPoolHandle )
\r
321 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
324 bool completeShutdown = true;
\r
326 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
328 /* Track how many threads the task pool owns. */
\r
329 uint32_t activeThreads;
\r
331 /* Parameter checking. */
\r
332 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
334 /* Destroying the task pool should be safe, and therefore we will grab the task pool lock.
\r
335 * No worker thread or application thread should access any data structure
\r
336 * in the task pool while the task pool is being destroyed. */
\r
337 taskENTER_CRITICAL();
\r
339 IotLink_t * pItemLink;
\r
341 /* Record how many active threads in the task pool. */
\r
342 activeThreads = pTaskPool->activeThreads;
\r
344 /* Destroying a Task pool happens in six (6) stages: First, (1) we clear the job queue and (2) the timer queue.
\r
345 * Then (3) we clear the jobs cache. We will then (4) wait for all worker threads to signal exit,
\r
346 * before (5) setting the exit condition and wake up all active worker threads. Finally (6) destroying
\r
347 * all task pool data structures and release the associated memory.
\r
350 /* (1) Clear the job queue. */
\r
355 pItemLink = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
357 if( pItemLink != NULL )
\r
359 _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );
\r
361 _destroyJob( pJob );
\r
363 } while( pItemLink );
\r
365 /* (2) Clear the timer queue. */
\r
367 _taskPoolTimerEvent_t * pTimerEvent;
\r
369 /* A deferred job may have fired already. Since deferred jobs will go through the same mutex
\r
370 * the shutdown sequence is holding at this stage, there is no risk for race conditions. Yet, we
\r
371 * need to let the deferred job to destroy the task pool. */
\r
373 pItemLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
375 if( pItemLink != NULL )
\r
377 TickType_t now = xTaskGetTickCount();
\r
379 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );
\r
381 if( pTimerEvent->expirationTime <= now )
\r
383 IotLogDebug( "Shutdown will be deferred to the timer thread" );
\r
385 /* Timer may have fired already! Let the timer thread destroy
\r
386 * complete the taskpool destruction sequence. */
\r
387 completeShutdown = false;
\r
390 /* Remove all timers from the timeout list. */
\r
393 pItemLink = IotListDouble_RemoveHead( &pTaskPool->timerEventsList );
\r
395 if( pItemLink == NULL )
\r
400 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );
\r
402 _destroyJob( pTimerEvent->job );
\r
404 IotTaskPool_FreeTimerEvent( pTimerEvent );
\r
409 /* (3) Clear the job cache. */
\r
414 pItemLink = IotListDouble_RemoveHead( &pTaskPool->jobsCache.freeList );
\r
416 if( pItemLink != NULL )
\r
418 _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );
\r
420 _destroyJob( pJob );
\r
422 } while( pItemLink );
\r
424 /* (4) Set the exit condition. */
\r
425 _signalShutdown( pTaskPool, activeThreads );
\r
427 taskEXIT_CRITICAL();
\r
429 /* (5) Wait for all active threads to reach the end of their life-span. */
\r
430 for( count = 0; count < activeThreads; ++count )
\r
432 xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );
\r
435 IotTaskPool_Assert( uxSemaphoreGetCount( pTaskPool->startStopSignal ) == 0 );
\r
436 IotTaskPool_Assert( pTaskPool->activeThreads == 0 );
\r
438 /* (6) Destroy all signaling objects. */
\r
439 if( completeShutdown == true )
\r
441 _destroyTaskPool( pTaskPool );
\r
443 /* Do not free the system task pool which is statically allocated. */
\r
444 if( pTaskPool != &_IotSystemTaskPool )
\r
446 IotTaskPool_FreeTaskPool( pTaskPool );
\r
450 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
453 /*-----------------------------------------------------------*/
\r
455 IotTaskPoolError_t IotTaskPool_SetMaxThreads( IotTaskPool_t taskPoolHandle,
\r
456 uint32_t maxThreads )
\r
458 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
460 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
462 /* Parameter checking. */
\r
463 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
464 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( maxThreads < 1UL );
\r
466 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
469 /*-----------------------------------------------------------*/
\r
471 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,
\r
472 void * pUserContext,
\r
473 IotTaskPoolJobStorage_t * const pJobStorage,
\r
474 IotTaskPoolJob_t * const ppJob )
\r
476 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
478 /* Parameter checking. */
\r
479 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
480 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobStorage );
\r
481 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
483 /* Build a job around the user-provided storage. */
\r
484 _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext, true );
\r
486 *ppJob = ( IotTaskPoolJob_t ) pJobStorage;
\r
488 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
491 /*-----------------------------------------------------------*/
\r
493 IotTaskPoolError_t IotTaskPool_CreateRecyclableSystemJob( IotTaskPoolRoutine_t userCallback,
\r
494 void * pUserContext,
\r
495 IotTaskPoolJob_t * const pJob )
\r
497 return IotTaskPool_CreateRecyclableJob ( &_IotSystemTaskPool, userCallback, pUserContext, pJob );
\r
500 /*-----------------------------------------------------------*/
\r
502 IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
503 IotTaskPoolRoutine_t userCallback,
\r
504 void * pUserContext,
\r
505 IotTaskPoolJob_t * const ppJob )
\r
507 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
509 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
510 _taskPoolJob_t * pTempJob = NULL;
\r
512 /* Parameter checking. */
\r
513 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
514 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );
\r
515 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );
\r
517 taskENTER_CRITICAL();
\r
519 /* Bail out early if this task pool is shutting down. */
\r
520 pTempJob = _fetchOrAllocateJob( &pTaskPool->jobsCache );
\r
522 taskEXIT_CRITICAL();
\r
524 if( pTempJob == NULL )
\r
526 IotLogInfo( "Failed to allocate a job." );
\r
528 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
531 _initializeJob( pTempJob, userCallback, pUserContext, false );
\r
535 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
538 /*-----------------------------------------------------------*/
\r
540 IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandle,
\r
541 IotTaskPoolJob_t pJobHandle )
\r
543 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
545 ( void ) taskPoolHandle;
\r
547 _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle;
\r
549 /* Parameter checking. */
\r
550 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
551 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle );
\r
553 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
555 _destroyJob( pJob );
\r
557 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
560 /*-----------------------------------------------------------*/
\r
562 IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle,
\r
563 IotTaskPoolJob_t pJob )
\r
565 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
567 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
569 /* Parameter checking. */
\r
570 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
571 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
573 taskENTER_CRITICAL();
\r
575 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
577 _recycleJob( &pTaskPool->jobsCache, pJob );
\r
579 taskEXIT_CRITICAL();
\r
581 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
584 /*-----------------------------------------------------------*/
\r
586 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,
\r
587 IotTaskPoolJob_t pJob,
\r
590 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
592 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
594 configASSERT( pTaskPool->running != false );
\r
596 /* Parameter checking. */
\r
597 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
598 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );
\r
599 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( ( flags != 0UL ) && ( flags != IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );
\r
601 taskENTER_CRITICAL(); //_RB_ Critical section is too long - does the whole thing need to be protected?
\r
603 _scheduleInternal( pTaskPool, pJob );
\r
605 taskEXIT_CRITICAL();
\r
607 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
610 /*-----------------------------------------------------------*/
\r
612 IotTaskPoolError_t IotTaskPool_ScheduleSystemJob( IotTaskPoolJob_t pJob,
\r
615 return IotTaskPool_Schedule( &_IotSystemTaskPool, pJob, flags );
\r
618 /*-----------------------------------------------------------*/
\r
620 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,
\r
621 IotTaskPoolJob_t job,
\r
624 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
626 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
628 /* Parameter checking. */
\r
629 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
630 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
632 if( timeMs == 0UL )
\r
634 TASKPOOL_SET_AND_GOTO_CLEANUP( IotTaskPool_Schedule( pTaskPool, job, 0 ) );
\r
637 taskENTER_CRITICAL();
\r
639 _taskPoolTimerEvent_t * pTimerEvent = IotTaskPool_MallocTimerEvent( sizeof( _taskPoolTimerEvent_t ) );
\r
641 if( pTimerEvent == NULL )
\r
643 taskEXIT_CRITICAL();
\r
645 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
648 IotLink_t * pTimerEventLink;
\r
650 TickType_t now = xTaskGetTickCount();
\r
652 pTimerEvent->link.pNext = NULL;
\r
653 pTimerEvent->link.pPrevious = NULL;
\r
654 pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );
\r
655 pTimerEvent->job = job;
\r
657 /* Append the timer event to the timer list. */
\r
658 IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );
\r
660 /* Update the job status to 'scheduled'. */
\r
661 job->status = IOT_TASKPOOL_STATUS_DEFERRED;
\r
663 /* Peek the first event in the timer event list. There must be at least one,
\r
664 * since we just inserted it. */
\r
665 pTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
666 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
668 /* If the event we inserted is at the front of the queue, then
\r
669 * we need to reschedule the underlying timer. */
\r
670 if( pTimerEventLink == &pTimerEvent->link )
\r
672 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );
\r
674 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
677 taskEXIT_CRITICAL();
\r
679 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
682 /*-----------------------------------------------------------*/
\r
684 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,
\r
685 IotTaskPoolJob_t job,
\r
686 IotTaskPoolJobStatus_t * const pStatus )
\r
688 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
690 /* Parameter checking. */
\r
691 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
692 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
693 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus );
\r
694 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
696 taskENTER_CRITICAL();
\r
698 *pStatus = job->status;
\r
700 taskEXIT_CRITICAL();
\r
702 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
705 /*-----------------------------------------------------------*/
\r
707 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,
\r
708 IotTaskPoolJob_t job,
\r
709 IotTaskPoolJobStatus_t * const pStatus )
\r
711 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
713 _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;
\r
715 /* Parameter checking. */
\r
716 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );
\r
717 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );
\r
719 if( pStatus != NULL )
\r
721 *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
724 taskENTER_CRITICAL();
\r
726 status = _tryCancelInternal( pTaskPool, job, pStatus );
\r
728 taskEXIT_CRITICAL();
\r
730 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
733 /*-----------------------------------------------------------*/
\r
735 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )
\r
737 return ( IotTaskPoolJobStorage_t * ) pJob;
\r
740 /*-----------------------------------------------------------*/
\r
742 const char * IotTaskPool_strerror( IotTaskPoolError_t status )
\r
744 const char * pMessage = NULL;
\r
748 case IOT_TASKPOOL_SUCCESS:
\r
749 pMessage = "SUCCESS";
\r
752 case IOT_TASKPOOL_BAD_PARAMETER:
\r
753 pMessage = "BAD PARAMETER";
\r
756 case IOT_TASKPOOL_ILLEGAL_OPERATION:
\r
757 pMessage = "ILLEGAL OPERATION";
\r
760 case IOT_TASKPOOL_NO_MEMORY:
\r
761 pMessage = "NO MEMORY";
\r
764 case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:
\r
765 pMessage = "SHUTDOWN IN PROGRESS";
\r
768 case IOT_TASKPOOL_CANCEL_FAILED:
\r
769 pMessage = "CANCEL FAILED";
\r
773 pMessage = "INVALID STATUS";
\r
780 /* ---------------------------------------------------------------------------------------------- */
\r
781 /* ---------------------------------------------------------------------------------------------- */
\r
782 /* ---------------------------------------------------------------------------------------------- */
\r
784 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo )
\r
786 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
788 /* Check input values for consistency. */
\r
789 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pInfo );
\r
790 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads > pInfo->maxThreads );
\r
791 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads < 1UL );
\r
792 TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->maxThreads < 1UL );
\r
794 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
797 static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,
\r
798 _taskPool_t * const pTaskPool )
\r
800 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
802 bool semStartStopInit = false;
\r
803 bool semDispatchInit = false;
\r
805 /* Initialize a job data structures that require no de-initialization.
\r
806 * All other data structures carry a value of 'NULL' before initailization.
\r
808 IotDeQueue_Create( &pTaskPool->dispatchQueue );
\r
809 IotListDouble_Create( &pTaskPool->timerEventsList );
\r
811 _initJobsCache( &pTaskPool->jobsCache );
\r
813 /* Initialize the semaphore to ensure all threads have started. */
\r
814 pTaskPool->startStopSignal = xSemaphoreCreateCountingStatic( pInfo->minThreads, 0, &pTaskPool->startStopSignalBuffer );
\r
816 if( pTaskPool->startStopSignal != NULL )
\r
818 semStartStopInit = true;
\r
820 /* Initialize the semaphore for waiting for incoming work. */
\r
821 pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );
\r
823 if( pTaskPool->dispatchSignal != NULL )
\r
825 semDispatchInit = true;
\r
829 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
834 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
837 TASKPOOL_FUNCTION_CLEANUP();
\r
839 if( TASKPOOL_FAILED( status ) )
\r
841 if( semStartStopInit )
\r
843 vSemaphoreDelete( &pTaskPool->startStopSignal );
\r
846 if( semDispatchInit )
\r
848 vSemaphoreDelete( &pTaskPool->dispatchSignal );
\r
852 TASKPOOL_FUNCTION_CLEANUP_END();
\r
855 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,
\r
856 _taskPool_t * const pTaskPool )
\r
858 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
861 uint32_t threadsCreated;
\r
863 /* Check input values for consistency. */
\r
864 TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );
\r
866 /* Zero out all data structures. */
\r
867 memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );
\r
869 /* Initialize all internal data structure prior to creating all threads. */
\r
870 TASKPOOL_ON_ERROR_GOTO_CLEANUP( _initTaskPoolControlStructures( pInfo, pTaskPool ) );
\r
872 /* Create the timer for a new connection. */
\r
873 pTaskPool->timer = xTimerCreate( NULL, portMAX_DELAY, pdFALSE, ( void * ) pTaskPool, _timerCallback );
\r
875 if( pTaskPool->timer == NULL )
\r
877 IotLogError( "Failed to create timer for task pool." );
\r
879 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
882 /* The task pool will initialize the minimum number of threads requested by the user upon start. */
\r
883 /* When a thread is created, it will signal a semaphore to signify that it is about to wait on incoming */
\r
884 /* jobs. A thread can be woken up for exit or for new jobs only at that point in time. */
\r
885 /* The exit condition is setting the maximum number of threads to 0. */
\r
887 /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */
\r
888 for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )
\r
890 TaskHandle_t task = NULL;
\r
892 BaseType_t res = xTaskCreate( _taskPoolWorker,
\r
899 /* Create one thread. */
\r
900 if( res == pdFALSE )
\r
902 IotLogError( "Could not create worker thread! Exiting..." );
\r
904 /* If creating one thread fails, set error condition and exit the loop. */
\r
905 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );
\r
908 /* Upon successful thread creation, increase the number of active threads. */
\r
909 pTaskPool->activeThreads++;
\r
910 IotTaskPool_Assert( task != NULL );
\r
915 TASKPOOL_FUNCTION_CLEANUP();
\r
917 /* Wait for threads to be ready to wait on the condition, so that threads are actually able to receive messages. */
\r
918 for( count = 0; count < threadsCreated; ++count )
\r
920 xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY ); /*_RB_ Is waiting necessary, and if so, is a semaphore necessary? */
\r
923 /* In case of failure, wait on the created threads to exit. */
\r
924 if( TASKPOOL_FAILED( status ) )
\r
926 /* Set the exit condition for the newly created threads. */
\r
927 _signalShutdown( pTaskPool, threadsCreated );
\r
929 /* Signal all threads to exit. */
\r
930 for( count = 0; count < threadsCreated; ++count )
\r
932 xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );
\r
935 _destroyTaskPool( pTaskPool );
\r
938 pTaskPool->running = true;
\r
940 TASKPOOL_FUNCTION_CLEANUP_END();
\r
943 /*-----------------------------------------------------------*/
\r
945 static void _destroyTaskPool( _taskPool_t * const pTaskPool )
\r
947 if( pTaskPool->timer != NULL )
\r
949 xTimerDelete( pTaskPool->timer, 0 );
\r
952 if( pTaskPool->dispatchSignal != NULL )
\r
954 vSemaphoreDelete( pTaskPool->dispatchSignal );
\r
957 if( pTaskPool->startStopSignal != NULL )
\r
959 vSemaphoreDelete( pTaskPool->startStopSignal );
\r
963 /* ---------------------------------------------------------------------------------------------- */
\r
965 static void _taskPoolWorker( void * pUserContext )
\r
967 IotTaskPool_Assert( pUserContext != NULL );
\r
969 IotTaskPoolRoutine_t userCallback = NULL;
\r
970 bool running = true;
\r
972 /* Extract pTaskPool pointer from context. */
\r
973 _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;
\r
975 /* Signal that this worker completed initialization and it is ready to receive notifications. */
\r
976 ( void ) xSemaphoreGive( pTaskPool->startStopSignal );
\r
978 /* OUTER LOOP: it controls the lifetiem of the worker thread: exit condition for a worker thread
\r
979 * is setting maxThreads to zero. A worker thread is running until the maximum number of allowed
\r
980 * threads is not zero and the active threads are less than the maximum number of allowed threads.
\r
984 IotLink_t * pFirst = NULL;
\r
985 _taskPoolJob_t * pJob = NULL;
\r
987 /* Wait on incoming notifications... */
\r
988 xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY );
\r
990 /* Acquire the lock to check the exit condition, and release the lock if the exit condition is verified,
\r
991 * or before waiting for incoming notifications.
\r
993 taskENTER_CRITICAL();
\r
995 /* If the exit condition is verified, update the number of active threads and exit the loop. */
\r
996 if( _IsShutdownStarted( pTaskPool ) )
\r
998 IotLogDebug( "Worker thread exiting because exit condition was set." );
\r
1000 /* Decrease the number of active threads. */
\r
1001 pTaskPool->activeThreads--;
\r
1003 taskEXIT_CRITICAL();
\r
1005 /* Signal that this worker is exiting. */
\r
1006 xSemaphoreGive( pTaskPool->startStopSignal );
\r
1008 /* On shutdown, abandon the OUTER LOOP immediately. */
\r
1012 /* Dequeue the first job in FIFO order. */
\r
1013 pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
1015 /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */
\r
1016 if( pFirst != NULL )
\r
1018 /* Extract the job from its link. */
\r
1019 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );
\r
1021 /* Update status to 'executing'. */
\r
1022 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
1023 userCallback = pJob->userCallback;
\r
1026 taskEXIT_CRITICAL();
\r
1028 /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */
\r
1029 while( pJob != NULL )
\r
1031 /* Process the job by invoking the associated callback with the user context.
\r
1032 * This task pool thread will not be available until the user callback returns.
\r
1035 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
1036 IotTaskPool_Assert( userCallback != NULL );
\r
1038 userCallback( pTaskPool, pJob, pJob->pUserContext );
\r
1040 /* This job is finished, clear its pointer. */
\r
1042 userCallback = NULL;
\r
1044 /* If this thread exceeded the quota, then let it terminate. */
\r
1045 if( running == false )
\r
1047 /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */
\r
1052 /* Acquire the lock before updating the job status. */
\r
1053 taskENTER_CRITICAL();
\r
1055 /* Try and dequeue the next job in the dispatch queue. */
\r
1056 IotLink_t * pItem = NULL;
\r
1058 /* Dequeue the next job from the dispatch queue. */
\r
1059 pItem = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );
\r
1061 /* If there is no job left in the dispatch queue, update the worker status and leave. */
\r
1062 if( pItem == NULL )
\r
1064 taskEXIT_CRITICAL();
\r
1066 /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */
\r
1071 pJob = IotLink_Container( _taskPoolJob_t, pItem, link );
\r
1073 userCallback = pJob->userCallback;
\r
1076 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;
\r
1078 taskEXIT_CRITICAL();
\r
1080 } while( running == true );
\r
1082 vTaskDelete( NULL );
\r
1085 /* ---------------------------------------------------------------------------------------------- */
\r
1087 static void _initJobsCache( _taskPoolCache_t * const pCache )
\r
1089 IotDeQueue_Create( &pCache->freeList );
\r
1091 pCache->freeCount = 0;
\r
1094 /*-----------------------------------------------------------*/
\r
1096 static void _initializeJob( _taskPoolJob_t * const pJob,
\r
1097 IotTaskPoolRoutine_t userCallback,
\r
1098 void * pUserContext,
\r
1101 pJob->link.pNext = NULL;
\r
1102 pJob->link.pPrevious = NULL;
\r
1103 pJob->userCallback = userCallback;
\r
1104 pJob->pUserContext = pUserContext;
\r
1108 pJob->flags = IOT_TASK_POOL_INTERNAL_STATIC;
\r
1109 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
1113 pJob->status = IOT_TASKPOOL_STATUS_READY;
\r
1117 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache )
\r
1119 _taskPoolJob_t * pJob = NULL;
\r
1120 IotLink_t * pLink = IotListDouble_RemoveHead( &( pCache->freeList ) );
\r
1122 if( pLink != NULL )
\r
1124 pJob = IotLink_Container( _taskPoolJob_t, pLink, link );
\r
1127 /* If there is no available job in the cache, then allocate one. */
\r
1128 if( pJob == NULL )
\r
1130 pJob = ( _taskPoolJob_t * ) IotTaskPool_MallocJob( sizeof( _taskPoolJob_t ) );
\r
1132 if( pJob != NULL )
\r
1134 memset( pJob, 0x00, sizeof( _taskPoolJob_t ) );
\r
1138 /* Log alocation failure for troubleshooting purposes. */
\r
1139 IotLogInfo( "Failed to allocate job." );
\r
1142 /* If there was a job in the cache, then make sure we keep the counters up-to-date. */
\r
1145 IotTaskPool_Assert( pCache->freeCount > 0 );
\r
1147 pCache->freeCount--;
\r
1153 /*-----------------------------------------------------------*/
\r
1155 static void _recycleJob( _taskPoolCache_t * const pCache,
\r
1156 _taskPoolJob_t * const pJob )
\r
1158 /* We should never try and recycling a job that is linked into some queue. */
\r
1159 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );
\r
1161 /* We will recycle the job if there is space in the cache. */
\r
1162 if( pCache->freeCount < IOT_TASKPOOL_JOBS_RECYCLE_LIMIT )
\r
1164 /* Destroy user data, for added safety&security. */
\r
1165 pJob->userCallback = NULL;
\r
1166 pJob->pUserContext = NULL;
\r
1168 /* Reset the status for added debuggability. */
\r
1169 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
1171 IotListDouble_InsertTail( &pCache->freeList, &pJob->link );
\r
1173 pCache->freeCount++;
\r
1175 IotTaskPool_Assert( pCache->freeCount >= 1 );
\r
1179 _destroyJob( pJob );
\r
1183 /*-----------------------------------------------------------*/
\r
1185 static void _destroyJob( _taskPoolJob_t * const pJob )
\r
1187 /* Destroy user data, for added safety & security. */
\r
1188 pJob->userCallback = NULL;
\r
1189 pJob->pUserContext = NULL;
\r
1191 /* Reset the status for added debuggability. */
\r
1192 pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;
\r
1194 /* Only dispose of dynamically allocated jobs. */
\r
1195 if( ( pJob->flags & IOT_TASK_POOL_INTERNAL_STATIC ) == 0UL )
\r
1197 IotTaskPool_FreeJob( pJob );
\r
1201 /* ---------------------------------------------------------------------------------------------- */
\r
1203 static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool )
\r
1205 return( pTaskPool->running == false );
\r
1208 /*-----------------------------------------------------------*/
\r
1210 static void _signalShutdown( _taskPool_t * const pTaskPool,
\r
1211 uint32_t threads )
\r
1215 /* Set the exit condition. */
\r
1216 pTaskPool->running = false;
\r
1218 /* Broadcast to all active threads to wake-up. Active threads do check the exit condition right after wakein up. */
\r
1219 for( count = 0; count < threads; ++count )
\r
1221 ( void ) xSemaphoreGive( pTaskPool->dispatchSignal );
\r
1225 /* ---------------------------------------------------------------------------------------------- */
\r
1227 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,
\r
1228 _taskPoolJob_t * const pJob )
\r
1230 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
1232 /* Update the job status to 'scheduled'. */
\r
1233 pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;
\r
1235 BaseType_t higherPriorityTaskWoken;
\r
1237 /* Append the job to the dispatch queue. */
\r
1238 IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link );
\r
1240 /* Signal a worker to pick up the job. */
\r
1241 ( void ) xSemaphoreGiveFromISR( pTaskPool->dispatchSignal, &higherPriorityTaskWoken );
\r
1243 portYIELD_FROM_ISR( higherPriorityTaskWoken );
\r
1245 TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL();
\r
1248 /*-----------------------------------------------------------*/
\r
1250 static bool _matchJobByPointer( const IotLink_t * const pLink,
\r
1253 const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;
\r
1255 const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
1257 if( pJob == pTimerEvent->job )
\r
1265 /*-----------------------------------------------------------*/
\r
1267 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,
\r
1268 _taskPoolJob_t * const pJob,
\r
1269 IotTaskPoolJobStatus_t * const pStatus )
\r
1271 TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );
\r
1273 bool cancelable = false;
\r
1275 /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */
\r
1277 IotTaskPoolJobStatus_t currentStatus = pJob->status;
\r
1279 switch( currentStatus )
\r
1281 case IOT_TASKPOOL_STATUS_READY:
\r
1282 case IOT_TASKPOOL_STATUS_DEFERRED:
\r
1283 case IOT_TASKPOOL_STATUS_SCHEDULED:
\r
1284 case IOT_TASKPOOL_STATUS_CANCELED:
\r
1285 cancelable = true;
\r
1288 case IOT_TASKPOOL_STATUS_COMPLETED:
\r
1289 /* Log mesggesong purposes. */
\r
1290 IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );
\r
1294 /* Log mesggesong purposes. */
\r
1295 IotLogError( "Attempt to cancel a job with an undefined state." );
\r
1299 /* Update the returned status to the current status of the job. */
\r
1300 if( pStatus != NULL )
\r
1302 *pStatus = currentStatus;
\r
1305 if( cancelable == false )
\r
1307 TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_CANCEL_FAILED );
\r
1311 /* Update the status of the job. */
\r
1312 pJob->status = IOT_TASKPOOL_STATUS_CANCELED;
\r
1314 /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch
\r
1315 * queue and signal any waiting threads. */
\r
1316 if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )
\r
1318 /* A scheduled work items must be in the dispatch queue. */
\r
1319 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) );
\r
1321 IotDeQueue_Remove( &pJob->link );
\r
1324 /* If the job current status is 'deferred' then the job has to be pending
\r
1325 * in the timeouts queue. */
\r
1326 else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )
\r
1328 /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */
\r
1329 IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &pTaskPool->timerEventsList, NULL, _matchJobByPointer, pJob );
\r
1330 IotTaskPool_Assert( pTimerEventLink != NULL );
\r
1332 if( pTimerEventLink != NULL )
\r
1334 bool shouldReschedule = false;
\r
1336 /* If the job being cancelled was at the head of the timeouts queue, then we need to reschedule the timer
\r
1337 * with the next job timeout */
\r
1338 IotLink_t * pHeadLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1340 if( pHeadLink == pTimerEventLink )
\r
1342 shouldReschedule = true;
\r
1345 /* Remove the timer event associated with the canceled job and free the associated memory. */
\r
1346 IotListDouble_Remove( pTimerEventLink );
\r
1347 IotTaskPool_FreeTimerEvent( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ) );
\r
1349 if( shouldReschedule )
\r
1351 IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1353 if( pNextTimerEventLink != NULL )
\r
1355 _rescheduleDeferredJobsTimer( pTaskPool->timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );
\r
1362 /* A cancelable job status should be either 'scheduled' or 'deferrred'. */
\r
1363 IotTaskPool_Assert( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );
\r
1367 TASKPOOL_NO_FUNCTION_CLEANUP();
\r
1370 /*-----------------------------------------------------------*/
\r
1372 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,
\r
1373 const IotLink_t * const pTimerEventLink2 )
\r
1375 const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1378 const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,
\r
1382 if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )
\r
1387 if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )
\r
1395 /*-----------------------------------------------------------*/
\r
1397 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,
\r
1398 _taskPoolTimerEvent_t * const pFirstTimerEvent )
\r
1400 uint64_t delta = 0;
\r
1401 TickType_t now = xTaskGetTickCount();
\r
1403 if( pFirstTimerEvent->expirationTime > now )
\r
1405 delta = pFirstTimerEvent->expirationTime - now;
\r
1408 if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )
\r
1410 delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */
\r
1413 IotTaskPool_Assert( delta > 0 );
\r
1415 if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )
\r
1417 IotLogWarn( "Failed to re-arm timer for task pool" );
\r
1421 /*-----------------------------------------------------------*/
\r
1423 static void _timerCallback( TimerHandle_t xTimer )
\r
1425 _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );
\r
1427 IotTaskPool_Assert( pTaskPool );
\r
1429 _taskPoolTimerEvent_t * pTimerEvent = NULL;
\r
1431 IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );
\r
1433 /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.
\r
1434 * If this mutex cannot be locked it means that another thread is manipulating the
\r
1435 * timeouts list, and will reset the timer to fire again, although it will be late.
\r
1437 taskENTER_CRITICAL();
\r
1439 /* Check again for shutdown and bail out early in case. */
\r
1440 if( _IsShutdownStarted( pTaskPool ) )
\r
1442 taskEXIT_CRITICAL();
\r
1444 /* Complete the shutdown sequence. */
\r
1445 _destroyTaskPool( pTaskPool );
\r
1450 /* Dispatch all deferred job whose timer expired, then reset the timer for the next
\r
1451 * job down the line. */
\r
1454 /* Peek the first event in the timer event list. */
\r
1455 IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );
\r
1457 /* Check if the timer misfired for any reason. */
\r
1458 if( pLink != NULL )
\r
1460 /* Record the current time. */
\r
1461 TickType_t now = xTaskGetTickCount();
\r
1463 /* Extract the job from its envelope. */
\r
1464 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );
\r
1466 /* Check if the first event should be processed now. */
\r
1467 if( pTimerEvent->expirationTime <= now )
\r
1469 /* Remove the timer event for immediate processing. */
\r
1470 IotListDouble_Remove( &( pTimerEvent->link ) );
\r
1474 /* The first element in the timer queue shouldn't be processed yet.
\r
1475 * Arm the timer for when it should be processed and leave altogether. */
\r
1476 _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );
\r
1481 /* If there are no timer events to process, terminate this thread. */
\r
1484 IotLogDebug( "No further timer events to process. Exiting timer thread." );
\r
1489 IotLogDebug( "Scheduling job from timer event." );
\r
1491 /* Queue the job associated with the received timer event. */
\r
1492 ( void ) _scheduleInternal( pTaskPool, pTimerEvent->job );
\r
1494 /* Free the timer event. */
\r
1495 IotTaskPool_FreeTimerEvent( pTimerEvent );
\r
1498 taskEXIT_CRITICAL();
\r