]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/abstractions/platform/freertos/iot_taskpool_freertos.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / abstractions / platform / freertos / iot_taskpool_freertos.c
1 /*\r
2  * FreeRTOS Task Pool v1.0.0\r
3  * Copyright (C) 2019 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 \r
23 /**\r
24  * @file iot_taskpool_freertos.c\r
25  * @brief Implements the task pool functions in iot_taskpool.h for FreeRTOS.\r
26  */\r
27 \r
28 \r
29 /*\r
30  * The full IoT Task Pool Library has many use cases, including Linux development.\r
31  * Typical FreeRTOS use cases do not require the full functionality so an optimized\r
32  * implementation is provided specifically for use with FreeRTOS.  The optimized\r
33  * version has a fixed number of tasks in the pool, each of which uses statically\r
34  * allocated memory to ensure creation of the pool is guaranteed (it does not run out\r
35  * of heap space).  The constant IOT_TASKPOOL_NUMBER_OF_WORKERS sets the number of\r
36  * tasks in the pool.\r
37  *\r
38  * Unlike the full version, this optimized version:\r
39  *   + Only supports a single task pool (system task pool) at a time.\r
40  *   + Does not auto-scale by dynamically adding more tasks if the number of\r
41  *     tasks in the pool becomes exhausted.  The number of tasks in the pool\r
42  *     are fixed at compile time. See the task pool configuration options for\r
43  *     more information.\r
44  *   + Cannot be shut down - it exists for the lifetime of the application.\r
45  *\r
46  * Users who are interested in the functionality of the full IoT Task Pool\r
47  * library can us it in place of this optimized version.\r
48  */\r
49 \r
50 \r
51 /* Kernel includes. */\r
52 #include "FreeRTOS.h"\r
53 #include "semphr.h"\r
54 \r
55 /* IoT libraries includes. */\r
56 #include "iot_config.h"\r
57 \r
58 /* Standard includes. */\r
59 #include <stdbool.h>\r
60 #include <stdio.h>\r
61 #include <stddef.h>\r
62 #include <stdint.h>\r
63 #include <string.h>\r
64 \r
65 #if !defined( configSUPPORT_STATIC_ALLOCATION ) || ( configSUPPORT_STATIC_ALLOCATION != 1 )\r
66     #error configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h to build this file.\r
67 #endif\r
68 \r
69 /* Task pool internal include. */\r
70 #include "private/iot_taskpool_internal_freertos.h"\r
71 \r
72 /**\r
73  * @brief Maximum semaphore value for wait operations.\r
74  */\r
75 #define TASKPOOL_MAX_SEM_VALUE              ( ( UBaseType_t ) 0xFFFF )\r
76 \r
77 /**\r
78  * @brief Reschedule delay in milliseconds for deferred jobs.\r
79  */\r
80 #define TASKPOOL_JOB_RESCHEDULE_DELAY_MS    ( 10ULL )\r
81 \r
82 /* ---------------------------------------------------------------------------------- */\r
83 \r
84 /**\r
85  * @brief Get the job associated with a timer.\r
86  *\r
87  * @warning This only works on a _taskPoolTimerEvent_t within a _taskPoolJob_t.\r
88  */\r
89 #define GET_JOB_FROM_TIMER(t) ((_taskPoolJob_t *)((uint8_t*)(t) - offsetof(_taskPoolJob_t, timer)))\r
90 \r
91 /**\r
92  * brief The maximum time to wait when attempting to obtain an internal semaphore.\r
93  * Don't wait indefinitely to give the application a chance to recover in the case\r
94  * of an error.\r
95  */\r
96 #define MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ( pdMS_TO_TICKS( 10000UL ) )\r
97 /* ---------------------------------------------------------------------------------- */\r
98 \r
99 /**\r
100  * Doxygen should ignore this section.\r
101  *\r
102  * @brief The system task pool handle for all libraries to use.\r
103  * User application can use the system task pool as well knowing that the usage will be shared with\r
104  * the system libraries as well. The system task pool needs to be initialized before any library is used or\r
105  * before any code that posts jobs to the task pool runs.\r
106  */\r
107 static _taskPool_t _IotSystemTaskPool = { 0 };\r
108 \r
109 /* -------------- Convenience functions to create/recycle/destroy jobs -------------- */\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 to be passed to the callback.\r
117  */\r
118 static void _initializeJob( _taskPoolJob_t * const pJob,\r
119                             IotTaskPoolRoutine_t userCallback,\r
120                             void * pUserContext );\r
121 \r
122 /* -------------- The worker thread procedure for a task pool thread -------------- */\r
123 \r
124 /**\r
125  * The procedure for a task pool worker thread.\r
126  *\r
127  * @param[in] pUserContext The user context.\r
128  *\r
129  */\r
130 static void _taskPoolWorker( void * pUserContext );\r
131 \r
132 /* -------------- Convenience functions to handle timer events  -------------- */\r
133 \r
134 /**\r
135  * Comparer for the time list.\r
136  *\r
137  * param[in] pTimerEventLink1 The link to the first timer event.\r
138  * param[in] pTimerEventLink1 The link to the first timer event.\r
139  */\r
140 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
141                                    const IotLink_t * const pTimerEventLink2 );\r
142 \r
143 /**\r
144  * Reschedules the timer for handling deferred jobs to the next timeout.\r
145  *\r
146  * param[in] timer The timer to reschedule.\r
147  * param[in] pFirstTimerEvent The timer event that carries the timeout and job information.\r
148  */\r
149 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
150                                           _taskPoolTimerEvent_t * const pFirstTimerEvent );\r
151 \r
152 /**\r
153  * The task pool timer procedure for scheduling deferred jobs.\r
154  *\r
155  * param[in] timer The timer to handle.\r
156  */\r
157 static void _timerCallback( TimerHandle_t xTimer );\r
158 \r
159 /* -------------- Convenience functions to create/initialize/destroy the task pool -------------- */\r
160 \r
161 /**\r
162  * Initializes a pre-allocated instance of a task pool.\r
163  */\r
164 static void _initTaskPoolControlStructures( void );\r
165 \r
166 /**\r
167  * Initializes a pre-allocated instance of a task pool.\r
168  *\r
169  * @param[in] pInfo The initialization information for the task pool.\r
170  *\r
171  */\r
172 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo );\r
173 \r
174 /**\r
175  * Places a job in the dispatch queue.\r
176  *\r
177  * @param[in] pJob The job to schedule.\r
178  *\r
179  */\r
180 static void _scheduleInternal( _taskPoolJob_t * const pJob );\r
181 /**\r
182  * Matches a deferred job in the timer queue with its timer event wrapper.\r
183  *\r
184  * @param[in] pLink A pointer to the timer event link in the timer queue.\r
185  * @param[in] pMatch A pointer to the job to match.\r
186  *\r
187  */\r
188 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
189                                 void * pMatch );\r
190 \r
191 /**\r
192  * Tries to cancel a job.\r
193  *\r
194  * @param[in] pJob The job to cancel.\r
195  * @param[out] pStatus The status of the job at the time of cancellation.\r
196  *\r
197  */\r
198 static IotTaskPoolError_t _tryCancelInternal( _taskPoolJob_t * const pJob,\r
199                                               IotTaskPoolJobStatus_t * const pStatus );\r
200 \r
201 /* ---------------------------------------------------------------------------------------------- */\r
202 \r
203 IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo )\r
204 {\r
205     IotTaskPoolError_t status;\r
206     static BaseType_t isInitialized = pdFALSE;\r
207 \r
208     configASSERT( pInfo );\r
209     configASSERT( pInfo->minThreads >= 1UL );\r
210 \r
211     /* This version of the task pool does not auto-scale so the specified minimum\r
212     number of threads in the pool must match the specified maximum number of threads\r
213     in the pool. */\r
214     configASSERT( pInfo->maxThreads == pInfo->minThreads );\r
215 \r
216     /* Guard against multiple attempts to create the system task pool in case\r
217     this function is called by more than one library initialization routine. */\r
218     taskENTER_CRITICAL();\r
219     {\r
220         /* If the task pool has already been initialized then\r
221         IOT_TASKPOOL_ILLEGAL_OPERATION will be returned - but that does not guarantee\r
222         the initialization operation is complete as the task to which\r
223         IOT_TASKPOOL_ILLEGAL_OPERATION is returned may have preempted the task that\r
224         was performing the initialization before the initialization was complete -\r
225         hence the assert to catch this occurrence during development and debug. */\r
226         configASSERT( isInitialized == pdFALSE );\r
227 \r
228         if( isInitialized == pdFALSE )\r
229         {\r
230             /* The task pool has not been initialized already so will be initialized\r
231             now. */\r
232             status = IOT_TASKPOOL_SUCCESS;\r
233             isInitialized = pdTRUE;\r
234         }\r
235         else\r
236         {\r
237             /* This function has already been called but executing this path does\r
238             not guarantee the task pool has already been initialized as the task\r
239             to which this error is returned may have preempted the task that was\r
240             performing the initialization before the initialization was complete. */\r
241             status = IOT_TASKPOOL_ILLEGAL_OPERATION;\r
242         }\r
243     }\r
244     taskEXIT_CRITICAL();\r
245 \r
246     if( status == IOT_TASKPOOL_SUCCESS )\r
247     {\r
248         /* Create the system task pool.  Note in this version _createTaskPool()\r
249         cannot fail because it is using statically allocated memory.  Therefore the\r
250         return value can be safely ignored and there is no need to consider resetting\r
251         isInitialized in a failure case. */\r
252         ( void ) _createTaskPool( pInfo );\r
253     }\r
254 \r
255     return status;\r
256 }\r
257 /*-----------------------------------------------------------*/\r
258 \r
259 IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,\r
260                                           void * pUserContext,\r
261                                           IotTaskPoolJobStorage_t * const pJobStorage,\r
262                                           IotTaskPoolJob_t * const ppJob )\r
263 {\r
264     /* Parameter checking. */\r
265     configASSERT( userCallback != NULL );\r
266     configASSERT( pJobStorage != NULL );\r
267     configASSERT( ppJob != NULL );\r
268 \r
269     /* Build a job around the user-provided storage. */\r
270     _initializeJob( ( _taskPoolJob_t * ) pJobStorage, userCallback, pUserContext );\r
271 \r
272     *ppJob = ( IotTaskPoolJob_t ) pJobStorage;\r
273 \r
274     return IOT_TASKPOOL_SUCCESS;\r
275 }\r
276 \r
277 /*-----------------------------------------------------------*/\r
278 \r
279 IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPoolHandle,\r
280                                          IotTaskPoolJob_t pJob,\r
281                                          uint32_t flags )\r
282 {\r
283     IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;\r
284 \r
285     /* Task pool must have been created. */\r
286     configASSERT( _IotSystemTaskPool.running != false );\r
287 \r
288     /* This lean version of the task pool only supports the task pool created\r
289     by this library (the system task pool).  NULL means use the system task\r
290     pool - no other values are allowed.  Use the full implementation of this\r
291     library if you want multiple task pools (there is more than one task in\r
292     each pool. */\r
293     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
294 \r
295     /* Avoid compiler warnings about unused parameters if configASSERT() is not\r
296     defined. */\r
297     ( void ) taskPoolHandle;\r
298 \r
299     configASSERT( pJob != NULL );\r
300     configASSERT( ( flags == 0UL ) || ( flags == IOT_TASKPOOL_JOB_HIGH_PRIORITY ) );\r
301 \r
302     /* Acquire the mutex for manipulating the job timer queue. */\r
303     if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ) == pdTRUE )\r
304     {\r
305         _scheduleInternal( pJob );\r
306         if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )\r
307         {\r
308             /* This can only be reached if semaphores are configured incorrectly. */\r
309             status =  IOT_TASKPOOL_GENERAL_FAILURE;\r
310         }\r
311         /* Signal a worker task that a job was queued. */\r
312         if ( xSemaphoreGive( _IotSystemTaskPool.dispatchSignal ) == pdFALSE )\r
313         {\r
314             /* This can only be reached if semaphores are configured incorrectly. */\r
315             status = IOT_TASKPOOL_GENERAL_FAILURE;\r
316         }\r
317     }\r
318     else\r
319     {\r
320         status =  IOT_TASKPOOL_GENERAL_FAILURE;\r
321     }\r
322 \r
323     return status;\r
324 }\r
325 \r
326 /*-----------------------------------------------------------*/\r
327 \r
328 IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPoolHandle,\r
329                                                  IotTaskPoolJob_t job,\r
330                                                  uint32_t timeMs )\r
331 {\r
332     TickType_t now;\r
333     IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;\r
334 \r
335     /* This lean version of the task pool only supports the task pool created\r
336     by this library (the system task pool).  NULL means use the system task\r
337     pool - no other values are allowed.  Use the full implementation of this\r
338     library if you want multiple task pools (there is more than one task in\r
339     each pool. */\r
340     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
341 \r
342     configASSERT( job != NULL );\r
343 \r
344     /* If the timer period is zero, just immediately queue the job for execution. */\r
345     if( timeMs == 0UL )\r
346     {\r
347         status = IotTaskPool_Schedule( &_IotSystemTaskPool, job, 0 );\r
348     }\r
349     else\r
350     {\r
351         _taskPoolTimerEvent_t* pTimerEvent = &(job->timer);\r
352 \r
353         configASSERT( job->timer.link.pNext == NULL );\r
354 \r
355         IotLink_t* pTimerEventLink;\r
356 \r
357         pTimerEvent->link.pNext = NULL;\r
358         pTimerEvent->link.pPrevious = NULL;\r
359 \r
360         now = xTaskGetTickCount();\r
361         pTimerEvent->expirationTime = now + pdMS_TO_TICKS( timeMs );\r
362 \r
363         if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, MAX_SEMAPHORE_TAKE_WAIT_TIME_MS ) == pdTRUE )\r
364         {\r
365 \r
366             /* Append the timer event to the timer list. */\r
367             IotListDouble_InsertSorted( &( _IotSystemTaskPool.timerEventsList ), &( pTimerEvent->link ), _timerEventCompare );\r
368 \r
369             /* Update the job status to 'scheduled'. */\r
370             job->status = IOT_TASKPOOL_STATUS_DEFERRED;\r
371 \r
372             /* Peek the first event in the timer event list. There must be at least one,\r
373              * since we just inserted it. */\r
374             pTimerEventLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );\r
375             configASSERT( pTimerEventLink != NULL );\r
376 \r
377             /* If the event we inserted is at the front of the queue, then\r
378                 * we need to reschedule the underlying timer. */\r
379             if ( pTimerEventLink == &( pTimerEvent->link ) )\r
380             {\r
381                 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link );\r
382 \r
383                 _rescheduleDeferredJobsTimer( _IotSystemTaskPool.timer, pTimerEvent );\r
384             }\r
385 \r
386             if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )\r
387             {\r
388                 /* This can only be reached if semaphores are configured incorrectly. */\r
389                 status = IOT_TASKPOOL_GENERAL_FAILURE;\r
390             }\r
391 \r
392         }\r
393         else\r
394         {\r
395             status = IOT_TASKPOOL_GENERAL_FAILURE;\r
396         }\r
397     }\r
398 \r
399     return status;\r
400 }\r
401 \r
402 /*-----------------------------------------------------------*/\r
403 \r
404 IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPoolHandle,\r
405                                           IotTaskPoolJob_t job,\r
406                                           IotTaskPoolJobStatus_t * const pStatus )\r
407 {\r
408 \r
409     /* This lean version of the task pool only supports the task pool created by\r
410     this library (the system task pool).  NULL means use the system task pool -\r
411     no other values are allowed.  Use the full implementation of this library if you\r
412     want multiple task pools (there is more than one task in each pool. */\r
413     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
414 \r
415     /* Remove warning about unused parameter. */\r
416     ( void ) taskPoolHandle;\r
417 \r
418     /* Parameter checking. */\r
419     configASSERT( job != NULL );\r
420     configASSERT( pStatus != NULL );\r
421 \r
422     taskENTER_CRITICAL();\r
423     {\r
424         *pStatus = job->status;\r
425     }\r
426     taskEXIT_CRITICAL();\r
427 \r
428     return IOT_TASKPOOL_SUCCESS;\r
429 }\r
430 \r
431 /*-----------------------------------------------------------*/\r
432 \r
433 IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPoolHandle,\r
434                                           IotTaskPoolJob_t job,\r
435                                           IotTaskPoolJobStatus_t * const pStatus )\r
436 {\r
437     IotTaskPoolError_t status = IOT_TASKPOOL_SUCCESS;\r
438     const TickType_t dontBlock = ( TickType_t ) 0;\r
439 \r
440     /* This lean version of the task pool only supports the task pool created\r
441     by this library (the system task pool).  NULL means use the system task\r
442     pool - no other values are allowed.  Use the full implementation of this\r
443     library if you want multiple task pools (there is more than one task in\r
444     each pool. */\r
445     configASSERT( ( taskPoolHandle == NULL ) || ( taskPoolHandle == &_IotSystemTaskPool ) );\r
446 \r
447     if( job != NULL )\r
448     {\r
449         if( pStatus != NULL )\r
450         {\r
451             *pStatus = IOT_TASKPOOL_STATUS_UNDEFINED;\r
452         }\r
453 \r
454         if ( xSemaphoreTake( _IotSystemTaskPool.xTimerEventMutex, dontBlock ) != pdFALSE )\r
455         {\r
456             status = _tryCancelInternal( job, pStatus );\r
457             if ( xSemaphoreGive( _IotSystemTaskPool.xTimerEventMutex ) == pdFALSE )\r
458             {\r
459                 /* This can only be reached if semaphores are configured incorrectly. */\r
460                 status = IOT_TASKPOOL_GENERAL_FAILURE;\r
461             }\r
462         }\r
463         else\r
464         {\r
465             /* If we fail to take the semaphore, just abort the cancel. */\r
466             status = IOT_TASKPOOL_CANCEL_FAILED;\r
467         }\r
468     }\r
469     else\r
470     {\r
471         status = IOT_TASKPOOL_BAD_PARAMETER;\r
472     }\r
473 \r
474     return status;\r
475 }\r
476 \r
477 /*-----------------------------------------------------------*/\r
478 \r
479 IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t pJob )\r
480 {\r
481     return ( IotTaskPoolJobStorage_t * ) pJob;\r
482 }\r
483 \r
484 /*-----------------------------------------------------------*/\r
485 \r
486 const char * IotTaskPool_strerror( IotTaskPoolError_t status )\r
487 {\r
488     const char * pMessage = NULL;\r
489 \r
490     switch( status )\r
491     {\r
492         case IOT_TASKPOOL_SUCCESS:\r
493             pMessage = "SUCCESS";\r
494             break;\r
495 \r
496         case IOT_TASKPOOL_BAD_PARAMETER:\r
497             pMessage = "BAD PARAMETER";\r
498             break;\r
499 \r
500         case IOT_TASKPOOL_ILLEGAL_OPERATION:\r
501             pMessage = "ILLEGAL OPERATION";\r
502             break;\r
503 \r
504         case IOT_TASKPOOL_NO_MEMORY:\r
505             pMessage = "NO MEMORY";\r
506             break;\r
507 \r
508         case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS:\r
509             pMessage = "SHUTDOWN IN PROGRESS";\r
510             break;\r
511 \r
512         case IOT_TASKPOOL_CANCEL_FAILED:\r
513             pMessage = "CANCEL FAILED";\r
514             break;\r
515 \r
516         case IOT_TASKPOOL_GENERAL_FAILURE:\r
517             pMessage = "GENERAL FAILURE";\r
518             break;\r
519 \r
520         default:\r
521             pMessage = "INVALID STATUS";\r
522             break;\r
523     }\r
524 \r
525     return pMessage;\r
526 }\r
527 \r
528 /* ---------------------------------------------------------------------------------------------- */\r
529 /* ---------------------------------------------------------------------------------------------- */\r
530 /* ---------------------------------------------------------------------------------------------- */\r
531 \r
532 static void _initTaskPoolControlStructures( void )\r
533 {\r
534     /* Initialize a job data structures that require no de-initialization.\r
535      * All other data structures carry a value of 'NULL' before initialization.\r
536      */\r
537     IotDeQueue_Create( &( _IotSystemTaskPool.dispatchQueue ) );\r
538     IotListDouble_Create( &( _IotSystemTaskPool.timerEventsList ) );\r
539 \r
540     /* Initialize the semaphore for waiting for incoming work.  Cannot fail as\r
541     statically allocated. */\r
542     _IotSystemTaskPool.dispatchSignal = xSemaphoreCreateCountingStatic( TASKPOOL_MAX_SEM_VALUE, 0, &( _IotSystemTaskPool.dispatchSignalBuffer ) );\r
543     _IotSystemTaskPool.xTimerEventMutex = xSemaphoreCreateMutexStatic( &( _IotSystemTaskPool.xTimerEventMutexBuffer ) );\r
544 }\r
545 \r
546 static IotTaskPoolError_t _createTaskPool( const IotTaskPoolInfo_t * const pInfo )\r
547 {\r
548     /* The taskpool will create a number of threads equal to the minThreads\r
549     setting. The number of workers should be equal to avoid over/under\r
550     allocation.    */\r
551     configASSERT( IOT_TASKPOOL_NUMBER_OF_WORKERS == pInfo->minThreads );\r
552 \r
553     /* Static TCB structures and arrays to be used by statically allocated worker\r
554     tasks. */\r
555     static StaticTask_t workerTaskTCBs[ IOT_TASKPOOL_NUMBER_OF_WORKERS ];\r
556     static StackType_t workerTaskStacks[ IOT_TASKPOOL_NUMBER_OF_WORKERS ][ IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ) ];\r
557 \r
558     /* Static structure to hold the software timer. */\r
559     static StaticTimer_t staticTimer;\r
560 \r
561     uint32_t threadsCreated = 0; /* Although initialized before use removing the initializer here results in compiler warnings. */\r
562     char taskName[ 10 ];\r
563 \r
564 \r
565     /* This assert is primarily to catch the function being called more than once,\r
566     but will also ensure the C start up code has zeroed out the structure\r
567     correctly. */\r
568     #if( configASSERT_DEFINED == 1 )\r
569     {\r
570     size_t x;\r
571     uint8_t *pucNextByte = ( uint8_t * ) &_IotSystemTaskPool;\r
572 \r
573         for( x = 0; x < sizeof( _taskPool_t ); x++ )\r
574         {\r
575             configASSERT( pucNextByte[ x ] == ( uint8_t ) 0x00 );\r
576         }\r
577     }\r
578     #endif /* configASSERT_DEFINED */\r
579 \r
580 \r
581 \r
582     /* Initialize all internal data structure prior to creating all threads. */\r
583     _initTaskPoolControlStructures();\r
584 \r
585     /* Create the FreeRTOS timer for managing Task Pool timers. */\r
586     _IotSystemTaskPool.timer = xTimerCreateStatic( NULL, /* Text name for the timer, only used for debugging. */\r
587                                                   portMAX_DELAY, /* Timer period in ticks. */\r
588                                                   pdFALSE, /* pdFALSE means its a one-shot timer. */\r
589                                                   ( void * ) &_IotSystemTaskPool, /* Parameter passed into callback. */\r
590                                                   _timerCallback, /* Callback that executes when the timer expires. */\r
591                                                   &staticTimer ); /* Static storage for the timer's data structure. */\r
592 \r
593     /* The task pool will initialize the minimum number of threads requested by the user upon start.\r
594     Note this tailored version of the task pool does not auto-scale, but fixes the number of tasks\r
595     in the pool to the originally specified minimum, and the specified maximum value is ignored. */\r
596     /* Create the minimum number of threads specified by the user, and if one fails shutdown and return error. */\r
597     for( threadsCreated = 0; threadsCreated < pInfo->minThreads; )\r
598     {\r
599         /* Generate a unique name for the task. */\r
600         snprintf( taskName, sizeof( taskName ), "pool%d", ( int ) threadsCreated );\r
601 \r
602         xTaskCreateStatic( _taskPoolWorker, /* Function that implements the task. */\r
603                            taskName,       /* Text name for the task, used for debugging only. */\r
604                            IOT_TASKPOOL_WORKER_STACK_SIZE_BYTES / sizeof( portSTACK_TYPE ), /* xTaskCreate() expects the stack size to be specified in words. */\r
605                            &_IotSystemTaskPool,       /* Parameter passed into the task. */\r
606                            pInfo->priority, /* Priority at which the task starts running. */\r
607                            &( workerTaskStacks[ threadsCreated ][ 0 ] ), /* Pointer to static storage for the task's stack. */\r
608                            &( workerTaskTCBs[ threadsCreated ] ) ); /* Pointer to static storage for the task's TCB. */\r
609 \r
610         ++threadsCreated;\r
611     }\r
612     _IotSystemTaskPool.running = true;\r
613 \r
614     /* This version of this function cannot fail as all the memory is allocated\r
615     statically at compile time. */\r
616     return IOT_TASKPOOL_SUCCESS;\r
617 }\r
618 \r
619 /* ---------------------------------------------------------------------------------------------- */\r
620 \r
621 static void _taskPoolWorker( void * pUserContext )\r
622 {\r
623     configASSERT( pUserContext != NULL );\r
624 \r
625     IotTaskPoolRoutine_t userCallback = NULL;\r
626 \r
627     /* Extract pTaskPool pointer from context. */\r
628     _taskPool_t * pTaskPool = ( _taskPool_t * ) pUserContext;\r
629 \r
630     /* OUTER LOOP: it controls the lifetime of the worker thread. */\r
631     for( ; ; )\r
632     {\r
633     IotLink_t * pFirst = NULL;\r
634     _taskPoolJob_t * pJob = NULL;\r
635 \r
636         /* Wait on incoming notifications... */\r
637         configASSERT( pTaskPool->dispatchSignal );\r
638 \r
639         /* If the semaphore for job dispatch expires without a job, a critical\r
640            precondition of this task has not been met. See the xBlockTime\r
641            parameter of xSemaphoreTake for details. */\r
642         configASSERT( xSemaphoreTake( pTaskPool->dispatchSignal, portMAX_DELAY ) );\r
643 \r
644         /* Acquire the lock to check for incoming notifications. This call\r
645            should not expire. See the xBlockTime parameter of xSemaphoreTake\r
646            for details. */\r
647         configASSERT( xSemaphoreTake( pTaskPool->xTimerEventMutex, portMAX_DELAY ) );\r
648 \r
649         /* Dequeue the first job in FIFO order. */\r
650         pFirst = IotDeQueue_DequeueHead( &pTaskPool->dispatchQueue );\r
651 \r
652         /* If there is indeed a job, then update status under lock, and release the lock before processing the job. */\r
653         if( pFirst != NULL )\r
654         {\r
655             /* Extract the job from its link. */\r
656             pJob = IotLink_Container( _taskPoolJob_t, pFirst, link );\r
657 \r
658             /* Update status to 'completed' to indicate it is queued for execution. */\r
659             pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;\r
660             userCallback = pJob->userCallback;\r
661         }\r
662 \r
663         /* Release the lock now that the job dispatch queue has been checked.\r
664            This call should not expire. See the xBlockTime parameter of\r
665            xSemaphoreTake for details. */\r
666         configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );\r
667 \r
668         /* INNER LOOP: it controls the execution of jobs: the exit condition is the lack of a job to execute. */\r
669         while( pJob != NULL )\r
670         {\r
671             /* Process the job by invoking the associated callback with the user context.\r
672              * This task pool thread will not be available until the user callback returns.\r
673              */\r
674             {\r
675                 configASSERT( IotLink_IsLinked( &pJob->link ) == false );\r
676                 configASSERT( userCallback != NULL );\r
677 \r
678                 userCallback( pTaskPool, pJob, pJob->pUserContext );\r
679 \r
680                 /* This job is finished, clear its pointer. */\r
681                 pJob = NULL;\r
682                 userCallback = NULL;\r
683             }\r
684 \r
685             /* Acquire the lock to check for incoming notifications. This call\r
686             should not expire. See the xBlockTime parameter of xSemaphoreTake\r
687             for details. */\r
688             configASSERT( xSemaphoreTake( pTaskPool->xTimerEventMutex, portMAX_DELAY ) );\r
689             {\r
690                 /* Try and dequeue the next job in the dispatch queue. */\r
691                 IotLink_t * pItem = NULL;\r
692 \r
693                 /* Dequeue the next job from the dispatch queue. */\r
694                 pItem = IotDeQueue_DequeueHead( &( pTaskPool->dispatchQueue ) );\r
695 \r
696                 /* If there is no job left in the dispatch queue, update the worker status and leave. */\r
697                 if( pItem == NULL )\r
698                 {\r
699                     /* Release the lock before exiting the loop. */\r
700                     configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );\r
701 \r
702                     /* Abandon the INNER LOOP. Execution will transfer back to the OUTER LOOP condition. */\r
703                     break;\r
704                 }\r
705                 else\r
706                 {\r
707                     pJob = IotLink_Container( _taskPoolJob_t, pItem, link );\r
708 \r
709                     userCallback = pJob->userCallback;\r
710                 }\r
711 \r
712                 pJob->status = IOT_TASKPOOL_STATUS_COMPLETED;\r
713             }\r
714             /* Release the lock now that the job dispatch queue has been checked. */\r
715             configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) );\r
716         }\r
717     }\r
718 }\r
719 \r
720 /* ---------------------------------------------------------------------------------------------- */\r
721 \r
722 static void _initializeJob( _taskPoolJob_t * const pJob,\r
723                             IotTaskPoolRoutine_t userCallback,\r
724                             void * pUserContext )\r
725 {\r
726     memset( ( void * ) pJob, 0x00, sizeof( _taskPoolJob_t ) );\r
727 \r
728     pJob->userCallback = userCallback;\r
729     pJob->pUserContext = pUserContext;\r
730     pJob->status = IOT_TASKPOOL_STATUS_READY;\r
731 }\r
732 \r
733 /* ---------------------------------------------------------------------------------------------- */\r
734 \r
735 static void _scheduleInternal( _taskPoolJob_t * const pJob )\r
736 {\r
737     /* Update the job status to 'scheduled'. */\r
738     pJob->status = IOT_TASKPOOL_STATUS_SCHEDULED;\r
739 \r
740     /* Append the job to the dispatch queue. */\r
741     IotDeQueue_EnqueueTail( &( _IotSystemTaskPool.dispatchQueue ), &( pJob->link ) );\r
742 \r
743     /* NOTE:  Every call to this function must be followed by giving the\r
744     dispatchSignal semaphore - but do not give the semaphore directly in\r
745     this function as giving the semaphore will result in the execution of\r
746     a task pool worker task (depending on relative priorities) and we don't\r
747     want the worker task to execute until all semaphores obtained before calling\r
748     this function have been released. */\r
749 }\r
750 \r
751 /*-----------------------------------------------------------*/\r
752 \r
753 static bool _matchJobByPointer( const IotLink_t * const pLink,\r
754                                 void * pMatch )\r
755 {\r
756     const _taskPoolJob_t * const pJob = ( _taskPoolJob_t * ) pMatch;\r
757 \r
758     const _taskPoolTimerEvent_t * const pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
759 \r
760     if( pJob == GET_JOB_FROM_TIMER( pTimerEvent ) )\r
761     {\r
762         return true;\r
763     }\r
764 \r
765     return false;\r
766 }\r
767 \r
768 /*-----------------------------------------------------------*/\r
769 \r
770 static IotTaskPoolError_t _tryCancelInternal( _taskPoolJob_t * const pJob,\r
771                                               IotTaskPoolJobStatus_t * const pStatus )\r
772 {\r
773     IotTaskPoolError_t result = IOT_TASKPOOL_SUCCESS;\r
774 \r
775     bool cancelable = false;\r
776 \r
777     /* We can only cancel jobs that are either 'ready' (waiting to be scheduled). 'deferred', or 'scheduled'. */\r
778 \r
779     IotTaskPoolJobStatus_t currentStatus = pJob->status;\r
780 \r
781     switch( currentStatus )\r
782     {\r
783         case IOT_TASKPOOL_STATUS_READY:\r
784         case IOT_TASKPOOL_STATUS_DEFERRED:\r
785         case IOT_TASKPOOL_STATUS_SCHEDULED:\r
786         case IOT_TASKPOOL_STATUS_CANCELED:\r
787             cancelable = true;\r
788             break;\r
789 \r
790         case IOT_TASKPOOL_STATUS_COMPLETED:\r
791             /* Log message for debug purposes. */\r
792             IotLogWarn( "Attempt to cancel a job that is already executing, or canceled." );\r
793             break;\r
794 \r
795         default:\r
796             /* Log message for debug purposes. */\r
797             IotLogError( "Attempt to cancel a job with an undefined state." );\r
798             break;\r
799     }\r
800 \r
801     /* Update the returned status to the current status of the job. */\r
802     if( pStatus != NULL )\r
803     {\r
804         *pStatus = currentStatus;\r
805     }\r
806 \r
807     if( cancelable == false )\r
808     {\r
809         result = IOT_TASKPOOL_CANCEL_FAILED;\r
810     }\r
811     else\r
812     {\r
813         /* Update the status of the job. */\r
814         pJob->status = IOT_TASKPOOL_STATUS_CANCELED;\r
815 \r
816         /* If the job is cancelable and its current status is 'scheduled' then unlink it from the dispatch\r
817          * queue and signal any waiting threads. */\r
818         if( currentStatus == IOT_TASKPOOL_STATUS_SCHEDULED )\r
819         {\r
820             /* A scheduled work items must be in the dispatch queue. */\r
821             configASSERT( IotLink_IsLinked( &pJob->link ) );\r
822 \r
823             IotDeQueue_Remove( &pJob->link );\r
824         }\r
825 \r
826         /* If the job current status is 'deferred' then the job has to be pending\r
827          * in the timeouts queue. */\r
828         else if( currentStatus == IOT_TASKPOOL_STATUS_DEFERRED )\r
829         {\r
830             /* Find the timer event associated with the current job. There MUST be one, hence assert if not. */\r
831             IotLink_t * pTimerEventLink = IotListDouble_FindFirstMatch( &( _IotSystemTaskPool.timerEventsList ), NULL, _matchJobByPointer, pJob );\r
832             configASSERT( pTimerEventLink != NULL );\r
833 \r
834             if( pTimerEventLink != NULL )\r
835             {\r
836                 bool shouldReschedule = false;\r
837 \r
838                 /* If the job being canceled was at the head of the timeouts queue, then we need to reschedule the timer\r
839                  * with the next job timeout */\r
840                 IotLink_t * pHeadLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );\r
841 \r
842                 if( pHeadLink == pTimerEventLink )\r
843                 {\r
844                     shouldReschedule = true;\r
845                 }\r
846 \r
847                 /* Remove the timer event associated with the canceled job and free the associated memory. */\r
848                 IotListDouble_Remove( pTimerEventLink );\r
849                 memset( IotLink_Container( _taskPoolTimerEvent_t, pTimerEventLink, link ), 0, sizeof( IotLink_t ) );\r
850 \r
851                 if( shouldReschedule )\r
852                 {\r
853                     IotLink_t * pNextTimerEventLink = IotListDouble_PeekHead( &( _IotSystemTaskPool.timerEventsList ) );\r
854 \r
855                     if( pNextTimerEventLink != NULL )\r
856                     {\r
857                         _rescheduleDeferredJobsTimer( _IotSystemTaskPool.timer, IotLink_Container( _taskPoolTimerEvent_t, pNextTimerEventLink, link ) );\r
858                     }\r
859                 }\r
860             }\r
861         }\r
862         else\r
863         {\r
864             /* A cancelable job status should be either 'scheduled' or 'deferred'. */\r
865             configASSERT( ( currentStatus == IOT_TASKPOOL_STATUS_READY ) || ( currentStatus == IOT_TASKPOOL_STATUS_CANCELED ) );\r
866         }\r
867     }\r
868 \r
869     return result;\r
870 }\r
871 \r
872 /*-----------------------------------------------------------*/\r
873 \r
874 static int32_t _timerEventCompare( const IotLink_t * const pTimerEventLink1,\r
875                                    const IotLink_t * const pTimerEventLink2 )\r
876 {\r
877     const _taskPoolTimerEvent_t * const pTimerEvent1 = IotLink_Container( _taskPoolTimerEvent_t,\r
878                                                                           pTimerEventLink1,\r
879                                                                           link );\r
880     const _taskPoolTimerEvent_t * const pTimerEvent2 = IotLink_Container( _taskPoolTimerEvent_t,\r
881                                                                           pTimerEventLink2,\r
882                                                                           link );\r
883 \r
884     if( pTimerEvent1->expirationTime < pTimerEvent2->expirationTime )\r
885     {\r
886         return -1;\r
887     }\r
888 \r
889     if( pTimerEvent1->expirationTime > pTimerEvent2->expirationTime )\r
890     {\r
891         return 1;\r
892     }\r
893 \r
894     return 0;\r
895 }\r
896 \r
897 /*-----------------------------------------------------------*/\r
898 \r
899 static void _rescheduleDeferredJobsTimer( TimerHandle_t const timer,\r
900                                           _taskPoolTimerEvent_t * const pFirstTimerEvent )\r
901 {\r
902     uint64_t delta = 0;\r
903     TickType_t now = xTaskGetTickCount();\r
904 \r
905     configASSERT( pFirstTimerEvent != NULL );\r
906     configASSERT( timer != NULL );\r
907 \r
908      /* Determine how much time is left for the deferred job. */\r
909     if( pFirstTimerEvent->expirationTime > now )\r
910     {\r
911         delta = pFirstTimerEvent->expirationTime - now;\r
912     }\r
913 \r
914     /* If the job timer has exceeded it's period, schedule it to be executed shortly. */\r
915     if( delta < TASKPOOL_JOB_RESCHEDULE_DELAY_MS )\r
916     {\r
917         delta = TASKPOOL_JOB_RESCHEDULE_DELAY_MS; /* The job will be late... */\r
918     }\r
919 \r
920     /* Change the period of the task pools timer to be the period of this\r
921        timer. A precondition of this function is that this TimerEvent is the\r
922        timer event with the shortest deadline.\r
923     */\r
924     if( xTimerChangePeriod( timer, ( uint32_t ) delta, portMAX_DELAY ) == pdFAIL )\r
925     {\r
926         IotLogWarn( "Failed to re-arm timer for task pool" );\r
927     }\r
928 }\r
929 \r
930 /*-----------------------------------------------------------*/\r
931 \r
932 static void _timerCallback( TimerHandle_t xTimer )\r
933 {\r
934     _taskPool_t * pTaskPool = pvTimerGetTimerID( xTimer );\r
935 \r
936     configASSERT( pTaskPool );\r
937 \r
938     _taskPoolTimerEvent_t * pTimerEvent = NULL;\r
939     BaseType_t numberOfSchedules = 0;\r
940 \r
941     IotLogDebug( "Timer thread started for task pool %p.", pTaskPool );\r
942 \r
943     /* Attempt to lock the timer mutex. Return immediately if the mutex cannot be locked.\r
944      * If this mutex cannot be locked it means that another thread is manipulating the\r
945      * timeouts list, and will reset the timer to fire again, although it will be late.\r
946      */\r
947     if ( xSemaphoreTake( pTaskPool->xTimerEventMutex, 0 ) == pdPASS )\r
948     {\r
949 \r
950         /* Dispatch all deferred job whose timer expired, then reset the timer for the next\r
951          * job down the line. */\r
952         for( ; ; )\r
953         {\r
954             /* Peek the first event in the timer event list. */\r
955             IotLink_t * pLink = IotListDouble_PeekHead( &pTaskPool->timerEventsList );\r
956 \r
957             /* Check if the timer misfired for any reason.  */\r
958             if( pLink != NULL )\r
959             {\r
960                 /* Record the current time. */\r
961                 TickType_t now = xTaskGetTickCount();\r
962 \r
963                 /* Extract the job from its envelope. */\r
964                 pTimerEvent = IotLink_Container( _taskPoolTimerEvent_t, pLink, link );\r
965 \r
966                 /* Check if the first event should be processed now. */\r
967                 if( pTimerEvent->expirationTime <= now )\r
968                 {\r
969                     /*  Remove the timer event for immediate processing. */\r
970                     IotListDouble_Remove( &( pTimerEvent->link ) );\r
971                 }\r
972                 else\r
973                 {\r
974                     /* The first element in the timer queue shouldn't be processed yet.\r
975                      * Arm the timer for when it should be processed and leave altogether. */\r
976                     _rescheduleDeferredJobsTimer( pTaskPool->timer, pTimerEvent );\r
977                     break;\r
978                 }\r
979             }\r
980             /* If there are no timer events to process, terminate this thread. */\r
981             else\r
982             {\r
983                 IotLogDebug( "Finished scheduling deferred jobs." );\r
984                 break;\r
985             }\r
986 \r
987             /* Queue the job associated with the received timer event. */\r
988             _scheduleInternal( GET_JOB_FROM_TIMER( pTimerEvent ) );\r
989             numberOfSchedules++;\r
990             IotLogDebug( "Scheduled a job." );\r
991 \r
992             /* Free the timer event. */\r
993             memset( &( pTimerEvent->link ), 0, sizeof( pTimerEvent->link ) );\r
994         }\r
995 \r
996         /* Release mutex guarding the timer list. */\r
997         configASSERT( xSemaphoreGive( pTaskPool->xTimerEventMutex ) == pdPASS );\r
998 \r
999         for (; numberOfSchedules > 0; numberOfSchedules--)\r
1000         {\r
1001             /* Signal a worker task that a job was queued. */\r
1002             configASSERT( xSemaphoreGive( pTaskPool->dispatchSignal ) );\r
1003         }\r
1004     }\r
1005 \r
1006 }\r