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