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-2021 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_get_queue(&worker->event_queue, &dispatch_info, CY_RTOS_NEVER_TIMEOUT,
68 false);
69 if (result == CY_RSLT_SUCCESS)
70 {
71 if (dispatch_info.work_func != NULL)
72 {
73 dispatch_info.work_func(dispatch_info.arg);
74 }
75 else
76 {
77 break;
78 }
79 }
80 }
81 cy_rtos_exit_thread();
82 }
83
84
85 //--------------------------------------------------------------------------------------------------
86 // cy_worker_thread_create
87 //--------------------------------------------------------------------------------------------------
cy_worker_thread_create(cy_worker_thread_info_t * new_worker,const cy_worker_thread_params_t * params)88 cy_rslt_t cy_worker_thread_create(cy_worker_thread_info_t* new_worker,
89 const cy_worker_thread_params_t* params)
90 {
91 // Param check
92 CY_ASSERT((params != NULL) && (new_worker != NULL));
93 CY_ASSERT((params->stack == NULL) || ((params->stack != NULL) && (params->stack_size != 0)));
94
95 // Start with a clean structure
96 memset(new_worker, 0, sizeof(cy_worker_thread_info_t));
97
98 cy_rslt_t result = cy_rtos_init_queue(&new_worker->event_queue,
99 (params->num_entries != 0)
100 ? params->num_entries
101 : CY_WORKER_DEFAULT_ENTRIES,
102 sizeof(cy_worker_dispatch_info_t));
103 if (result == CY_RSLT_SUCCESS)
104 {
105 new_worker->state = CY_WORKER_THREAD_VALID;
106 result = cy_rtos_create_thread(&new_worker->thread,
107 cy_worker_thread_func,
108 (params->name != NULL)
109 ? params->name
110 : CY_WORKER_THREAD_DEFAULT_NAME,
111 params->stack,
112 params->stack_size,
113 params->priority,
114 (cy_thread_arg_t)new_worker);
115
116 if (result != CY_RSLT_SUCCESS)
117 {
118 new_worker->state = CY_WORKER_THREAD_INVALID;
119 cy_rtos_deinit_queue(&new_worker->event_queue);
120 }
121 }
122 return result;
123 }
124
125
126 //--------------------------------------------------------------------------------------------------
127 // cy_worker_thread_delete
128 //--------------------------------------------------------------------------------------------------
cy_worker_thread_delete(cy_worker_thread_info_t * old_worker)129 cy_rslt_t cy_worker_thread_delete(cy_worker_thread_info_t* old_worker)
130 {
131 cy_rslt_t result = CY_RSLT_SUCCESS;
132
133 uint32_t state = cyhal_system_critical_section_enter();
134 if (old_worker->state != CY_WORKER_THREAD_INVALID)
135 {
136 // Don't allow terminating while cy_rtos_put_queue is running
137 if (old_worker->state == CY_WORKER_THREAD_VALID)
138 {
139 // A terminating event is queued that will break the while loop
140 // Note that this is ok because thread enqueue function will not
141 // allow NULL as a valid value for the work function.
142 old_worker->state = CY_WORKER_THREAD_TERMINATING;
143 cyhal_system_critical_section_exit(state);
144 cy_worker_dispatch_info_t dispatch_info = { NULL, NULL };
145 bool in_isr = is_in_isr();
146 result = cy_rtos_put_queue(&old_worker->event_queue, &dispatch_info, 0, in_isr);
147 if (result != CY_RSLT_SUCCESS)
148 {
149 // Could not enqueue termination task, return to valid state
150 state = cyhal_system_critical_section_enter();
151 old_worker->state = CY_WORKER_THREAD_VALID;
152 cyhal_system_critical_section_exit(state);
153
154 return result;
155 }
156 }
157
158 if (old_worker->state != CY_WORKER_THREAD_JOIN_COMPLETE)
159 {
160 cyhal_system_critical_section_exit(state);
161 result = cy_rtos_join_thread(&old_worker->thread);
162 if (result != CY_RSLT_SUCCESS)
163 {
164 return result;
165 }
166 state = cyhal_system_critical_section_enter();
167 old_worker->state = CY_WORKER_THREAD_JOIN_COMPLETE;
168 }
169
170 if (old_worker->state != CY_WORKER_THREAD_INVALID)
171 {
172 cyhal_system_critical_section_exit(state);
173 result = cy_rtos_deinit_queue(&old_worker->event_queue);
174 if (result != CY_RSLT_SUCCESS)
175 {
176 return result;
177 }
178 state = cyhal_system_critical_section_enter();
179 old_worker->state = CY_WORKER_THREAD_INVALID;
180 }
181 }
182
183 cyhal_system_critical_section_exit(state);
184 return result;
185 }
186
187
188 //--------------------------------------------------------------------------------------------------
189 // cy_worker_thread_enqueue
190 //--------------------------------------------------------------------------------------------------
cy_worker_thread_enqueue(cy_worker_thread_info_t * worker_info,cy_worker_thread_func_t * work_func,void * arg)191 cy_rslt_t cy_worker_thread_enqueue(cy_worker_thread_info_t* worker_info,
192 cy_worker_thread_func_t* work_func, void* arg)
193 {
194 CY_ASSERT(worker_info != NULL);
195 CY_ASSERT(work_func != NULL);
196
197 uint32_t state = cyhal_system_critical_section_enter();
198 if ((worker_info->state != CY_WORKER_THREAD_VALID) &&
199 (worker_info->state != CY_WORKER_THREAD_ENQUEUING))
200 {
201 cyhal_system_critical_section_exit(state);
202 return CY_WORKER_THREAD_ERR_THREAD_INVALID;
203 }
204 worker_info->enqueue_count++;
205 worker_info->state = CY_WORKER_THREAD_ENQUEUING;
206 cyhal_system_critical_section_exit(state);
207
208 cy_worker_dispatch_info_t dispatch_info = { work_func, arg };
209 // Queue an event to be run by the worker thread
210 bool in_isr = is_in_isr();
211 cy_rslt_t result = cy_rtos_put_queue(&worker_info->event_queue, &dispatch_info, 0, in_isr);
212
213 state = cyhal_system_critical_section_enter();
214 worker_info->enqueue_count--;
215 if (worker_info->enqueue_count == 0)
216 {
217 worker_info->state = CY_WORKER_THREAD_VALID;
218 }
219 cyhal_system_critical_section_exit(state);
220
221 return result;
222 }
223
224
225 #if defined(__cplusplus)
226 }
227 #endif
228
229 #endif // defined(CY_USING_HAL)
230