1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** POSIX wrapper for THREADX */
16 /** */
17 /** */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 /* Include necessary system files. */
23
24 #include "tx_api.h" /* Threadx API */
25 #include "pthread.h" /* Posix API */
26 #include "px_int.h" /* Posix helper functions */
27 #include "tx_thread.h" /* Internal ThreadX thread management. */
28
29
30 /**************************************************************************/
31 /* */
32 /* FUNCTION RELEASE */
33 /* */
34 /* pthread_kill PORTABLE C */
35 /* 6.2.0 */
36 /* AUTHOR */
37 /* */
38 /* William E. Lamie, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* This function is used to request that a signal be delivered to */
43 /* the specified thread. */
44 /* */
45 /* INPUT */
46 /* */
47 /* thread_id Thread ID */
48 /* sig Signal */
49 /* */
50 /* OUTPUT */
51 /* */
52 /* 0 if successful */
53 /* Value in case of any error */
54 /* */
55 /* CALLS */
56 /* */
57 /* posix_in_thread_context Make sure caller is thread */
58 /* posix_internal_error Generic error Handler */
59 /* tx_event_flags_set */
60 /* posix_memory_allocate Create a byte pool for stack */
61 /* tx_thread_create */
62 /* tx_event_flags_delete */
63 /* posix_memory_release */
64 /* tx_thread_suspend */
65 /* _tx_thread_system_preempt_check */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* Application Code */
70 /* */
71 /* RELEASE HISTORY */
72 /* */
73 /* DATE NAME DESCRIPTION */
74 /* */
75 /* 06-02-2021 William E. Lamie Initial Version 6.1.7 */
76 /* 10-31-2022 Scott Larson Remove double parenthesis, */
77 /* update argument type, */
78 /* resulting in version 6.2.0 */
79 /* */
80 /**************************************************************************/
pthread_kill(ALIGN_TYPE thread_id,int sig)81 int pthread_kill(ALIGN_TYPE thread_id, int sig)
82 {
83
84 TX_INTERRUPT_SAVE_AREA
85
86 POSIX_TCB *target_thread;
87 POSIX_TCB *new_signal_thread;
88 VOID (*handler)(int);
89 INT status;
90 UINT retval;
91
92
93 /* Make sure we're calling this routine from a thread context. */
94 if (!posix_in_thread_context())
95 {
96 /* return POSIX error. */
97 posix_internal_error(444);
98 return(ERROR);
99 }
100
101 /* Determine if the desired signal is valid. */
102 if ((sig < 0) || (sig > SIGRTMAX))
103 {
104
105 /* Return an error. */
106 posix_set_pthread_errno(EINVAL);
107 return(ERROR);
108 }
109
110 /* Pickup target thread. */
111 target_thread = (POSIX_TCB *) thread_id;
112
113 /* Is it non-NULL? */
114 if (!target_thread)
115 {
116
117 /* Invalid target pthread object */
118 posix_errno= ESRCH;
119 posix_set_pthread_errno(ESRCH);
120 return(ERROR);
121 }
122
123 /* Pickup signal handler function pointer. */
124 handler = target_thread -> signals.signal_func[sig];
125
126 /* See if there is a signal handler setup for this signal. */
127 if (!handler)
128 {
129
130 /* No handler, just set/clear the event flags to handle the sigwait condition. */
131
132 /* Set the event flag corresponding the signal. */
133 tx_event_flags_set(&(target_thread -> signals.signal_event_flags), (((ULONG) 1) << sig), TX_OR);
134
135 /* Ensure the flag is left in a clear state. */
136 tx_event_flags_set(&(target_thread -> signals.signal_event_flags), ~(((ULONG) 1) << sig), TX_AND);
137
138 /* We are done, just return success. */
139 return(OK);
140 }
141
142 /* Now, let's look to see if the same signal is already pending. */
143 if (target_thread -> signals.signal_pending.signal_set & (((unsigned long) 1) << sig))
144 {
145
146 /* Yes, the same signal is already pending, just return. */
147 return(OK);
148 }
149
150 /* Now determine if the thread's signals are masked by pthread_sigmask. */
151 if (target_thread -> signals.signal_mask.signal_set & (((unsigned long) 1) << sig))
152 {
153
154 /* Yes, simply set the pending bit so we know that the signal must be activated later when the
155 signal mask for this signal is cleared. */
156 target_thread -> signals.signal_pending.signal_set = target_thread -> signals.signal_pending.signal_set | (((unsigned long) 1) << sig);
157 return(OK);
158 }
159
160 /* At this point we know that we need to create a new signal handler thread for processing this signal. */
161
162 /* Get a pthread control block for this new signal pthread */
163
164 /* Disable interrupts for protection. */
165 TX_DISABLE
166
167 /* Disable preemption temporarily. */
168 _tx_thread_preempt_disable++;
169
170 /* Allocate a POSIX thread control block. */
171 status = posix_allocate_pthread_t(&new_signal_thread);
172
173 /* Restore interrupts. */
174 TX_RESTORE
175
176 /* Make sure we got a new thread control block */
177 if ((status == ERROR) || (!new_signal_thread))
178 {
179
180 /* Disable interrupts. */
181 TX_DISABLE
182
183 /* Enable preemption. */
184 _tx_thread_preempt_disable--;
185
186 /* Restore interrupts. */
187 TX_RESTORE
188
189 /* Configuration/resource error. */
190 posix_set_pthread_errno(EAGAIN);
191 return(ERROR);
192 }
193
194 /* Inherit the stack size for the new signal thread. */
195 new_signal_thread -> stack_size = target_thread -> stack_size ;
196
197 /* Allocate memory for stack. */
198 status = posix_memory_allocate(new_signal_thread -> stack_size, &new_signal_thread -> stack_address);
199
200 /* problem allocating stack space */
201 if (status == ERROR)
202 {
203
204 /* Mark the previously allocated control block as available. */
205 new_signal_thread -> in_use = FALSE;
206
207 /* Disable interrupts. */
208 TX_DISABLE
209
210 /* Enable preemption. */
211 _tx_thread_preempt_disable--;
212
213 /* Restore interrupts. */
214 TX_RESTORE
215
216 /* Configuration/resource error. */
217 posix_set_pthread_errno(EAGAIN);
218 return(ERROR);
219 }
220
221 /* Inherit scheduling attributes from base thread. */
222 new_signal_thread -> current_priority = target_thread -> current_priority ;
223 new_signal_thread -> detach_state = target_thread -> detach_state ;
224 new_signal_thread -> inherit_sched = target_thread -> inherit_sched ;
225 new_signal_thread -> orig_priority = target_thread -> orig_priority ;
226 new_signal_thread -> sched_attr.sched_priority= target_thread -> sched_attr.sched_priority ;
227 new_signal_thread -> pthread_flags = target_thread -> pthread_flags ;
228 new_signal_thread -> sched_policy = target_thread -> sched_policy;
229 new_signal_thread -> is_joined_by = TX_FALSE;
230 new_signal_thread -> joined_by_pthreadID = TX_FALSE;
231 new_signal_thread -> is_joined_to = TX_FALSE;
232 new_signal_thread -> joined_to_pthreadID = TX_FALSE;
233 new_signal_thread -> cancel_state = PTHREAD_CANCEL_ENABLE;
234 new_signal_thread -> cancel_type = PTHREAD_CANCEL_DEFERRED;
235 new_signal_thread -> cancel_request = FALSE;
236 new_signal_thread -> value_ptr = NULL;
237
238 /* Increment the target thread's signal nesting depth. */
239 target_thread -> signals.signal_nesting_depth++;
240
241 /* Mark this signal as pending in the signal set. */
242 target_thread -> signals.signal_pending.signal_set = target_thread -> signals.signal_pending.signal_set | (((unsigned long) 1) << sig);
243
244 /* Mark the new thread as a signal thread, clear signal info, and setup links. */
245 new_signal_thread -> signals.signal_handler = TRUE;
246 new_signal_thread -> signals.signal_nesting_depth = target_thread -> signals.signal_nesting_depth;
247 new_signal_thread -> signals.signal_pending.signal_set = target_thread -> signals.signal_pending.signal_set;
248 new_signal_thread -> signals.saved_thread_state = ((TX_THREAD *) target_thread) -> tx_thread_state;
249 new_signal_thread -> signals.base_thread_ptr = target_thread;
250 new_signal_thread -> signals.next_signal_thread = target_thread -> signals.top_signal_thread;
251
252 /* Remember the top signal thread in the base thread. */
253 target_thread -> signals.top_signal_thread = new_signal_thread;
254
255 /* Now actually create and start the signal thread. */
256 retval = tx_thread_create( (TX_THREAD *) new_signal_thread,
257 "signal pthr",
258 internal_signal_dispatch,
259 (ULONG) sig,
260 new_signal_thread -> stack_address,
261 new_signal_thread -> stack_size,
262 (TX_LOWEST_PRIORITY - new_signal_thread -> current_priority + 1),
263 (TX_LOWEST_PRIORITY - new_signal_thread -> current_priority + 1),
264 new_signal_thread -> time_slice,
265 TX_AUTO_START);
266
267 /* See if ThreadX encountered an error */
268 if (retval)
269 {
270
271 /* Create an event flags group. */
272 tx_event_flags_delete(&(new_signal_thread -> signals.signal_event_flags));
273
274 /* Release the stack memory. */
275 posix_memory_release(new_signal_thread -> stack_address);
276
277 /* Mark the previously allocated control block as available. */
278 new_signal_thread -> in_use = FALSE;
279
280 /* Disable interrupts. */
281 TX_DISABLE
282
283 /* Enable preemption. */
284 _tx_thread_preempt_disable--;
285
286 /* Restore interrupts. */
287 TX_RESTORE
288
289 /* Internal error */
290 posix_error_handler(3333);
291 posix_set_pthread_errno(EACCES);
292 return(ERROR);
293 }
294
295 /* Disable interrupts. */
296 TX_DISABLE
297
298 /* Enable preemption. */
299 _tx_thread_preempt_disable--;
300
301 /* At this point, we need to suspend the target thread if we are the first signal handler to run. */
302 if (new_signal_thread -> signals.signal_nesting_depth == 1)
303 {
304
305 /* Restore interrupts. */
306 TX_RESTORE
307
308 /* Suspend the base thread so that it doesn't run again until all the signals have been processed. */
309 tx_thread_suspend((TX_THREAD *) target_thread);
310 }
311 else if (new_signal_thread -> signals.next_signal_thread)
312 {
313
314 /* Restore interrupts. */
315 TX_RESTORE
316
317 /* Make sure the current top level signal handler thread is suspended. */
318 tx_thread_suspend((TX_THREAD *) new_signal_thread -> signals.next_signal_thread);
319 }
320
321 /* At this point, the new signal has been set and the signal handler is ready for execution. */
322
323 /* Check for a preemption condition. */
324 _tx_thread_system_preempt_check();
325
326 /* Return success! */
327 return(OK);
328 }
329