]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c
In small FreeRTOS applications it is unlikely there will be any task pools other...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-IoT-SDK / c_sdk / standard / common / taskpool / iot_taskpool.c
1 /*\r
2  * Amazon FreeRTOS Common V1.0.0\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 /**\r
26  * @file iot_taskpool.c\r
27  * @brief Implements the task pool functions in iot_taskpool.h\r
28  */\r
29 \r
30 /* The config header is always included first. */\r
31 #include "iot_config.h"\r
32 \r
33 /* Standard includes. */\r
34 #include <stdbool.h>\r
35 #include <stddef.h>\r
36 #include <stdint.h>\r
37 #include <string.h>\r
38 \r
39 /* Platform layer includes. */\r
40 #include "platform/iot_threads.h"\r
41 #include "platform/iot_clock.h"\r
42 \r
43 /* Task pool internal include. */\r
44 #include "private/iot_taskpool_internal.h"\r
45 \r
46 /**\r
47  * @brief Maximum semaphore value for wait operations.\r
48  */\r
49 #define TASKPOOL_MAX_SEM_VALUE              0xFFFF\r
50 \r
51 /**\r
52  * @brief Reschedule delay in milliseconds for deferred jobs.\r
53  */\r
54 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS    ( 10ULL )\r
55 \r
56 /* ---------------------------------------------------------------------------------- */\r
57 \r
58 /**\r
59  * Doxygen should ignore this section.\r
60  *\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
65  */\r
66 _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };\r
67 \r
68 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */\r
69 \r
70 /**\r
71  * @brief Initializes one instance of a Task pool cache.\r
72  *\r
73  * @param[in] pCache The pre-allocated instance of the cache to initialize.\r
74  */\r
75 static void _initJobsCache( _taskPoolCache_t * const pCache );\r
76 \r
77 /**\r
78  * @brief Initialize a job.\r
79  *\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
84  */\r
85 static void _initializeJob( _taskPoolJob_t * const pJob,\r
86                             IotTaskPoolRoutine_t userCallback,\r
87                             void * pUserContext,\r
88                             bool isStatic );\r
89 \r
90 /**\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
92  *\r
93  * @param[in] pCache The instance of the cache to extract the job from.\r
94  */\r
95 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache );\r
96 \r
97 /**\r
98  * Recycles one instance of a job into the cache or, if the cache is full, it destroys it.\r
99  *\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
102  *\r
103  */\r
104 static void _recycleJob( _taskPoolCache_t * const pCache,\r
105                          _taskPoolJob_t * const pJob );\r
106 \r
107 /**\r
108  * Destroys one instance of a job.\r
109  *\r
110  * @param[in] pJob The job to destroy.\r
111  *\r
112  */\r
113 static void _destroyJob( _taskPoolJob_t * const pJob );\r
114 \r
115 /* -------------- The worker thread procedure for a task pool thread -------------- */\r
116 \r
117 /**\r
118  * The procedure for a task pool worker thread.\r
119  *\r
120  * @param[in] pUserContext The user context.\r
121  *\r
122  */\r
123 static void _taskPoolWorker( void * pUserContext );\r
124 \r
125 /* -------------- Convenience functions to handle timer events  -------------- */\r
126 \r
127 /**\r
128  * Comparer for the time list.\r
129  *\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
132  */\r
133 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
134                                    const IotLink_t * const pTimerEventLink2 );\r
135 \r
136 /**\r
137  * Reschedules the timer for handling deferred jobs to the next timeout.\r
138  *\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
141  */\r
142 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
143                                           _taskPoolTimerEvent_t * const pFirstTimerEvent );\r
144 \r
145 /**\r
146  * The task pool timer procedure for scheduling deferred jobs.\r
147  *\r
148  * param[in] timer The timer to handle.\r
149  */\r
150 static void _timerCallback( TimerHandle_t xTimer );\r
151 \r
152 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */\r
153 \r
154 /**\r
155  * Parameter validation for a task pool initialization.\r
156  *\r
157  * @param[in] pInfo The initialization information for the task pool.\r
158  *\r
159  */\r
160 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo );\r
161 \r
162 /**\r
163  * Initializes a pre-allocated instance of a task pool.\r
164  *\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
167  *\r
168  */\r
169 static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,\r
170                                                           _taskPool_t * const pTaskPool );\r
171 \r
172 /**\r
173  * Initializes a pre-allocated instance of a task pool.\r
174  *\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
177  *\r
178  */\r
179 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,\r
180                                            _taskPool_t * const pTaskPool );\r
181 \r
182 /**\r
183  * Destroys one instance of a task pool.\r
184  *\r
185  * @param[in] pTaskPool The task pool to destroy.\r
186  *\r
187  */\r
188 static void _destroyTaskPool( _taskPool_t * const pTaskPool );\r
189 \r
190 /**\r
191  * Check for the exit condition.\r
192  *\r
193  * @param[in] pTaskPool The task pool to destroy.\r
194  *\r
195  */\r
196 static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool );\r
197 \r
198 /**\r
199  * Set the exit condition.\r
200  *\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
203  *\r
204  */\r
205 static void _signalShutdown( _taskPool_t * const pTaskPool,\r
206                              uint32_t threads );\r
207 \r
208 /**\r
209  * Places a job in the dispatch queue.\r
210  *\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
214  *\r
215  */\r
216 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,\r
217                                              _taskPoolJob_t * const pJob );\r
218 \r
219 /**\r
220  * Matches a deferred job in the timer queue with its timer event wrapper.\r
221  *\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
224  *\r
225  */\r
226 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
227                                 void * pMatch );\r
228 \r
229 /**\r
230  * Tries to cancel a job.\r
231  *\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
235  *\r
236  */\r
237 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,\r
238                                               _taskPoolJob_t * const pJob,\r
239                                               IotTaskPoolJobStatus_t * const pStatus );\r
240 \r
241 /* ---------------------------------------------------------------------------------------------- */\r
242 \r
243 IotTaskPool_t IotTaskPool_GetSystemTaskPool( void )\r
244 {\r
245     return &_IotSystemTaskPool;\r
246 }\r
247 \r
248 /*-----------------------------------------------------------*/\r
249 \r
250 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )\r
251 {\r
252     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
253 \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
258 \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
262     {\r
263         /* Parameter checking. */\r
264         TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );\r
265 \r
266         /* Create the system task pool pool. */\r
267         TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, &_IotSystemTaskPool ) );\r
268     }\r
269 \r
270     TASKPOOL_NO_FUNCTION_CLEANUP();\r
271 }\r
272 \r
273 /*-----------------------------------------------------------*/\r
274 \r
275 IotTaskPoolError_t IotTaskPool_Create( const IotTaskPoolInfo_t * const pInfo,\r
276                                        IotTaskPool_t * const pTaskPool )\r
277 {\r
278     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
279 \r
280     _taskPool_t * pTempTaskPool = NULL;\r
281 \r
282     /* Verify that the task pool storage is valid. */\r
283     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
284 \r
285     /* Parameter checking. */\r
286     TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );\r
287 \r
288     /* Allocate the memory for the task pool */\r
289     pTempTaskPool = ( _taskPool_t * ) IotTaskPool_MallocTaskPool( sizeof( _taskPool_t ) );\r
290 \r
291     if( pTempTaskPool == NULL )\r
292     {\r
293         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
294     }\r
295 \r
296     memset( pTempTaskPool, 0x00, sizeof( _taskPool_t ) );\r
297 \r
298     TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, pTempTaskPool ) );\r
299 \r
300     TASKPOOL_FUNCTION_CLEANUP();\r
301 \r
302     if( TASKPOOL_FAILED( status ) )\r
303     {\r
304         if( pTempTaskPool != NULL )\r
305         {\r
306             IotTaskPool_FreeTaskPool( pTempTaskPool );\r
307         }\r
308     }\r
309     else\r
310     {\r
311         *pTaskPool = pTempTaskPool;\r
312     }\r
313 \r
314     TASKPOOL_FUNCTION_CLEANUP_END();\r
315 }\r
316 \r
317 /*-----------------------------------------------------------*/\r
318 \r
319 IotTaskPoolError_t IotTaskPool_Destroy( IotTaskPool_t taskPoolHandle )\r
320 {\r
321     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
322 \r
323     uint32_t count;\r
324     bool completeShutdown = true;\r
325 \r
326     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
327 \r
328     /* Track how many threads the task pool owns. */\r
329     uint32_t activeThreads;\r
330 \r
331     /* Parameter checking. */\r
332     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
333 \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
338     {\r
339         IotLink_t * pItemLink;\r
340 \r
341         /* Record how many active threads in the task pool. */\r
342         activeThreads = pTaskPool->activeThreads;\r
343 \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
348          */\r
349 \r
350         /* (1) Clear the job queue. */\r
351         do\r
352         {\r
353             pItemLink = NULL;\r
354 \r
355             pItemLink = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
356 \r
357             if( pItemLink != NULL )\r
358             {\r
359                 _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );\r
360 \r
361                 _destroyJob( pJob );\r
362             }\r
363         } while( pItemLink );\r
364 \r
365         /* (2) Clear the timer queue. */\r
366         {\r
367             _taskPoolTimerEvent_t * pTimerEvent;\r
368 \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
372 \r
373             pItemLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
374 \r
375             if( pItemLink != NULL )\r
376             {\r
377                 TickType_t now = xTaskGetTickCount();\r
378 \r
379                 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );\r
380 \r
381                 if( pTimerEvent->expirationTime <= now )\r
382                 {\r
383                     IotLogDebug( "Shutdown will be deferred to the timer thread" );\r
384 \r
385                     /* Timer may have fired already! Let the timer thread destroy\r
386                      * complete the taskpool destruction sequence. */\r
387                     completeShutdown = false;\r
388                 }\r
389 \r
390                 /* Remove all timers from the timeout list. */\r
391                 for( ; ; )\r
392                 {\r
393                     pItemLink = IotListDouble_RemoveHead( &pTaskPool->timerEventsList );\r
394 \r
395                     if( pItemLink == NULL )\r
396                     {\r
397                         break;\r
398                     }\r
399 \r
400                     pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pItemLink, link );\r
401 \r
402                     _destroyJob( pTimerEvent->job );\r
403 \r
404                     IotTaskPool_FreeTimerEvent( pTimerEvent );\r
405                 }\r
406             }\r
407         }\r
408 \r
409         /* (3) Clear the job cache. */\r
410         do\r
411         {\r
412             pItemLink = NULL;\r
413 \r
414             pItemLink = IotListDouble_RemoveHead( &pTaskPool->jobsCache.freeList );\r
415 \r
416             if( pItemLink != NULL )\r
417             {\r
418                 _taskPoolJob_t * pJob = IotLink_Container( _taskPoolJob_t, pItemLink, link );\r
419 \r
420                 _destroyJob( pJob );\r
421             }\r
422         } while( pItemLink );\r
423 \r
424         /* (4) Set the exit condition. */\r
425         _signalShutdown( pTaskPool, activeThreads );\r
426     }\r
427     taskEXIT_CRITICAL();\r
428 \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
431     {\r
432         xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );\r
433     }\r
434 \r
435     IotTaskPool_Assert( uxSemaphoreGetCount( pTaskPool->startStopSignal ) == 0 );\r
436     IotTaskPool_Assert( pTaskPool->activeThreads == 0 );\r
437 \r
438     /* (6) Destroy all signaling objects. */\r
439     if( completeShutdown == true )\r
440     {\r
441         _destroyTaskPool( pTaskPool );\r
442 \r
443         /* Do not free the system task pool which is statically allocated. */\r
444         if( pTaskPool != &_IotSystemTaskPool )\r
445         {\r
446             IotTaskPool_FreeTaskPool( pTaskPool );\r
447         }\r
448     }\r
449 \r
450     TASKPOOL_NO_FUNCTION_CLEANUP();\r
451 }\r
452 \r
453 /*-----------------------------------------------------------*/\r
454 \r
455 IotTaskPoolError_t IotTaskPool_SetMaxThreads( IotTaskPool_t taskPoolHandle,\r
456                                               uint32_t maxThreads )\r
457 {\r
458     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
459 \r
460     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
461 \r
462     /* Parameter checking. */\r
463     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
464     TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( maxThreads < 1UL );\r
465 \r
466     TASKPOOL_NO_FUNCTION_CLEANUP();\r
467 }\r
468 \r
469 /*-----------------------------------------------------------*/\r
470 \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
475 {\r
476     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
477 \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
482 \r
483     /* Build a job around the user-provided storage. */\r
484     _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext, true );\r
485 \r
486     *ppJob = ( IotTaskPoolJob_t ) pJobStorage;\r
487 \r
488     TASKPOOL_NO_FUNCTION_CLEANUP();\r
489 }\r
490 \r
491 /*-----------------------------------------------------------*/\r
492 \r
493 IotTaskPoolError_t IotTaskPool_CreateRecyclableSystemJob( IotTaskPoolRoutine_t userCallback,\r
494                                                           void * pUserContext,\r
495                                                           IotTaskPoolJob_t * const pJob )\r
496 {\r
497     return IotTaskPool_CreateRecyclableJob ( &_IotSystemTaskPool, userCallback, pUserContext, pJob );\r
498 }\r
499 \r
500 /*-----------------------------------------------------------*/\r
501 \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
506 {\r
507     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
508 \r
509     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
510     _taskPoolJob_t * pTempJob = NULL;\r
511 \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
516 \r
517     taskENTER_CRITICAL();\r
518     {\r
519         /* Bail out early if this task pool is shutting down. */\r
520         pTempJob = _fetchOrAllocateJob( &pTaskPool->jobsCache );\r
521     }\r
522     taskEXIT_CRITICAL();\r
523 \r
524     if( pTempJob == NULL )\r
525     {\r
526         IotLogInfo( "Failed to allocate a job." );\r
527 \r
528         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
529     }\r
530 \r
531     _initializeJob( pTempJob, userCallback, pUserContext, false );\r
532 \r
533     *ppJob = pTempJob;\r
534 \r
535     TASKPOOL_NO_FUNCTION_CLEANUP();\r
536 }\r
537 \r
538 /*-----------------------------------------------------------*/\r
539 \r
540 IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandle,\r
541                                                      IotTaskPoolJob_t pJobHandle )\r
542 {\r
543     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
544 \r
545     ( void ) taskPoolHandle;\r
546 \r
547     _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle;\r
548 \r
549     /* Parameter checking. */\r
550     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );\r
551     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle );\r
552 \r
553     IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
554 \r
555     _destroyJob( pJob );\r
556 \r
557     TASKPOOL_NO_FUNCTION_CLEANUP();\r
558 }\r
559 \r
560 /*-----------------------------------------------------------*/\r
561 \r
562 IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle,\r
563                                            IotTaskPoolJob_t pJob )\r
564 {\r
565     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
566 \r
567     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
568 \r
569     /* Parameter checking. */\r
570     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );\r
571     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );\r
572 \r
573     taskENTER_CRITICAL();\r
574     {\r
575         IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
576 \r
577         _recycleJob( &pTaskPool->jobsCache, pJob );\r
578     }\r
579     taskEXIT_CRITICAL();\r
580 \r
581     TASKPOOL_NO_FUNCTION_CLEANUP();\r
582 }\r
583 \r
584 /*-----------------------------------------------------------*/\r
585 \r
586 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,\r
587                                          IotTaskPoolJob_t pJob,\r
588                                          uint32_t flags )\r
589 {\r
590     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
591 \r
592     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
593 \r
594     configASSERT( pTaskPool->running != false );\r
595 \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
600 \r
601     taskENTER_CRITICAL(); //_RB_ Critical section is too long - does the whole thing need to be protected?\r
602     {\r
603         _scheduleInternal( pTaskPool, pJob );\r
604     }\r
605     taskEXIT_CRITICAL();\r
606 \r
607     TASKPOOL_NO_FUNCTION_CLEANUP();\r
608 }\r
609 \r
610 /*-----------------------------------------------------------*/\r
611 \r
612 IotTaskPoolError_t IotTaskPool_ScheduleSystemJob( IotTaskPoolJob_t pJob,\r
613                                                   uint32_t flags )\r
614 {\r
615     return IotTaskPool_Schedule( &_IotSystemTaskPool, pJob, flags );\r
616 }\r
617 \r
618 /*-----------------------------------------------------------*/\r
619 \r
620 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,\r
621                                                  IotTaskPoolJob_t job,\r
622                                                  uint32_t timeMs )\r
623 {\r
624     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
625 \r
626     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
627 \r
628     /* Parameter checking. */\r
629     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );\r
630     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
631 \r
632     if( timeMs == 0UL )\r
633     {\r
634         TASKPOOL_SET_AND_GOTO_CLEANUP( IotTaskPool_Schedule( pTaskPool, job, 0 ) );\r
635     }\r
636 \r
637     taskENTER_CRITICAL();\r
638     {\r
639         _taskPoolTimerEvent_t * pTimerEvent = IotTaskPool_MallocTimerEvent( sizeof( _taskPoolTimerEvent_t ) );\r
640 \r
641         if( pTimerEvent == NULL )\r
642         {\r
643             taskEXIT_CRITICAL();\r
644 \r
645             TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
646         }\r
647 \r
648         IotLink_t * pTimerEventLink;\r
649 \r
650         TickType_t now = xTaskGetTickCount();\r
651 \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
656 \r
657         /* Append the timer event to the timer list. */\r
658         IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );\r
659 \r
660         /* Update the job status to 'scheduled'. */\r
661         job->status = IOT_TASKPOOL_STATUS_DEFERRED;\r
662 \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
667 \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
671         {\r
672             pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );\r
673 \r
674             _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );\r
675         }\r
676     }\r
677     taskEXIT_CRITICAL();\r
678 \r
679     TASKPOOL_NO_FUNCTION_CLEANUP();\r
680 }\r
681 \r
682 /*-----------------------------------------------------------*/\r
683 \r
684 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,\r
685                                           IotTaskPoolJob_t job,\r
686                                           IotTaskPoolJobStatus_t * const pStatus )\r
687 {\r
688     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
689 \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
695 \r
696     taskENTER_CRITICAL();\r
697     {\r
698         *pStatus = job->status;\r
699     }\r
700     taskEXIT_CRITICAL();\r
701 \r
702     TASKPOOL_NO_FUNCTION_CLEANUP();\r
703 }\r
704 \r
705 /*-----------------------------------------------------------*/\r
706 \r
707 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,\r
708                                           IotTaskPoolJob_t job,\r
709                                           IotTaskPoolJobStatus_t * const pStatus )\r
710 {\r
711     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
712 \r
713     _taskPool_t * pTaskPool = ( _taskPool_t * ) taskPoolHandle;\r
714 \r
715     /* Parameter checking. */\r
716     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( taskPoolHandle );\r
717     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
718 \r
719     if( pStatus != NULL )\r
720     {\r
721         *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;\r
722     }\r
723 \r
724     taskENTER_CRITICAL();\r
725     {\r
726         status = _tryCancelInternal( pTaskPool, job, pStatus );\r
727     }\r
728     taskEXIT_CRITICAL();\r
729 \r
730     TASKPOOL_NO_FUNCTION_CLEANUP();\r
731 }\r
732 \r
733 /*-----------------------------------------------------------*/\r
734 \r
735 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )\r
736 {\r
737     return ( IotTaskPoolJobStorage_t * ) pJob;\r
738 }\r
739 \r
740 /*-----------------------------------------------------------*/\r
741 \r
742 const char * IotTaskPool_strerror( IotTaskPoolError_t status )\r
743 {\r
744     const char * pMessage = NULL;\r
745 \r
746     switch( status )\r
747     {\r
748         case IOT_TASKPOOL_SUCCESS:\r
749             pMessage = "SUCCESS";\r
750             break;\r
751 \r
752         case IOT_TASKPOOL_BAD_PARAMETER:\r
753             pMessage = "BAD PARAMETER";\r
754             break;\r
755 \r
756         case IOT_TASKPOOL_ILLEGAL_OPERATION:\r
757             pMessage = "ILLEGAL OPERATION";\r
758             break;\r
759 \r
760         case IOT_TASKPOOL_NO_MEMORY:\r
761             pMessage = "NO MEMORY";\r
762             break;\r
763 \r
764         case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:\r
765             pMessage = "SHUTDOWN IN PROGRESS";\r
766             break;\r
767 \r
768         case IOT_TASKPOOL_CANCEL_FAILED:\r
769             pMessage = "CANCEL FAILED";\r
770             break;\r
771 \r
772         default:\r
773             pMessage = "INVALID STATUS";\r
774             break;\r
775     }\r
776 \r
777     return pMessage;\r
778 }\r
779 \r
780 /* ---------------------------------------------------------------------------------------------- */\r
781 /* ---------------------------------------------------------------------------------------------- */\r
782 /* ---------------------------------------------------------------------------------------------- */\r
783 \r
784 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo )\r
785 {\r
786     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
787 \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
793 \r
794     TASKPOOL_NO_FUNCTION_CLEANUP();\r
795 }\r
796 \r
797 static IotTaskPoolError_t _initTaskPoolControlStructures( const IotTaskPoolInfo_t * const pInfo,\r
798                                                           _taskPool_t * const pTaskPool )\r
799 {\r
800     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
801 \r
802     bool semStartStopInit = false;\r
803     bool semDispatchInit = false;\r
804 \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
807      */\r
808     IotDeQueue_Create( &pTaskPool->dispatchQueue );\r
809     IotListDouble_Create( &pTaskPool->timerEventsList );\r
810 \r
811     _initJobsCache( &pTaskPool->jobsCache );\r
812 \r
813     /* Initialize the semaphore to ensure all threads have started. */\r
814     pTaskPool->startStopSignal = xSemaphoreCreateCountingStatic( pInfo->minThreads, 0, &pTaskPool->startStopSignalBuffer );\r
815 \r
816     if( pTaskPool->startStopSignal != NULL )\r
817     {\r
818         semStartStopInit = true;\r
819 \r
820         /* Initialize the semaphore for waiting for incoming work. */\r
821         pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );\r
822 \r
823         if( pTaskPool->dispatchSignal != NULL )\r
824         {\r
825             semDispatchInit = true;\r
826         }\r
827         else\r
828         {\r
829             TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
830         }\r
831     }\r
832     else\r
833     {\r
834         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
835     }\r
836 \r
837     TASKPOOL_FUNCTION_CLEANUP();\r
838 \r
839     if( TASKPOOL_FAILED( status ) )\r
840     {\r
841         if( semStartStopInit )\r
842         {\r
843             vSemaphoreDelete( &pTaskPool->startStopSignal );\r
844         }\r
845 \r
846         if( semDispatchInit )\r
847         {\r
848             vSemaphoreDelete( &pTaskPool->dispatchSignal );\r
849         }\r
850     }\r
851 \r
852     TASKPOOL_FUNCTION_CLEANUP_END();\r
853 }\r
854 \r
855 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,\r
856                                            _taskPool_t * const pTaskPool )\r
857 {\r
858     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
859 \r
860     uint32_t count;\r
861     uint32_t threadsCreated;\r
862 \r
863     /* Check input values for consistency. */\r
864     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
865 \r
866     /* Zero out all data structures. */\r
867     memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );\r
868 \r
869     /* Initialize all internal data structure prior to creating all threads. */\r
870     TASKPOOL_ON_ERROR_GOTO_CLEANUP( _initTaskPoolControlStructures( pInfo, pTaskPool ) );\r
871 \r
872     /* Create the timer for a new connection. */\r
873     pTaskPool->timer = xTimerCreate( NULL, portMAX_DELAY, pdFALSE, ( void * ) pTaskPool, _timerCallback );\r
874 \r
875     if( pTaskPool->timer == NULL )\r
876     {\r
877         IotLogError( "Failed to create timer for task pool." );\r
878 \r
879         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
880     }\r
881 \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
886 \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
889     {\r
890         TaskHandle_t task = NULL;\r
891 \r
892         BaseType_t res = xTaskCreate( _taskPoolWorker,\r
893                                       NULL,\r
894                                       pInfo->stackSize,\r
895                                       pTaskPool,\r
896                                       pInfo->priority,\r
897                                       &task );\r
898 \r
899         /* Create one thread. */\r
900         if( res == pdFALSE )\r
901         {\r
902             IotLogError( "Could not create worker thread! Exiting..." );\r
903 \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
906         }\r
907 \r
908         /* Upon successful thread creation, increase the number of active threads. */\r
909         pTaskPool->activeThreads++;\r
910         IotTaskPool_Assert( task != NULL );\r
911 \r
912         ++threadsCreated;\r
913     }\r
914 \r
915     TASKPOOL_FUNCTION_CLEANUP();\r
916 \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
919     {\r
920         xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY ); /*_RB_ Is waiting necessary, and if so, is a semaphore necessary? */\r
921     }\r
922 \r
923     /* In case of failure, wait on the created threads to exit. */\r
924     if( TASKPOOL_FAILED( status ) )\r
925     {\r
926         /* Set the exit condition for the newly created threads. */\r
927         _signalShutdown( pTaskPool, threadsCreated );\r
928 \r
929         /* Signal all threads to exit. */\r
930         for( count = 0; count < threadsCreated; ++count )\r
931         {\r
932             xSemaphoreTake( pTaskPool->startStopSignal, portMAX_DELAY );\r
933         }\r
934 \r
935         _destroyTaskPool( pTaskPool );\r
936     }\r
937 \r
938     pTaskPool->running = true;\r
939 \r
940     TASKPOOL_FUNCTION_CLEANUP_END();\r
941 }\r
942 \r
943 /*-----------------------------------------------------------*/\r
944 \r
945 static void _destroyTaskPool( _taskPool_t * const pTaskPool )\r
946 {\r
947     if( pTaskPool->timer != NULL )\r
948     {\r
949         xTimerDelete( pTaskPool->timer, 0 );\r
950     }\r
951 \r
952     if( pTaskPool->dispatchSignal != NULL )\r
953     {\r
954         vSemaphoreDelete( pTaskPool->dispatchSignal );\r
955     }\r
956 \r
957     if( pTaskPool->startStopSignal != NULL )\r
958     {\r
959         vSemaphoreDelete( pTaskPool->startStopSignal );\r
960     }\r
961 }\r
962 \r
963 /* ---------------------------------------------------------------------------------------------- */\r
964 \r
965 static void _taskPoolWorker( void * pUserContext )\r
966 {\r
967     IotTaskPool_Assert( pUserContext != NULL );\r
968 \r
969     IotTaskPoolRoutine_t userCallback = NULL;\r
970     bool running = true;\r
971 \r
972     /* Extract pTaskPool pointer from context. */\r
973     _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;\r
974 \r
975     /* Signal that this worker completed initialization and it is ready to receive notifications. */\r
976     ( void ) xSemaphoreGive( pTaskPool->startStopSignal );\r
977 \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
981      */\r
982     do\r
983     {\r
984         IotLink_t * pFirst = NULL;\r
985         _taskPoolJob_t * pJob = NULL;\r
986 \r
987         /* Wait on incoming notifications... */\r
988         xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY );\r
989 \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
992          */\r
993         taskENTER_CRITICAL();\r
994         {\r
995             /* If the exit condition is verified, update the number of active threads and exit the loop. */\r
996             if( _IsShutdownStarted( pTaskPool ) )\r
997             {\r
998                 IotLogDebug( "Worker thread exiting because exit condition was set." );\r
999 \r
1000                 /* Decrease the number of active threads. */\r
1001                 pTaskPool->activeThreads--;\r
1002 \r
1003                 taskEXIT_CRITICAL();\r
1004 \r
1005                 /* Signal that this worker is exiting. */\r
1006                 xSemaphoreGive( pTaskPool->startStopSignal );\r
1007 \r
1008                 /* On shutdown, abandon the OUTER LOOP immediately. */\r
1009                 break;\r
1010             }\r
1011 \r
1012             /* Dequeue the first job in FIFO order. */\r
1013             pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
1014 \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
1017             {\r
1018                 /* Extract the job from its link. */\r
1019                 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );\r
1020 \r
1021                 /* Update status to 'executing'. */\r
1022                 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;\r
1023                 userCallback = pJob->userCallback;\r
1024             }\r
1025         }\r
1026         taskEXIT_CRITICAL();\r
1027 \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
1030         {\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
1033              */\r
1034             {\r
1035                 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
1036                 IotTaskPool_Assert( userCallback != NULL );\r
1037 \r
1038                 userCallback( pTaskPool, pJob, pJob->pUserContext );\r
1039 \r
1040                 /* This job is finished, clear its pointer. */\r
1041                 pJob = NULL;\r
1042                 userCallback = NULL;\r
1043 \r
1044                 /* If this thread exceeded the quota, then let it terminate. */\r
1045                 if( running == false )\r
1046                 {\r
1047                     /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */\r
1048                     break;\r
1049                 }\r
1050             }\r
1051 \r
1052             /* Acquire the lock before updating the job status. */\r
1053             taskENTER_CRITICAL();\r
1054             {\r
1055                 /* Try and dequeue the next job in the dispatch queue. */\r
1056                 IotLink_t * pItem = NULL;\r
1057 \r
1058                 /* Dequeue the next job from the dispatch queue. */\r
1059                 pItem = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
1060 \r
1061                 /* If there is no job left in the dispatch queue, update the worker status and leave. */\r
1062                 if( pItem == NULL )\r
1063                 {\r
1064                     taskEXIT_CRITICAL();\r
1065 \r
1066                     /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */\r
1067                     break;\r
1068                 }\r
1069                 else\r
1070                 {\r
1071                     pJob = IotLink_Container( _taskPoolJob_t, pItem, link );\r
1072 \r
1073                     userCallback = pJob->userCallback;\r
1074                 }\r
1075 \r
1076                 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;\r
1077             }\r
1078             taskEXIT_CRITICAL();\r
1079         }\r
1080     } while( running == true );\r
1081 \r
1082     vTaskDelete( NULL );\r
1083 }\r
1084 \r
1085 /* ---------------------------------------------------------------------------------------------- */\r
1086 \r
1087 static void _initJobsCache( _taskPoolCache_t * const pCache )\r
1088 {\r
1089     IotDeQueue_Create( &pCache->freeList );\r
1090 \r
1091     pCache->freeCount = 0;\r
1092 }\r
1093 \r
1094 /*-----------------------------------------------------------*/\r
1095 \r
1096 static void _initializeJob( _taskPoolJob_t * const pJob,\r
1097                             IotTaskPoolRoutine_t userCallback,\r
1098                             void * pUserContext,\r
1099                             bool isStatic )\r
1100 {\r
1101     pJob->link.pNext = NULL;\r
1102     pJob->link.pPrevious = NULL;\r
1103     pJob->userCallback = userCallback;\r
1104     pJob->pUserContext = pUserContext;\r
1105 \r
1106     if( isStatic )\r
1107     {\r
1108         pJob->flags = IOT_TASK_POOL_INTERNAL_STATIC;\r
1109         pJob->status = IOT_TASKPOOL_STATUS_READY;\r
1110     }\r
1111     else\r
1112     {\r
1113         pJob->status = IOT_TASKPOOL_STATUS_READY;\r
1114     }\r
1115 }\r
1116 \r
1117 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache )\r
1118 {\r
1119     _taskPoolJob_t * pJob = NULL;\r
1120     IotLink_t * pLink = IotListDouble_RemoveHead( &( pCache->freeList ) );\r
1121 \r
1122     if( pLink != NULL )\r
1123     {\r
1124         pJob = IotLink_Container( _taskPoolJob_t, pLink, link );\r
1125     }\r
1126 \r
1127     /* If there is no available job in the cache, then allocate one. */\r
1128     if( pJob == NULL )\r
1129     {\r
1130         pJob = ( _taskPoolJob_t * ) IotTaskPool_MallocJob( sizeof( _taskPoolJob_t ) );\r
1131 \r
1132         if( pJob != NULL )\r
1133         {\r
1134             memset( pJob, 0x00, sizeof( _taskPoolJob_t ) );\r
1135         }\r
1136         else\r
1137         {\r
1138             /* Log alocation failure for troubleshooting purposes. */\r
1139             IotLogInfo( "Failed to allocate job." );\r
1140         }\r
1141     }\r
1142     /* If there was a job in the cache, then make sure we keep the counters up-to-date. */\r
1143     else\r
1144     {\r
1145         IotTaskPool_Assert( pCache->freeCount > 0 );\r
1146 \r
1147         pCache->freeCount--;\r
1148     }\r
1149 \r
1150     return pJob;\r
1151 }\r
1152 \r
1153 /*-----------------------------------------------------------*/\r
1154 \r
1155 static void _recycleJob( _taskPoolCache_t * const pCache,\r
1156                          _taskPoolJob_t * const pJob )\r
1157 {\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
1160 \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
1163     {\r
1164         /* Destroy user data, for added safety&security. */\r
1165         pJob->userCallback = NULL;\r
1166         pJob->pUserContext = NULL;\r
1167 \r
1168         /* Reset the status for added debuggability. */\r
1169         pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;\r
1170 \r
1171         IotListDouble_InsertTail( &pCache->freeList, &pJob->link );\r
1172 \r
1173         pCache->freeCount++;\r
1174 \r
1175         IotTaskPool_Assert( pCache->freeCount >= 1 );\r
1176     }\r
1177     else\r
1178     {\r
1179         _destroyJob( pJob );\r
1180     }\r
1181 }\r
1182 \r
1183 /*-----------------------------------------------------------*/\r
1184 \r
1185 static void _destroyJob( _taskPoolJob_t * const pJob )\r
1186 {\r
1187     /* Destroy user data, for added safety & security. */\r
1188     pJob->userCallback = NULL;\r
1189     pJob->pUserContext = NULL;\r
1190 \r
1191     /* Reset the status for added debuggability. */\r
1192     pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;\r
1193 \r
1194     /* Only dispose of dynamically allocated jobs. */\r
1195     if( ( pJob->flags & IOT_TASK_POOL_INTERNAL_STATIC ) == 0UL )\r
1196     {\r
1197         IotTaskPool_FreeJob( pJob );\r
1198     }\r
1199 }\r
1200 \r
1201 /* ---------------------------------------------------------------------------------------------- */\r
1202 \r
1203 static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool )\r
1204 {\r
1205     return( pTaskPool->running == false );\r
1206 }\r
1207 \r
1208 /*-----------------------------------------------------------*/\r
1209 \r
1210 static void _signalShutdown( _taskPool_t * const pTaskPool,\r
1211                              uint32_t threads )\r
1212 {\r
1213     uint32_t count;\r
1214 \r
1215     /* Set the exit condition. */\r
1216     pTaskPool->running = false;\r
1217 \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
1220     {\r
1221         ( void ) xSemaphoreGive( pTaskPool->dispatchSignal );\r
1222     }\r
1223 }\r
1224 \r
1225 /* ---------------------------------------------------------------------------------------------- */\r
1226 \r
1227 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,\r
1228                                              _taskPoolJob_t * const pJob )\r
1229 {\r
1230     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
1231 \r
1232     /* Update the job status to 'scheduled'. */\r
1233     pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;\r
1234 \r
1235     BaseType_t higherPriorityTaskWoken;\r
1236 \r
1237     /* Append the job to the dispatch queue. */\r
1238     IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link );\r
1239 \r
1240     /* Signal a worker to pick up the job. */\r
1241     ( void ) xSemaphoreGiveFromISR( pTaskPool->dispatchSignal, &higherPriorityTaskWoken );\r
1242 \r
1243     portYIELD_FROM_ISR( higherPriorityTaskWoken );\r
1244 \r
1245     TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL();\r
1246 }\r
1247 \r
1248 /*-----------------------------------------------------------*/\r
1249 \r
1250 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
1251                                 void * pMatch )\r
1252 {\r
1253     const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;\r
1254 \r
1255     const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
1256 \r
1257     if( pJob == pTimerEvent->job )\r
1258     {\r
1259         return true;\r
1260     }\r
1261 \r
1262     return false;\r
1263 }\r
1264 \r
1265 /*-----------------------------------------------------------*/\r
1266 \r
1267 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,\r
1268                                               _taskPoolJob_t * const pJob,\r
1269                                               IotTaskPoolJobStatus_t * const pStatus )\r
1270 {\r
1271     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
1272 \r
1273     bool cancelable = false;\r
1274 \r
1275     /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */\r
1276 \r
1277     IotTaskPoolJobStatus_t currentStatus = pJob->status;\r
1278 \r
1279     switch( currentStatus )\r
1280     {\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
1286             break;\r
1287 \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
1291             break;\r
1292 \r
1293         default:\r
1294             /* Log mesggesong purposes. */\r
1295             IotLogError( "Attempt to cancel a job with an undefined state." );\r
1296             break;\r
1297     }\r
1298 \r
1299     /* Update the returned status to the current status of the job. */\r
1300     if( pStatus != NULL )\r
1301     {\r
1302         *pStatus = currentStatus;\r
1303     }\r
1304 \r
1305     if( cancelable == false )\r
1306     {\r
1307         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_CANCEL_FAILED );\r
1308     }\r
1309     else\r
1310     {\r
1311         /* Update the status of the job. */\r
1312         pJob->status = IOT_TASKPOOL_STATUS_CANCELED;\r
1313 \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
1317         {\r
1318             /* A scheduled work items must be in the dispatch queue. */\r
1319             IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) );\r
1320 \r
1321             IotDeQueue_Remove( &pJob->link );\r
1322         }\r
1323 \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
1327         {\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
1331 \r
1332             if( pTimerEventLink != NULL )\r
1333             {\r
1334                 bool shouldReschedule = false;\r
1335 \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
1339 \r
1340                 if( pHeadLink == pTimerEventLink )\r
1341                 {\r
1342                     shouldReschedule = true;\r
1343                 }\r
1344 \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
1348 \r
1349                 if( shouldReschedule )\r
1350                 {\r
1351                     IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
1352 \r
1353                     if( pNextTimerEventLink != NULL )\r
1354                     {\r
1355                         _rescheduleDeferredJobsTimer( pTaskPool->timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );\r
1356                     }\r
1357                 }\r
1358             }\r
1359         }\r
1360         else\r
1361         {\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
1364         }\r
1365     }\r
1366 \r
1367     TASKPOOL_NO_FUNCTION_CLEANUP();\r
1368 }\r
1369 \r
1370 /*-----------------------------------------------------------*/\r
1371 \r
1372 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
1373                                    const IotLink_t * const pTimerEventLink2 )\r
1374 {\r
1375     const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,\r
1376                                                                           pTimerEventLink1,\r
1377                                                                           link );\r
1378     const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,\r
1379                                                                           pTimerEventLink2,\r
1380                                                                           link );\r
1381 \r
1382     if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )\r
1383     {\r
1384         return -1;\r
1385     }\r
1386 \r
1387     if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )\r
1388     {\r
1389         return 1;\r
1390     }\r
1391 \r
1392     return 0;\r
1393 }\r
1394 \r
1395 /*-----------------------------------------------------------*/\r
1396 \r
1397 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
1398                                           _taskPoolTimerEvent_t * const pFirstTimerEvent )\r
1399 {\r
1400     uint64_t delta = 0;\r
1401     TickType_t now = xTaskGetTickCount();\r
1402 \r
1403     if( pFirstTimerEvent->expirationTime > now )\r
1404     {\r
1405         delta = pFirstTimerEvent->expirationTime - now;\r
1406     }\r
1407 \r
1408     if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )\r
1409     {\r
1410         delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */\r
1411     }\r
1412 \r
1413     IotTaskPool_Assert( delta > 0 );\r
1414 \r
1415     if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )\r
1416     {\r
1417         IotLogWarn( "Failed to re-arm timer for task pool" );\r
1418     }\r
1419 }\r
1420 \r
1421 /*-----------------------------------------------------------*/\r
1422 \r
1423 static void _timerCallback( TimerHandle_t xTimer )\r
1424 {\r
1425     _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );\r
1426 \r
1427     IotTaskPool_Assert( pTaskPool );\r
1428 \r
1429     _taskPoolTimerEvent_t * pTimerEvent = NULL;\r
1430 \r
1431     IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );\r
1432 \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
1436      */\r
1437     taskENTER_CRITICAL();\r
1438     {\r
1439         /* Check again for shutdown and bail out early in case. */\r
1440         if( _IsShutdownStarted( pTaskPool ) )\r
1441         {\r
1442             taskEXIT_CRITICAL();\r
1443 \r
1444             /* Complete the shutdown sequence. */\r
1445             _destroyTaskPool( pTaskPool );\r
1446 \r
1447             return;\r
1448         }\r
1449 \r
1450         /* Dispatch all deferred job whose timer expired, then reset the timer for the next\r
1451          * job down the line. */\r
1452         for( ; ; )\r
1453         {\r
1454             /* Peek the first event in the timer event list. */\r
1455             IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
1456 \r
1457             /* Check if the timer misfired for any reason.  */\r
1458             if( pLink != NULL )\r
1459             {\r
1460                 /* Record the current time. */\r
1461                 TickType_t now = xTaskGetTickCount();\r
1462 \r
1463                 /* Extract the job from its envelope. */\r
1464                 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
1465 \r
1466                 /* Check if the first event should be processed now. */\r
1467                 if( pTimerEvent->expirationTime <= now )\r
1468                 {\r
1469                     /*  Remove the timer event for immediate processing. */\r
1470                     IotListDouble_Remove( &( pTimerEvent->link ) );\r
1471                 }\r
1472                 else\r
1473                 {\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
1477 \r
1478                     break;\r
1479                 }\r
1480             }\r
1481             /* If there are no timer events to process, terminate this thread. */\r
1482             else\r
1483             {\r
1484                 IotLogDebug( "No further timer events to process. Exiting timer thread." );\r
1485 \r
1486                 break;\r
1487             }\r
1488 \r
1489             IotLogDebug( "Scheduling job from timer event." );\r
1490 \r
1491             /* Queue the job associated with the received timer event. */\r
1492             ( void ) _scheduleInternal( pTaskPool, pTimerEvent->job );\r
1493 \r
1494             /* Free the timer event. */\r
1495             IotTaskPool_FreeTimerEvent( pTimerEvent );\r
1496         }\r
1497     }\r
1498     taskEXIT_CRITICAL();\r
1499 }\r