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 
28 
29 /**************************************************************************/
30 /*                                                                        */
31 /*  FUNCTION                                               RELEASE        */
32 /*                                                                        */
33 /*    pthread_create                                      PORTABLE C      */
34 /*                                                           6.2.0        */
35 /*  AUTHOR                                                                */
36 /*                                                                        */
37 /*    William E. Lamie, Microsoft Corporation                             */
38 /*                                                                        */
39 /*  DESCRIPTION                                                           */
40 /*                                                                        */
41 /*    This creates a new thread with attributes specified by attr within  */
42 /*    a process.If the attr is NULL,then default attributes are used.     */
43 /*    Upon successful completion, pthread_create() stores the ID of the   */
44 /*    created thread in the location referenced by thread.                */
45 /*    The thread is created executing start_routine with arg as its sole  */
46 /*    argument.                                                           */
47 /*    Initially, threads are created from within a process. Once created, */
48 /*    threads are peers, and may create other threads. Note that an       */
49 /*    "initial thread" exists by default  which runs 'main'               */
50 /*    If pthread_create( ) fails,no new thread is created and the contents*/
51 /*    of the location referenced by thread are undefined.                 */
52 /*                                                                        */
53 /*  INPUT                                                                 */
54 /*                                                                        */
55 /*    thread                      place to store newly created thread ID  */
56 /*                                Each thread in a process is uniquely    */
57 /*                                identified during its lifetime by a     */
58 /*                                value of type pthread_t called as       */
59 /*                                thread ID.                              */
60 /*                                                                        */
61 /*    attr                        attributes object , NULL = Default attr.*/
62 /*    start_routine               thread start up routine                 */
63 /*    arg                         arguments to start up routine by ref.   */
64 /*                                                                        */
65 /*  OUTPUT                                                                */
66 /*                                                                        */
67 /*    zero                        If successful                           */
68 /*    error number                If fails                                */
69 /*                                pthread_create() function will fail if: */
70 /*                                [EAGAIN] system lacked the necessary    */
71 /*                                         resources to create a thread,  */
72 /*                                         or the system-imposed limit on */
73 /*                                         the number of pthreads in      */
74 /*                                         a process PTHREAD_THREADS_MAX  */
75 /*                                         would be exceeded.             */
76 /*                                [EINVAL] value specified by attr is     */
77 /*                                         invalid.                       */
78 /*                                [EPERM]  The caller does not have       */
79 /*                                         appropriate permission to set  */
80 /*                                         the required scheduling        */
81 /*                                         parameters or scheduling policy*/
82 /*                                                                        */
83 /*                      This call will not return an error code of [EINTR]*/
84 /*                                                                        */
85 /*                                                                        */
86 /*  CALLS                                                                 */
87 /*                                                                        */
88 /*    posix_in_thread_context         To check calling context.           */
89 /*    posix_internal_error            Internal error                      */
90 /*    posix_allocate_pthread_t        Allocate control block for pthread  */
91 /*                                                                        */
92 /*  CALLED BY                                                             */
93 /*                                                                        */
94 /*    Application Code                                                    */
95 /*                                                                        */
96 /*  RELEASE HISTORY                                                       */
97 /*                                                                        */
98 /*    DATE              NAME                      DESCRIPTION             */
99 /*                                                                        */
100 /*  06-02-2021      William E. Lamie        Initial Version 6.1.7         */
101 /*  10-31-2022      Scott Larson            Add 64-bit support,           */
102 /*                                            remove double parenthesis,  */
103 /*                                            resulting in version 6.2.0  */
104 /*                                                                        */
105 /**************************************************************************/
pthread_create(pthread_t * thread,pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)106 INT pthread_create (pthread_t *thread, pthread_attr_t *attr,
107                    void *(*start_routine)(void*),void *arg)
108 {
109 
110 TX_INTERRUPT_SAVE_AREA
111 
112 
113 TX_THREAD        *thread_ptr;
114 POSIX_TCB        *pthread_ptr, *current_thread_ptr;
115 INT               status,retval;
116 
117 
118 
119     /* Make sure we're calling this routine from a thread context.  */
120     if (!posix_in_thread_context())
121     {
122         /* return POSIX error.  */
123         posix_internal_error(444);
124     }
125 
126     /* Disable interrupts.  */
127 
128 
129     /*  check for any pthread_t attr suggested */
130     if (!attr)
131     {
132         /* no attributes passed so assume default attributes */
133         attr = &(posix_default_pthread_attr);
134     }
135     else
136     {
137         /* Check attributes passed , check for validity */
138         if ( (attr->inuse) == TX_FALSE)
139         {
140             /* attributes passed are not valid, return with an error */
141             posix_errno = EINVAL;
142             posix_set_pthread_errno(EINVAL);
143             return(EINVAL);
144         }
145     }
146 
147 
148     /* Get a pthread control block for this new pthread */
149     TX_DISABLE
150     status = posix_allocate_pthread_t(&pthread_ptr);
151     TX_RESTORE
152     /* Make sure we got a Thread control block */
153     if ((status == ERROR) || (!pthread_ptr))
154     {
155         /* Configuration/resource error.  */
156         return(EAGAIN);
157     }
158 
159     if(attr->inherit_sched==PTHREAD_INHERIT_SCHED)
160     {
161         /* get current thread tcb */
162         current_thread_ptr=posix_tid2tcb(pthread_self());
163 
164         /* copy scheduling attributes */
165         pthread_ptr->current_priority = current_thread_ptr->current_priority ;
166         pthread_ptr->detach_state = current_thread_ptr->detach_state ;
167         pthread_ptr->inherit_sched = current_thread_ptr->inherit_sched ;
168         pthread_ptr->orig_priority = current_thread_ptr->orig_priority ;
169         pthread_ptr->sched_attr.sched_priority= current_thread_ptr->sched_attr.sched_priority ;
170         pthread_ptr->pthread_flags = current_thread_ptr->pthread_flags ;
171         pthread_ptr->sched_policy = current_thread_ptr->sched_policy;
172         pthread_ptr->stack_size = current_thread_ptr->stack_size ;
173         pthread_ptr->stack_address = NULL;                          /* will allocate our own stack */
174         pthread_ptr->threshold = pthread_ptr->current_priority ;    /* preemption threshold */
175     }
176     else  /* assume PTHREAD_EXPLICIT_SCHED */
177     {
178 
179         /* copy pthread-attr to TCB */
180         posix_copy_pthread_attr(pthread_ptr,attr);
181 
182         /* preemption treshold */
183         pthread_ptr->threshold = pthread_ptr->current_priority ;
184         /* scheduling */
185         if(pthread_ptr->sched_policy==SCHED_RR) pthread_ptr->time_slice = SCHED_RR_TIME_SLICE;
186         else pthread_ptr->time_slice = 0;
187     }
188 
189     /* Now set up pthread initial parameters */
190 
191     pthread_ptr->entry_parameter = arg;
192     pthread_ptr->start_routine = start_routine;
193     /* Newly created pthread not joined by anybody! */
194     pthread_ptr->is_joined_by = TX_FALSE;
195     pthread_ptr->joined_by_pthreadID =TX_FALSE;
196     /* Newly created pthread not yet joined to any other pthread */
197     pthread_ptr->is_joined_to = TX_FALSE;
198     pthread_ptr->joined_to_pthreadID = TX_FALSE;
199 
200 
201     /* Allow cancel */
202     pthread_ptr->cancel_state = PTHREAD_CANCEL_ENABLE;
203     pthread_ptr->cancel_type = PTHREAD_CANCEL_DEFERRED;
204     pthread_ptr->cancel_request = FALSE;
205 
206     pthread_ptr->value_ptr = NULL;
207 
208 
209     /* default stack allocation */
210     if((attr->stack_address)==NULL)
211     {
212         pthread_ptr->stack_size = attr->stack_size ;
213         status = posix_memory_allocate( pthread_ptr->stack_size, &(pthread_ptr->stack_address));
214 
215         /* problem allocating stack space */
216         if (status == ERROR)
217         {
218           /* Configuration/resource error.  */
219           return(EAGAIN);
220         }
221 
222     }
223 
224     /* Create an event flags group for sigwait.  */
225     retval =  tx_event_flags_create(&(pthread_ptr -> signals.signal_event_flags), "posix sigwait events");
226 
227    /* Get the thread info from the TCB.  */
228     thread_ptr = posix_tcb2thread(pthread_ptr);
229 
230    /* Now actually create and start the thread.  */
231    /* convert Posix priorities to Threadx priority */
232     retval += tx_thread_create(thread_ptr,
233                                "pthr",
234                                posix_thread_wrapper,
235                                (ULONG)(ALIGN_TYPE)pthread_ptr,
236                                pthread_ptr->stack_address,
237                                pthread_ptr->stack_size,
238                                (TX_LOWEST_PRIORITY - pthread_ptr->current_priority + 1),
239                                (TX_LOWEST_PRIORITY - pthread_ptr->threshold + 1),
240                                pthread_ptr->time_slice,
241                                TX_AUTO_START);
242 
243     TX_THREAD_EXTENSION_PTR_SET(thread_ptr, pthread_ptr)
244 
245     /* See if ThreadX encountered an error */
246     if (retval)
247     {
248         /* Internal error */
249         posix_error_handler(3333);
250         retval = EACCES;
251     }
252     else
253     {
254         /* Everything is fine.  */
255         /* create a pthreadID by type casting POSIX_TCB into pthread_t */
256         pthread_ptr->pthreadID = (pthread_t )pthread_ptr;
257         *thread = pthread_ptr->pthreadID ;
258         retval = OK;
259     }
260 
261     /* Everything worked fine if we got here */
262     return(retval);
263 
264 }
265