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