1 /***********************************************************************************************//**
2  * \file cy_worker_thread.c
3  *
4  * \brief
5  * Provides implementation for functions that allow creating/deleting worker
6  * threads and deferring work to a worker thread.
7  ***************************************************************************************************
8  * \copyright
9  * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
10  * an affiliate of Cypress Semiconductor Corporation
11  *
12  * SPDX-License-Identifier: Apache-2.0
13  *
14  * Licensed under the Apache License, Version 2.0 (the "License");
15  * you may not use this file except in compliance with the License.
16  * You may obtain a copy of the License at
17  *
18  *     http://www.apache.org/licenses/LICENSE-2.0
19  *
20  * Unless required by applicable law or agreed to in writing, software
21  * distributed under the License is distributed on an "AS IS" BASIS,
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23  * See the License for the specific language governing permissions and
24  * limitations under the License.
25  **************************************************************************************************/
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "cy_worker_thread.h"
31 #include "cy_utils.h"
32 #include "cyabs_rtos_internal.h"
33 #if defined(CY_USING_HAL)
34 #include "cyhal_system.h"
35 
36 #if defined(__cplusplus)
37 extern "C"
38 {
39 #endif
40 
41 // Info for dispatching a function call
42 typedef struct
43 {
44     cy_worker_thread_func_t* work_func;
45     void*                    arg;
46 } cy_worker_dispatch_info_t;
47 
48 
49 //--------------------------------------------------------------------------------------------------
50 // cy_worker_thread_func
51 //
52 /* Worker Thread to dispatch the events that added to the event queue.
53  * It will wait indefinitely for a item to be queued and will terminate
54  * when the NULL work function is queued by delete. It will process all
55  * events before the terminating event.
56  * @param   arg : pointer to @ref cy_worker_thread_info_t
57  */
58 //--------------------------------------------------------------------------------------------------
cy_worker_thread_func(cy_thread_arg_t arg)59 static void cy_worker_thread_func(cy_thread_arg_t arg)
60 {
61     cy_rslt_t                 result;
62     cy_worker_dispatch_info_t dispatch_info;
63     cy_worker_thread_info_t*  worker = (cy_worker_thread_info_t*)arg;
64 
65     while (1)
66     {
67         result = cy_rtos_queue_get(&worker->event_queue, &dispatch_info, CY_RTOS_NEVER_TIMEOUT);
68         if (result == CY_RSLT_SUCCESS)
69         {
70             if (dispatch_info.work_func != NULL)
71             {
72                 dispatch_info.work_func(dispatch_info.arg);
73             }
74             else
75             {
76                 break;
77             }
78         }
79     }
80     cy_rtos_thread_exit();
81 }
82 
83 
84 //--------------------------------------------------------------------------------------------------
85 // cy_worker_thread_create
86 //--------------------------------------------------------------------------------------------------
cy_worker_thread_create(cy_worker_thread_info_t * new_worker,const cy_worker_thread_params_t * params)87 cy_rslt_t cy_worker_thread_create(cy_worker_thread_info_t* new_worker,
88                                   const cy_worker_thread_params_t* params)
89 {
90     // Param check
91     CY_ASSERT((params != NULL) && (new_worker != NULL));
92     CY_ASSERT((params->stack == NULL) || ((params->stack != NULL) && (params->stack_size != 0)));
93 
94     // Start with a clean structure
95     memset(new_worker, 0, sizeof(cy_worker_thread_info_t));
96 
97     cy_rslt_t result = cy_rtos_queue_init(&new_worker->event_queue,
98                                           (params->num_entries != 0)
99                                           ? params->num_entries
100                                           : CY_WORKER_DEFAULT_ENTRIES,
101                                           sizeof(cy_worker_dispatch_info_t));
102     if (result == CY_RSLT_SUCCESS)
103     {
104         new_worker->state = CY_WORKER_THREAD_VALID;
105         result            = cy_rtos_thread_create(&new_worker->thread,
106                                                   cy_worker_thread_func,
107                                                   (params->name != NULL)
108                                                   ? params->name
109                                                   : CY_WORKER_THREAD_DEFAULT_NAME,
110                                                   params->stack,
111                                                   params->stack_size,
112                                                   params->priority,
113                                                   (cy_thread_arg_t)new_worker);
114 
115         if (result != CY_RSLT_SUCCESS)
116         {
117             new_worker->state = CY_WORKER_THREAD_INVALID;
118             cy_rtos_queue_deinit(&new_worker->event_queue);
119         }
120     }
121     return result;
122 }
123 
124 
125 //--------------------------------------------------------------------------------------------------
126 // cy_worker_thread_delete
127 //--------------------------------------------------------------------------------------------------
cy_worker_thread_delete(cy_worker_thread_info_t * old_worker)128 cy_rslt_t cy_worker_thread_delete(cy_worker_thread_info_t* old_worker)
129 {
130     cy_rslt_t result = CY_RSLT_SUCCESS;
131 
132     uint32_t state = cyhal_system_critical_section_enter();
133     if (old_worker->state != CY_WORKER_THREAD_INVALID)
134     {
135         // Don't allow terminating while cy_rtos_put_queue is running
136         if (old_worker->state == CY_WORKER_THREAD_VALID)
137         {
138             // A terminating event is queued that will break the while loop
139             // Note that this is ok because thread enqueue function will not
140             // allow NULL as a valid value for the work function.
141             old_worker->state = CY_WORKER_THREAD_TERMINATING;
142             cyhal_system_critical_section_exit(state);
143             cy_worker_dispatch_info_t dispatch_info = { NULL, NULL };
144             result = cy_rtos_queue_put(&old_worker->event_queue, &dispatch_info, 0);
145             if (result != CY_RSLT_SUCCESS)
146             {
147                 // Could not enqueue termination task, return to valid state
148                 state = cyhal_system_critical_section_enter();
149                 old_worker->state = CY_WORKER_THREAD_VALID;
150                 cyhal_system_critical_section_exit(state);
151 
152                 return result;
153             }
154         }
155 
156         if (old_worker->state != CY_WORKER_THREAD_JOIN_COMPLETE)
157         {
158             cyhal_system_critical_section_exit(state);
159             result = cy_rtos_thread_join(&old_worker->thread);
160             if (result != CY_RSLT_SUCCESS)
161             {
162                 return result;
163             }
164             state = cyhal_system_critical_section_enter();
165             old_worker->state = CY_WORKER_THREAD_JOIN_COMPLETE;
166         }
167 
168         if (old_worker->state != CY_WORKER_THREAD_INVALID)
169         {
170             cyhal_system_critical_section_exit(state);
171             result = cy_rtos_queue_deinit(&old_worker->event_queue);
172             if (result != CY_RSLT_SUCCESS)
173             {
174                 return result;
175             }
176             state = cyhal_system_critical_section_enter();
177             old_worker->state = CY_WORKER_THREAD_INVALID;
178         }
179     }
180 
181     cyhal_system_critical_section_exit(state);
182     return result;
183 }
184 
185 
186 //--------------------------------------------------------------------------------------------------
187 // cy_worker_thread_enqueue
188 //--------------------------------------------------------------------------------------------------
cy_worker_thread_enqueue(cy_worker_thread_info_t * worker_info,cy_worker_thread_func_t * work_func,void * arg)189 cy_rslt_t cy_worker_thread_enqueue(cy_worker_thread_info_t* worker_info,
190                                    cy_worker_thread_func_t* work_func, void* arg)
191 {
192     CY_ASSERT(worker_info != NULL);
193     CY_ASSERT(work_func != NULL);
194 
195     uint32_t state = cyhal_system_critical_section_enter();
196     if ((worker_info->state != CY_WORKER_THREAD_VALID) &&
197         (worker_info->state != CY_WORKER_THREAD_ENQUEUING))
198     {
199         cyhal_system_critical_section_exit(state);
200         return CY_WORKER_THREAD_ERR_THREAD_INVALID;
201     }
202     worker_info->enqueue_count++;
203     worker_info->state = CY_WORKER_THREAD_ENQUEUING;
204     cyhal_system_critical_section_exit(state);
205 
206     cy_worker_dispatch_info_t dispatch_info = { work_func, arg };
207     // Queue an event to be run by the worker thread
208     cy_rslt_t result = cy_rtos_queue_put(&worker_info->event_queue, &dispatch_info, 0);
209 
210     state = cyhal_system_critical_section_enter();
211     worker_info->enqueue_count--;
212     if (worker_info->enqueue_count == 0)
213     {
214         worker_info->state = CY_WORKER_THREAD_VALID;
215     }
216     cyhal_system_critical_section_exit(state);
217 
218     return result;
219 }
220 
221 
222 #if defined(__cplusplus)
223 }
224 #endif
225 
226 #endif // defined(CY_USING_HAL)
227