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