]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-IoT-SDK/c_sdk/standard/common/taskpool/iot_taskpool.c
f0826281ad6a3fd1b9932b0eebe9fa98956d0cce
[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 /* Kernel includes. */\r
31 #include "FreeRTOS.h"\r
32 #include "semphr.h"\r
33 \r
34 /* IoT libraries includes. */\r
35 #include "iot_config.h"\r
36 \r
37 /* Standard includes. */\r
38 #include <stdbool.h>\r
39 #include <stdio.h>\r
40 #include <stddef.h>\r
41 #include <stdint.h>\r
42 #include <string.h>\r
43 \r
44 #if !defined( configSUPPORT_STATIC_ALLOCATION ) || ( configSUPPORT_STATIC_ALLOCATION != 1 )\r
45         #error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.\r
46 #endif\r
47 \r
48 /* Task pool internal include. */\r
49 #include "private/iot_taskpool_internal.h"\r
50 \r
51 /**\r
52  * @brief Maximum semaphore value for wait operations.\r
53  */\r
54 #define TASKPOOL_MAX_SEM_VALUE              0xFFFF\r
55 \r
56 /**\r
57  * @brief Reschedule delay in milliseconds for deferred jobs.\r
58  */\r
59 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS    ( 10ULL )\r
60 \r
61 /* ---------------------------------------------------------------------------------- */\r
62 \r
63 /**\r
64  * Doxygen should ignore this section.\r
65  *\r
66  * @brief The system task pool handle for all libraries to use.\r
67  * User application can use the system task pool as well knowing that the usage will be shared with\r
68  * the system libraries as well. The system task pool needs to be initialized before any library is used or\r
69  * before any code that posts jobs to the task pool runs.\r
70  */\r
71 static _taskPool_t _IotSystemTaskPool = { .dispatchQueue = IOT_DEQUEUE_INITIALIZER };\r
72 \r
73 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */\r
74 \r
75 /**\r
76  * @brief Initializes one instance of a Task pool cache.\r
77  *\r
78  * @param[in] pCache The pre-allocated instance of the cache to initialize.\r
79  */\r
80 static void _initJobsCache( _taskPoolCache_t * const pCache );\r
81 \r
82 /**\r
83  * @brief Initialize a job.\r
84  *\r
85  * @param[in] pJob The job to initialize.\r
86  * @param[in] userCallback The user callback for the job.\r
87  * @param[in] pUserContext The context tp be passed to the callback.\r
88  * @param[in] isStatic A flag to indicate whether the job is statically or synamically allocated.\r
89  */\r
90 static void _initializeJob( _taskPoolJob_t * const pJob,\r
91                             IotTaskPoolRoutine_t userCallback,\r
92                             void * pUserContext,\r
93                             bool isStatic );\r
94 \r
95 /**\r
96  * @brief Extracts and initializes one instance of a job from the cache or, if there is none available, it allocates and initialized a new one.\r
97  *\r
98  * @param[in] pCache The instance of the cache to extract the job from.\r
99  */\r
100 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache );\r
101 \r
102 /**\r
103  * Recycles one instance of a job into the cache or, if the cache is full, it destroys it.\r
104  *\r
105  * @param[in] pCache The instance of the cache to recycle the job into.\r
106  * @param[in] pJob The job to recycle.\r
107  *\r
108  */\r
109 static void _recycleJob( _taskPoolCache_t * const pCache,\r
110                          _taskPoolJob_t * const pJob );\r
111 \r
112 /**\r
113  * Destroys one instance of a job.\r
114  *\r
115  * @param[in] pJob The job to destroy.\r
116  *\r
117  */\r
118 static void _destroyJob( _taskPoolJob_t * const pJob );\r
119 \r
120 /* -------------- The worker thread procedure for a task pool thread -------------- */\r
121 \r
122 /**\r
123  * The procedure for a task pool worker thread.\r
124  *\r
125  * @param[in] pUserContext The user context.\r
126  *\r
127  */\r
128 static void _taskPoolWorker( void * pUserContext );\r
129 \r
130 /* -------------- Convenience functions to handle timer events  -------------- */\r
131 \r
132 /**\r
133  * Comparer for the time list.\r
134  *\r
135  * param[in] pTimerEventLink1 The link to the first timer event.\r
136  * param[in] pTimerEventLink1 The link to the first timer event.\r
137  */\r
138 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
139                                    const IotLink_t * const pTimerEventLink2 );\r
140 \r
141 /**\r
142  * Reschedules the timer for handling deferred jobs to the next timeout.\r
143  *\r
144  * param[in] timer The timer to reschedule.\r
145  * param[in] pFirstTimerEvent The timer event that carries the timeout and job inforamtion.\r
146  */\r
147 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
148                                           _taskPoolTimerEvent_t * const pFirstTimerEvent );\r
149 \r
150 /**\r
151  * The task pool timer procedure for scheduling deferred jobs.\r
152  *\r
153  * param[in] timer The timer to handle.\r
154  */\r
155 static void _timerCallback( TimerHandle_t xTimer );\r
156 \r
157 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */\r
158 \r
159 /**\r
160  * Parameter validation for a task pool initialization.\r
161  *\r
162  * @param[in] pInfo The initialization information for the task pool.\r
163  *\r
164  */\r
165 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo );\r
166 \r
167 /**\r
168  * Initializes a pre-allocated instance of a task pool.\r
169  *\r
170  * @param[in] pTaskPool The pre-allocated instance of the task pool to initialize.\r
171  *\r
172  */\r
173 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool );\r
174 \r
175 /**\r
176  * Initializes a pre-allocated instance of a task pool.\r
177  *\r
178  * @param[in] pInfo The initialization information for the task pool.\r
179  * @param[out] pTaskPool A pointer to the task pool data structure to initialize.\r
180  *\r
181  */\r
182 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,\r
183                                            _taskPool_t * const pTaskPool );\r
184 \r
185 /**\r
186  * Destroys one instance of a task pool.\r
187  *\r
188  * @param[in] pTaskPool The task pool to destroy.\r
189  *\r
190  */\r
191 static void _destroyTaskPool( _taskPool_t * const pTaskPool );\r
192 \r
193 /**\r
194  * Set the exit condition.\r
195  *\r
196  * @param[in] pTaskPool The task pool to destroy.\r
197  * @param[in] threads The number of threads active in the task pool at shutdown time.\r
198  *\r
199  */\r
200 static void _signalShutdown( _taskPool_t * const pTaskPool,\r
201                              uint32_t threads );\r
202 \r
203 /**\r
204  * Places a job in the dispatch queue.\r
205  *\r
206  * @param[in] pTaskPool The task pool to scheduel the job with.\r
207  * @param[in] pJob The job to schedule.\r
208  * @param[in] flags The job flags.\r
209  *\r
210  */\r
211 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,\r
212                                              _taskPoolJob_t * const pJob );\r
213 /**\r
214  * Matches a deferred job in the timer queue with its timer event wrapper.\r
215  *\r
216  * @param[in] pLink A pointer to the timer event link in the timer queue.\r
217  * @param[in] pMatch A pointer to the job to match.\r
218  *\r
219  */\r
220 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
221                                 void * pMatch );\r
222 \r
223 /**\r
224  * Tries to cancel a job.\r
225  *\r
226  * @param[in] pTaskPool The task pool to cancel an operation against.\r
227  * @param[in] pJob The job to cancel.\r
228  * @param[out] pStatus The status of the job at the time of cancellation.\r
229  *\r
230  */\r
231 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,\r
232                                               _taskPoolJob_t * const pJob,\r
233                                               IotTaskPoolJobStatus_t * const pStatus );\r
234 \r
235 /* ---------------------------------------------------------------------------------------------- */\r
236 \r
237 /*\r
238  * The full IoT Task Pool Library has many use cases, including Linux\r
239  * development. Â Typical FreeRTOS use cases do not require the full\r
240  * functionality so optimised version is provided specifically for use with\r
241  * FreeRTOS.  Unlike the full version, this optimised version:\r
242  *   + Only supports a single task pool (system task pool) at a time.\r
243  *   + Does not auto-scale by dynamically adding more tasks if the number of\r
244  *   + tasks in the pool becomes exhausted. Â The number of tasks in the pool\r
245  *     are fixed at compile time.  See the task pool configuration options for\r
246  *     more information.\r
247  *   + Cannot be shut down - it exists for the lifetime of the application.\r
248  *\r
249  * As such this file does not implement the following API functions:\r
250  *   + IotTaskPool_GetSystemTaskPool()\r
251  *   + IotTaskPool_Create()\r
252  *   + IotTaskPool_Destroy()\r
253  *   + IotTaskPool_SetMaxThreads()\r
254  *\r
255  * Users who are interested in the functionality of the full IoT Task Pool\r
256  * library can us it in place of this optimised version.\r
257  */\r
258 \r
259 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )\r
260 {\r
261     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
262 \r
263     /* At this time the task pool cannot be created before the scheduler has\r
264     started because the function attempts to block on synchronization\r
265     primitives (although I'm not sure why). */\r
266     configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED );\r
267 \r
268     /* Guard against multiple attempts to create the system task pool in case\r
269     this function is called by more than one library initialization routine. */\r
270     if( _IotSystemTaskPool.running == false )\r
271     {\r
272         /* Parameter checking. */\r
273         TASKPOOL_ON_ERROR_GOTO_CLEANUP( _performTaskPoolParameterValidation( pInfo ) );\r
274 \r
275         /* Create the system task pool pool. */\r
276         TASKPOOL_SET_AND_GOTO_CLEANUP( _createTaskPool( pInfo, &_IotSystemTaskPool ) );\r
277     }\r
278 \r
279     TASKPOOL_NO_FUNCTION_CLEANUP();\r
280 }\r
281 /*-----------------------------------------------------------*/\r
282 \r
283 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,\r
284                                           void * pUserContext,\r
285                                           IotTaskPoolJobStorage_t * const pJobStorage,\r
286                                           IotTaskPoolJob_t * const ppJob )\r
287 {\r
288     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
289 \r
290     /* Parameter checking. */\r
291     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );\r
292     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobStorage );\r
293     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );\r
294 \r
295     /* Build a job around the user-provided storage. */\r
296     _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext, true );\r
297 \r
298     *ppJob = ( IotTaskPoolJob_t ) pJobStorage;\r
299 \r
300     TASKPOOL_NO_FUNCTION_CLEANUP();\r
301 }\r
302 \r
303 /*-----------------------------------------------------------*/\r
304 \r
305 IotTaskPoolError_t IotTaskPool_CreateRecyclableJob( IotTaskPool_t taskPoolHandle,\r
306                                                     IotTaskPoolRoutine_t userCallback,\r
307                                                     void * pUserContext,\r
308                                                     IotTaskPoolJob_t * const ppJob )\r
309 {\r
310     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
311 \r
312     _taskPool_t * const pTaskPool = &_IotSystemTaskPool;\r
313     _taskPoolJob_t * pTempJob = NULL;\r
314 \r
315     /* This lean version of the task pool only supports the task pool created\r
316     by this library (the system task pool).  NULL means use the system task\r
317     pool - no other values are allowed.  Use the full implementation of this\r
318     library if you want multiple task pools (there is more than one task in\r
319     each pool. */\r
320     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
321 \r
322     /* Avoid compiler warnings about unused parameters if configASSERT() is not\r
323     defined. */\r
324     ( void ) taskPoolHandle;\r
325 \r
326     /* Parameter checking. */\r
327     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( userCallback );\r
328     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( ppJob );\r
329 \r
330     taskENTER_CRITICAL();\r
331     {\r
332         /* Bail out early if this task pool is shutting down. */\r
333         pTempJob = _fetchOrAllocateJob( &pTaskPool->jobsCache );\r
334     }\r
335     taskEXIT_CRITICAL();\r
336 \r
337     if( pTempJob == NULL )\r
338     {\r
339         IotLogInfo( "Failed to allocate a job." );\r
340 \r
341         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
342     }\r
343 \r
344     _initializeJob( pTempJob, userCallback, pUserContext, false );\r
345 \r
346     *ppJob = pTempJob;\r
347 \r
348     TASKPOOL_NO_FUNCTION_CLEANUP();\r
349 }\r
350 \r
351 /*-----------------------------------------------------------*/\r
352 \r
353 IotTaskPoolError_t IotTaskPool_DestroyRecyclableJob( IotTaskPool_t taskPoolHandle,\r
354                                                      IotTaskPoolJob_t pJobHandle )\r
355 {\r
356     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
357 \r
358     _taskPoolJob_t * pJob = ( _taskPoolJob_t * ) pJobHandle;\r
359 \r
360     /* This lean version of the task pool only supports the task pool created\r
361     by this library (the system task pool).  NULL means use the system task\r
362     pool - no other values are allowed.  Use the full implementation of this\r
363     library if you want multiple task pools (there is more than one task in\r
364     each pool. */\r
365     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
366 \r
367     /* Avoid compiler warnings about unused parameters if configASSERT() is not\r
368     defined. */\r
369     ( void ) taskPoolHandle;\r
370 \r
371     /* Parameter checking. */\r
372     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJobHandle );\r
373 \r
374     IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
375 \r
376     _destroyJob( pJob );\r
377 \r
378     TASKPOOL_NO_FUNCTION_CLEANUP();\r
379 }\r
380 \r
381 /*-----------------------------------------------------------*/\r
382 \r
383 IotTaskPoolError_t IotTaskPool_RecycleJob( IotTaskPool_t taskPoolHandle,\r
384                                            IotTaskPoolJob_t pJob )\r
385 {\r
386     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
387 \r
388     _taskPool_t * pTaskPool = ( _taskPool_t * ) &_IotSystemTaskPool;\r
389 \r
390     /* This lean version of the task pool only supports the task pool created\r
391     by this library (the system task pool).  NULL means use the system task\r
392     pool - no other values are allowed.  Use the full implementation of this\r
393     library if you want multiple task pools (there is more than one task in\r
394     each pool. */\r
395     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
396 \r
397     /* Ensure unused parameters do not cause compiler warnings in case\r
398     configASSERT() is not defined. */\r
399     ( void ) taskPoolHandle;\r
400 \r
401     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );\r
402 \r
403     taskENTER_CRITICAL();\r
404     {\r
405         IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
406 \r
407         _recycleJob( &pTaskPool->jobsCache, pJob );\r
408     }\r
409     taskEXIT_CRITICAL();\r
410 \r
411     TASKPOOL_NO_FUNCTION_CLEANUP();\r
412 }\r
413 \r
414 /*-----------------------------------------------------------*/\r
415 \r
416 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,\r
417                                          IotTaskPoolJob_t pJob,\r
418                                          uint32_t flags )\r
419 {\r
420     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
421 \r
422     _taskPool_t * const pTaskPool = &_IotSystemTaskPool;\r
423 \r
424     /* Task pool must have been created. */\r
425     configASSERT( pTaskPool->running != false );\r
426 \r
427     /* This lean version of the task pool only supports the task pool created\r
428     by this library (the system task pool).  NULL means use the system task\r
429     pool - no other values are allowed.  Use the full implementation of this\r
430     library if you want multiple task pools (there is more than one task in\r
431     each pool. */\r
432     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
433 \r
434     /* Avoid compiler warnings about unused parameters if configASSERT() is not\r
435     defined. */\r
436     ( void ) taskPoolHandle;\r
437 \r
438     /* Parameter checking. */\r
439     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pJob );\r
440     TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( ( flags != 0UL ) && ( flags != IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );\r
441 \r
442     taskENTER_CRITICAL(); //_RB_ Critical section is too long - does the whole thing need to be protected?\r
443     {\r
444         _scheduleInternal( pTaskPool, pJob );\r
445     }\r
446     taskEXIT_CRITICAL();\r
447 \r
448     TASKPOOL_NO_FUNCTION_CLEANUP();\r
449 }\r
450 \r
451 /*-----------------------------------------------------------*/\r
452 \r
453 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,\r
454                                                  IotTaskPoolJob_t job,\r
455                                                  uint32_t timeMs )\r
456 {\r
457     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
458 \r
459     _taskPool_t * pTaskPool = &_IotSystemTaskPool;\r
460 \r
461     /* This lean version of the task pool only supports the task pool created\r
462     by this library (the system task pool).  NULL means use the system task\r
463     pool - no other values are allowed.  Use the full implementation of this\r
464     library if you want multiple task pools (there is more than one task in\r
465     each pool. */\r
466     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
467 \r
468     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
469 \r
470     if( timeMs == 0UL )\r
471     {\r
472         TASKPOOL_SET_AND_GOTO_CLEANUP( IotTaskPool_Schedule( pTaskPool, job, 0 ) );\r
473     }\r
474 \r
475     taskENTER_CRITICAL();\r
476     {\r
477         _taskPoolTimerEvent_t * pTimerEvent = IotTaskPool_MallocTimerEvent( sizeof( _taskPoolTimerEvent_t ) );\r
478 \r
479         if( pTimerEvent == NULL )\r
480         {\r
481             taskEXIT_CRITICAL();\r
482 \r
483             TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
484         }\r
485 \r
486         IotLink_t * pTimerEventLink;\r
487 \r
488         TickType_t now = xTaskGetTickCount();\r
489 \r
490         pTimerEvent->link.pNext = NULL;\r
491         pTimerEvent->link.pPrevious = NULL;\r
492         pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );\r
493         pTimerEvent->job = job; //_RB_ Think up to here can be outside the critical section.\r
494 \r
495         /* Append the timer event to the timer list. */\r
496         IotListDouble_InsertSorted( &pTaskPool->timerEventsList, &pTimerEvent->link, _timerEventCompare );\r
497 \r
498         /* Update the job status to 'scheduled'. */\r
499         job->status = IOT_TASKPOOL_STATUS_DEFERRED;\r
500 \r
501         /* Peek the first event in the timer event list. There must be at least one,\r
502          * since we just inserted it. */\r
503         pTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
504         IotTaskPool_Assert( pTimerEventLink != NULL );\r
505 \r
506         /* If the event we inserted is at the front of the queue, then\r
507          * we need to reschedule the underlying timer. */\r
508         if( pTimerEventLink == &pTimerEvent->link )\r
509         {\r
510             pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );\r
511 \r
512             _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );\r
513         }\r
514     }\r
515     taskEXIT_CRITICAL();\r
516 \r
517     TASKPOOL_NO_FUNCTION_CLEANUP();\r
518 }\r
519 \r
520 /*-----------------------------------------------------------*/\r
521 \r
522 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,\r
523                                           IotTaskPoolJob_t job,\r
524                                           IotTaskPoolJobStatus_t * const pStatus )\r
525 {\r
526     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
527 \r
528     /* This lean version of the task pool only supports the task pool created\r
529     by this library (the system task pool).  NULL means use the system task\r
530     pool - no other values are allowed.  Use the full implementation of this\r
531     library if you want multiple task pools (there is more than one task in\r
532     each pool. */\r
533     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
534     ( void ) taskPoolHandle;\r
535 \r
536     /* Parameter checking. */\r
537     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
538     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pStatus );\r
539     *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;\r
540 \r
541     taskENTER_CRITICAL();\r
542     {\r
543         *pStatus = job->status;\r
544     }\r
545     taskEXIT_CRITICAL();\r
546 \r
547     TASKPOOL_NO_FUNCTION_CLEANUP();\r
548 }\r
549 \r
550 /*-----------------------------------------------------------*/\r
551 \r
552 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,\r
553                                           IotTaskPoolJob_t job,\r
554                                           IotTaskPoolJobStatus_t * const pStatus )\r
555 {\r
556     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
557 \r
558     _taskPool_t * const pTaskPool = &_IotSystemTaskPool;\r
559 \r
560     /* This lean version of the task pool only supports the task pool created\r
561     by this library (the system task pool).  NULL means use the system task\r
562     pool - no other values are allowed.  Use the full implementation of this\r
563     library if you want multiple task pools (there is more than one task in\r
564     each pool. */\r
565     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
566     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( job );\r
567 \r
568     if( pStatus != NULL )\r
569     {\r
570         *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;\r
571     }\r
572 \r
573     taskENTER_CRITICAL();\r
574     {\r
575         status = _tryCancelInternal( pTaskPool, job, pStatus );\r
576     }\r
577     taskEXIT_CRITICAL();\r
578 \r
579     TASKPOOL_NO_FUNCTION_CLEANUP();\r
580 }\r
581 \r
582 /*-----------------------------------------------------------*/\r
583 \r
584 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )\r
585 {\r
586     return ( IotTaskPoolJobStorage_t * ) pJob;\r
587 }\r
588 \r
589 /*-----------------------------------------------------------*/\r
590 \r
591 const char * IotTaskPool_strerror( IotTaskPoolError_t status )\r
592 {\r
593     const char * pMessage = NULL;\r
594 \r
595     switch( status )\r
596     {\r
597         case IOT_TASKPOOL_SUCCESS:\r
598             pMessage = "SUCCESS";\r
599             break;\r
600 \r
601         case IOT_TASKPOOL_BAD_PARAMETER:\r
602             pMessage = "BAD PARAMETER";\r
603             break;\r
604 \r
605         case IOT_TASKPOOL_ILLEGAL_OPERATION:\r
606             pMessage = "ILLEGAL OPERATION";\r
607             break;\r
608 \r
609         case IOT_TASKPOOL_NO_MEMORY:\r
610             pMessage = "NO MEMORY";\r
611             break;\r
612 \r
613         case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:\r
614             pMessage = "SHUTDOWN IN PROGRESS";\r
615             break;\r
616 \r
617         case IOT_TASKPOOL_CANCEL_FAILED:\r
618             pMessage = "CANCEL FAILED";\r
619             break;\r
620 \r
621         default:\r
622             pMessage = "INVALID STATUS";\r
623             break;\r
624     }\r
625 \r
626     return pMessage;\r
627 }\r
628 \r
629 /* ---------------------------------------------------------------------------------------------- */\r
630 /* ---------------------------------------------------------------------------------------------- */\r
631 /* ---------------------------------------------------------------------------------------------- */\r
632 \r
633 static IotTaskPoolError_t _performTaskPoolParameterValidation( const IotTaskPoolInfo_t * const pInfo )\r
634 {\r
635     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
636 \r
637     /* Check input values for consistency. */\r
638     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pInfo );\r
639     TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads > pInfo->maxThreads );\r
640     TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->minThreads < 1UL );\r
641     TASKPOOL_ON_ARG_ERROR_GOTO_CLEANUP( pInfo->maxThreads < 1UL );\r
642 \r
643     TASKPOOL_NO_FUNCTION_CLEANUP();\r
644 }\r
645 \r
646 static void _initTaskPoolControlStructures( _taskPool_t * const pTaskPool )\r
647 {\r
648     /* Initialize a job data structures that require no de-initialization.\r
649      * All other data structures carry a value of 'NULL' before initailization.\r
650      */\r
651     IotDeQueue_Create( &pTaskPool->dispatchQueue );\r
652     IotListDouble_Create( &pTaskPool->timerEventsList );\r
653 \r
654     _initJobsCache( &pTaskPool->jobsCache );\r
655 \r
656     /* Initialize the semaphore for waiting for incoming work.  Cannot fail as\r
657     statically allocated. */\r
658     pTaskPool->dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &pTaskPool->dispatchSignalBuffer );\r
659 }\r
660 \r
661 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo,\r
662                                            _taskPool_t * const pTaskPool )\r
663 {\r
664     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
665 \r
666     uint32_t threadsCreated = 0; /* Although initialised before use removing the initialiser here results in compiler warnings. */\r
667     char cTaskName[ 10 ];\r
668 \r
669     /* Check input values for consistency. */\r
670     TASKPOOL_ON_NULL_ARG_GOTO_CLEANUP( pTaskPool );\r
671 \r
672     /* Zero out all data structures. */\r
673     memset( ( void * ) pTaskPool, 0x00, sizeof( _taskPool_t ) );\r
674 \r
675     /* Initialize all internal data structure prior to creating all threads. */\r
676     _initTaskPoolControlStructures( pTaskPool );\r
677 \r
678     /* Create the timer for a new connection. */\r
679     pTaskPool->timer = xTimerCreate( NULL, portMAX_DELAY, pdFALSE, ( void * ) pTaskPool, _timerCallback );\r
680 \r
681     if( pTaskPool->timer == NULL )\r
682     {\r
683         IotLogError( "Failed to create timer for task pool." );\r
684 \r
685         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
686     }\r
687 \r
688     /* The task pool will initialize the minimum number of threads requested by the user upon start.\r
689     Note this tailored version of the task pool does not autoscale, but fixes the number of tasks\r
690     in the pool to the originally specified minimum, and the specified maximum value is ignored. */\r
691     /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */\r
692     for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )\r
693     {\r
694         TaskHandle_t task = NULL;\r
695 \r
696         /* Generate a unique name for the task. */\r
697         snprintf( cTaskName, sizeof( cTaskName ), "pool%d", ( int ) threadsCreated );\r
698 \r
699         BaseType_t res = xTaskCreate( _taskPoolWorker,\r
700                                       cTaskName,\r
701                                       pInfo->stackSize / sizeof( portSTACK_TYPE ), /* xTaskCreate() expects the stack size to be specified in words. */\r
702                                       pTaskPool,\r
703                                       pInfo->priority,\r
704                                       &task );\r
705 \r
706         /* Create one thread. */\r
707         if( res == pdFALSE ) //_RB_ would not be needed if tasks are created statically.\r
708         {\r
709             IotLogError( "Could not create worker thread! Exiting..." );\r
710 \r
711             /* If creating one thread fails, set error condition and exit the loop. */\r
712             TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_NO_MEMORY );\r
713         }\r
714 \r
715         /* Upon successful thread creation, increase the number of active threads. */\r
716         pTaskPool->activeThreads++;\r
717         IotTaskPool_Assert( task != NULL );\r
718 \r
719         ++threadsCreated;\r
720     }\r
721 \r
722     TASKPOOL_FUNCTION_CLEANUP();\r
723 \r
724     /* In case of failure, wait on the created threads to exit. */\r
725     if( TASKPOOL_FAILED( status ) )\r
726     {\r
727         /* Set the exit condition for the newly created threads. */\r
728         _signalShutdown( pTaskPool, threadsCreated );\r
729         _destroyTaskPool( pTaskPool );\r
730     }\r
731 \r
732     pTaskPool->running = true;\r
733 \r
734     TASKPOOL_FUNCTION_CLEANUP_END();\r
735 }\r
736 \r
737 /*-----------------------------------------------------------*/\r
738 \r
739 static void _destroyTaskPool( _taskPool_t * const pTaskPool )\r
740 {\r
741     if( pTaskPool->timer != NULL )\r
742     {\r
743         xTimerDelete( pTaskPool->timer, 0 );\r
744     }\r
745 \r
746     if( pTaskPool->dispatchSignal != NULL )\r
747     {\r
748         vSemaphoreDelete( pTaskPool->dispatchSignal );\r
749     }\r
750 }\r
751 \r
752 /* ---------------------------------------------------------------------------------------------- */\r
753 \r
754 static void _taskPoolWorker( void * pUserContext )\r
755 {\r
756     IotTaskPool_Assert( pUserContext != NULL );\r
757 \r
758     IotTaskPoolRoutine_t userCallback = NULL;\r
759     bool running = true;\r
760 \r
761     /* Extract pTaskPool pointer from context. */\r
762     _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;\r
763 \r
764     /* OUTER LOOP: it controls the lifetiem of the worker thread: exit condition for a worker thread\r
765      * is setting maxThreads to zero. A worker thread is running until the maximum number of allowed\r
766      * threads is not zero and the active threads are less than the maximum number of allowed threads.\r
767      */\r
768     do\r
769     {\r
770         IotLink_t * pFirst = NULL;\r
771         _taskPoolJob_t * pJob = NULL;\r
772 \r
773         /* Wait on incoming notifications... */\r
774         xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY );\r
775 \r
776         /* Acquire the lock to check the exit condition, and release the lock if the exit condition is verified,\r
777          * or before waiting for incoming notifications.\r
778          */\r
779         taskENTER_CRITICAL();\r
780         {\r
781             /* Dequeue the first job in FIFO order. */\r
782             pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
783 \r
784             /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */\r
785             if( pFirst != NULL )\r
786             {\r
787                 /* Extract the job from its link. */\r
788                 pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );\r
789 \r
790                 /* Update status to 'executing'. */\r
791                 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED; /*_RB_ Should this be 'executing'? */\r
792                 userCallback = pJob->userCallback;\r
793             }\r
794         }\r
795         taskEXIT_CRITICAL();\r
796 \r
797         /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */\r
798         while( pJob != NULL )\r
799         {\r
800             /* Process the job by invoking the associated callback with the user context.\r
801              * This task pool thread will not be available until the user callback returns.\r
802              */\r
803             {\r
804                 IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );\r
805                 IotTaskPool_Assert( userCallback != NULL );\r
806 \r
807                 userCallback( pTaskPool, pJob, pJob->pUserContext );\r
808 \r
809                 /* This job is finished, clear its pointer. */\r
810                 pJob = NULL;\r
811                 userCallback = NULL;\r
812 \r
813                 /* If this thread exceeded the quota, then let it terminate. */\r
814                 if( running == false )\r
815                 {\r
816                     /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */\r
817                     break;\r
818                 }\r
819             }\r
820 \r
821             /* Acquire the lock before updating the job status. */\r
822             taskENTER_CRITICAL();\r
823             {\r
824                 /* Try and dequeue the next job in the dispatch queue. */\r
825                 IotLink_t * pItem = NULL;\r
826 \r
827                 /* Dequeue the next job from the dispatch queue. */\r
828                 pItem = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
829 \r
830                 /* If there is no job left in the dispatch queue, update the worker status and leave. */\r
831                 if( pItem == NULL )\r
832                 {\r
833                     taskEXIT_CRITICAL();\r
834 \r
835                     /* Abandon the INNER LOOP. Execution will tranfer back to the OUTER LOOP condition. */\r
836                     break;\r
837                 }\r
838                 else\r
839                 {\r
840                     pJob = IotLink_Container( _taskPoolJob_t, pItem, link );\r
841 \r
842                     userCallback = pJob->userCallback;\r
843                 }\r
844 \r
845                 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;\r
846             }\r
847             taskEXIT_CRITICAL();\r
848         }\r
849     } while( running == true );\r
850 \r
851     vTaskDelete( NULL );\r
852 }\r
853 \r
854 /* ---------------------------------------------------------------------------------------------- */\r
855 \r
856 static void _initJobsCache( _taskPoolCache_t * const pCache )\r
857 {\r
858     IotDeQueue_Create( &pCache->freeList );\r
859 \r
860     pCache->freeCount = 0;\r
861 }\r
862 \r
863 /*-----------------------------------------------------------*/\r
864 \r
865 static void _initializeJob( _taskPoolJob_t * const pJob,\r
866                             IotTaskPoolRoutine_t userCallback,\r
867                             void * pUserContext,\r
868                             bool isStatic )\r
869 {\r
870     pJob->link.pNext = NULL;\r
871     pJob->link.pPrevious = NULL;\r
872     pJob->userCallback = userCallback;\r
873     pJob->pUserContext = pUserContext;\r
874 \r
875     if( isStatic )\r
876     {\r
877         pJob->flags = IOT_TASK_POOL_INTERNAL_STATIC;\r
878         pJob->status = IOT_TASKPOOL_STATUS_READY;\r
879     }\r
880     else\r
881     {\r
882         pJob->status = IOT_TASKPOOL_STATUS_READY;\r
883     }\r
884 }\r
885 \r
886 static _taskPoolJob_t * _fetchOrAllocateJob( _taskPoolCache_t * const pCache )\r
887 {\r
888     _taskPoolJob_t * pJob = NULL;\r
889     IotLink_t * pLink = IotListDouble_RemoveHead( &( pCache->freeList ) );\r
890 \r
891     if( pLink != NULL )\r
892     {\r
893         pJob = IotLink_Container( _taskPoolJob_t, pLink, link );\r
894     }\r
895 \r
896     /* If there is no available job in the cache, then allocate one. */\r
897     if( pJob == NULL )\r
898     {\r
899         pJob = ( _taskPoolJob_t * ) IotTaskPool_MallocJob( sizeof( _taskPoolJob_t ) );\r
900 \r
901         if( pJob != NULL )\r
902         {\r
903             memset( pJob, 0x00, sizeof( _taskPoolJob_t ) );\r
904         }\r
905         else\r
906         {\r
907             /* Log alocation failure for troubleshooting purposes. */\r
908             IotLogInfo( "Failed to allocate job." );\r
909         }\r
910     }\r
911     /* If there was a job in the cache, then make sure we keep the counters up-to-date. */\r
912     else\r
913     {\r
914         IotTaskPool_Assert( pCache->freeCount > 0 );\r
915 \r
916         pCache->freeCount--;\r
917     }\r
918 \r
919     return pJob;\r
920 }\r
921 \r
922 /*-----------------------------------------------------------*/\r
923 \r
924 static void _recycleJob( _taskPoolCache_t * const pCache,\r
925                          _taskPoolJob_t * const pJob )\r
926 {\r
927     /* We should never try and recycling a job that is linked into some queue. */\r
928     IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) == false );//_RB_ Seems to be duplicate of test before this is called.\r
929 \r
930     /* We will recycle the job if there is space in the cache. */\r
931     if( pCache->freeCount < IOT_TASKPOOL_JOBS_RECYCLE_LIMIT )\r
932     {\r
933         /* Destroy user data, for added safety&security. */\r
934         pJob->userCallback = NULL;\r
935         pJob->pUserContext = NULL;\r
936 \r
937         /* Reset the status for added debuggability. */\r
938         pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;\r
939 \r
940         IotListDouble_InsertTail( &pCache->freeList, &pJob->link );\r
941 \r
942         pCache->freeCount++;\r
943 \r
944         IotTaskPool_Assert( pCache->freeCount >= 1 );\r
945     }\r
946     else\r
947     {\r
948         _destroyJob( pJob );\r
949     }\r
950 }\r
951 \r
952 /*-----------------------------------------------------------*/\r
953 \r
954 static void _destroyJob( _taskPoolJob_t * const pJob )\r
955 {\r
956     /* Destroy user data, for added safety & security. */\r
957     pJob->userCallback = NULL;\r
958     pJob->pUserContext = NULL;\r
959 \r
960     /* Reset the status for added debuggability. */\r
961     pJob->status = IOT_TASKPOOL_STATUS_UNDEFINED;\r
962 \r
963     /* Only dispose of dynamically allocated jobs. */\r
964     if( ( pJob->flags & IOT_TASK_POOL_INTERNAL_STATIC ) == 0UL )\r
965     {\r
966         IotTaskPool_FreeJob( pJob );\r
967     }\r
968 }\r
969 \r
970 /* ---------------------------------------------------------------------------------------------- */\r
971 \r
972 static bool _IsShutdownStarted( const _taskPool_t * const pTaskPool )\r
973 {\r
974     return( pTaskPool->running == false );\r
975 }\r
976 \r
977 /*-----------------------------------------------------------*/\r
978 \r
979 static void _signalShutdown( _taskPool_t * const pTaskPool,\r
980                              uint32_t threads )\r
981 {\r
982     uint32_t count;\r
983 \r
984     /* Set the exit condition. */\r
985     pTaskPool->running = false;\r
986 \r
987     /* Broadcast to all active threads to wake-up. Active threads do check the exit condition right after wakein up. */\r
988     for( count = 0; count < threads; ++count )\r
989     {\r
990         ( void ) xSemaphoreGive( pTaskPool->dispatchSignal );\r
991     }\r
992 }\r
993 \r
994 /* ---------------------------------------------------------------------------------------------- */\r
995 \r
996 static IotTaskPoolError_t _scheduleInternal( _taskPool_t * const pTaskPool,\r
997                                              _taskPoolJob_t * const pJob )\r
998 {\r
999     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
1000 \r
1001     /* Update the job status to 'scheduled'. */\r
1002     pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;\r
1003 \r
1004     /* Append the job to the dispatch queue. */\r
1005     IotDeQueue_EnqueueTail( &pTaskPool->dispatchQueue, &pJob->link );\r
1006 \r
1007     /* Signal a worker to pick up the job. */\r
1008     xSemaphoreGive( pTaskPool->dispatchSignal );\r
1009 \r
1010     TASKPOOL_NO_FUNCTION_CLEANUP_NOLABEL();\r
1011 }\r
1012 \r
1013 /*-----------------------------------------------------------*/\r
1014 \r
1015 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
1016                                 void * pMatch )\r
1017 {\r
1018     const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;\r
1019 \r
1020     const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
1021 \r
1022     if( pJob == pTimerEvent->job )\r
1023     {\r
1024         return true;\r
1025     }\r
1026 \r
1027     return false;\r
1028 }\r
1029 \r
1030 /*-----------------------------------------------------------*/\r
1031 \r
1032 static IotTaskPoolError_t _tryCancelInternal( _taskPool_t * const pTaskPool,\r
1033                                               _taskPoolJob_t * const pJob,\r
1034                                               IotTaskPoolJobStatus_t * const pStatus )\r
1035 {\r
1036     TASKPOOL_FUNCTION_ENTRY( IOT_TASKPOOL_SUCCESS );\r
1037 \r
1038     bool cancelable = false;\r
1039 \r
1040     /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */\r
1041 \r
1042     IotTaskPoolJobStatus_t currentStatus = pJob->status;\r
1043 \r
1044     switch( currentStatus )\r
1045     {\r
1046         case IOT_TASKPOOL_STATUS_READY:\r
1047         case IOT_TASKPOOL_STATUS_DEFERRED:\r
1048         case IOT_TASKPOOL_STATUS_SCHEDULED:\r
1049         case IOT_TASKPOOL_STATUS_CANCELED:\r
1050             cancelable = true;\r
1051             break;\r
1052 \r
1053         case IOT_TASKPOOL_STATUS_COMPLETED:\r
1054             /* Log mesggesong purposes. */\r
1055             IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );\r
1056             break;\r
1057 \r
1058         default:\r
1059             /* Log mesggesong purposes. */\r
1060             IotLogError( "Attempt to cancel a job with an undefined state." );\r
1061             break;\r
1062     }\r
1063 \r
1064     /* Update the returned status to the current status of the job. */\r
1065     if( pStatus != NULL )\r
1066     {\r
1067         *pStatus = currentStatus;\r
1068     }\r
1069 \r
1070     if( cancelable == false )\r
1071     {\r
1072         TASKPOOL_SET_AND_GOTO_CLEANUP( IOT_TASKPOOL_CANCEL_FAILED );\r
1073     }\r
1074     else\r
1075     {\r
1076         /* Update the status of the job. */\r
1077         pJob->status = IOT_TASKPOOL_STATUS_CANCELED;\r
1078 \r
1079         /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch\r
1080          * queue and signal any waiting threads. */\r
1081         if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )\r
1082         {\r
1083             /* A scheduled work items must be in the dispatch queue. */\r
1084             IotTaskPool_Assert( IotLink_IsLinked( &pJob->link ) );\r
1085 \r
1086             IotDeQueue_Remove( &pJob->link );\r
1087         }\r
1088 \r
1089         /* If the job current status is 'deferred' then the job has to be pending\r
1090          * in the timeouts queue. */\r
1091         else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )\r
1092         {\r
1093             /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */\r
1094             IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &pTaskPool->timerEventsList, NULL, _matchJobByPointer, pJob );\r
1095             IotTaskPool_Assert( pTimerEventLink != NULL );\r
1096 \r
1097             if( pTimerEventLink != NULL )\r
1098             {\r
1099                 bool shouldReschedule = false;\r
1100 \r
1101                 /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer\r
1102                  * with the next job timeout */\r
1103                 IotLink_t * pHeadLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
1104 \r
1105                 if( pHeadLink == pTimerEventLink )\r
1106                 {\r
1107                     shouldReschedule = true;\r
1108                 }\r
1109 \r
1110                 /* Remove the timer event associated with the canceled job and free the associated memory. */\r
1111                 IotListDouble_Remove( pTimerEventLink );\r
1112                 IotTaskPool_FreeTimerEvent( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ) );\r
1113 \r
1114                 if( shouldReschedule )\r
1115                 {\r
1116                     IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
1117 \r
1118                     if( pNextTimerEventLink != NULL )\r
1119                     {\r
1120                         _rescheduleDeferredJobsTimer( pTaskPool->timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );\r
1121                     }\r
1122                 }\r
1123             }\r
1124         }\r
1125         else\r
1126         {\r
1127             /* A cancelable job status should be either 'scheduled' or 'deferrred'. */\r
1128             IotTaskPool_Assert( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );\r
1129         }\r
1130     }\r
1131 \r
1132     TASKPOOL_NO_FUNCTION_CLEANUP();\r
1133 }\r
1134 \r
1135 /*-----------------------------------------------------------*/\r
1136 \r
1137 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
1138                                    const IotLink_t * const pTimerEventLink2 )\r
1139 {\r
1140     const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,\r
1141                                                                           pTimerEventLink1,\r
1142                                                                           link );\r
1143     const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,\r
1144                                                                           pTimerEventLink2,\r
1145                                                                           link );\r
1146 \r
1147     if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )\r
1148     {\r
1149         return -1;\r
1150     }\r
1151 \r
1152     if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )\r
1153     {\r
1154         return 1;\r
1155     }\r
1156 \r
1157     return 0;\r
1158 }\r
1159 \r
1160 /*-----------------------------------------------------------*/\r
1161 \r
1162 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
1163                                           _taskPoolTimerEvent_t * const pFirstTimerEvent )\r
1164 {\r
1165     uint64_t delta = 0;\r
1166     TickType_t now = xTaskGetTickCount();\r
1167 \r
1168     if( pFirstTimerEvent->expirationTime > now )\r
1169     {\r
1170         delta = pFirstTimerEvent->expirationTime - now;\r
1171     }\r
1172 \r
1173     if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )\r
1174     {\r
1175         delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */\r
1176     }\r
1177 \r
1178     IotTaskPool_Assert( delta > 0 );\r
1179 \r
1180     if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )\r
1181     {\r
1182         IotLogWarn( "Failed to re-arm timer for task pool" );\r
1183     }\r
1184 }\r
1185 \r
1186 /*-----------------------------------------------------------*/\r
1187 \r
1188 static void _timerCallback( TimerHandle_t xTimer )\r
1189 {\r
1190     _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );\r
1191 \r
1192     IotTaskPool_Assert( pTaskPool );\r
1193 \r
1194     _taskPoolTimerEvent_t * pTimerEvent = NULL;\r
1195 \r
1196     IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );\r
1197 \r
1198     /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.\r
1199      * If this mutex cannot be locked it means that another thread is manipulating the\r
1200      * timeouts list, and will reset the timer to fire again, although it will be late.\r
1201      */\r
1202     taskENTER_CRITICAL();\r
1203     {\r
1204         /* Dispatch all deferred job whose timer expired, then reset the timer for the next\r
1205          * job down the line. */\r
1206         for( ; ; )\r
1207         {\r
1208             /* Peek the first event in the timer event list. */\r
1209             IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
1210 \r
1211             /* Check if the timer misfired for any reason.  */\r
1212             if( pLink != NULL )\r
1213             {\r
1214                 /* Record the current time. */\r
1215                 TickType_t now = xTaskGetTickCount();\r
1216 \r
1217                 /* Extract the job from its envelope. */\r
1218                 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
1219 \r
1220                 /* Check if the first event should be processed now. */\r
1221                 if( pTimerEvent->expirationTime <= now )\r
1222                 {\r
1223                     /*  Remove the timer event for immediate processing. */\r
1224                     IotListDouble_Remove( &( pTimerEvent->link ) );\r
1225                 }\r
1226                 else\r
1227                 {\r
1228                     /* The first element in the timer queue shouldn't be processed yet.\r
1229                      * Arm the timer for when it should be processed and leave altogether. */\r
1230                     _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );\r
1231 \r
1232                     break;\r
1233                 }\r
1234             }\r
1235             /* If there are no timer events to process, terminate this thread. */\r
1236             else\r
1237             {\r
1238                 IotLogDebug( "No further timer events to process. Exiting timer thread." );\r
1239 \r
1240                 break;\r
1241             }\r
1242 \r
1243             IotLogDebug( "Scheduling job from timer event." );\r
1244 \r
1245             /* Queue the job associated with the received timer event. */\r
1246             ( void ) _scheduleInternal( pTaskPool, pTimerEvent->job );\r
1247 \r
1248             /* Free the timer event. */\r
1249             IotTaskPool_FreeTimerEvent( pTimerEvent );\r
1250         }\r
1251     }\r
1252     taskEXIT_CRITICAL();\r
1253 }\r