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