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_join                                        PORTABLE C      */
34 /*                                                           6.1.7        */
35 /*  AUTHOR                                                                */
36 /*                                                                        */
37 /*    William E. Lamie, Microsoft Corporation                             */
38 /*                                                                        */
39 /*  DESCRIPTION                                                           */
40 /*                                                                        */
41 /*    This function shall suspend execution of the calling thread until   */
42 /*    the target thread terminates, unless the target thread has already  */
43 /*    terminated. On return from a successful pthread_join ( ) call with  */
44 /*    a non-NULL value_ptr argument, the value passed to pthread_exit( )  */
45 /*    by the terminating thread shall be made available in the location   */
46 /*    referenced by value_ptr. When a pthread_join returns successfully,  */
47 /*    the target thread has been terminated. The results of multiple      */
48 /*    simultaneous calls to pthread_join specifying the same target thread*/
49 /*    are undefined. If the thread calling pthread_join is canceled, then */
50 /*    the target thread shall not be detached.                            */
51 /*    Eventually, you should call pthread_join() or pthread_detach() for  */
52 /*    every thread that is created joinable (with a detachstate of        */
53 /*    PTHREAD_CREATE_JOINABLE)so that the system can reclaim all resources*/
54 /*    associated with the thread. Failure to join to or detach joinable   */
55 /*    threads will result in memory and other resource leaks until the    */
56 /*    process ends. If thread doesn't represent a valid undetached thread,*/
57 /*    pthread_detach() will return ESRCH.                                 */
58 /*                                                                        */
59 /*    Note: this function must be called from a POSIX context; if it is   */
60 /*    called from  ThreadX context an error is returned.                  */
61 /*                                                                        */
62 /*  INPUT                                                                 */
63 /*                                                                        */
64 /*    thread                      pthread handle to the target thread     */
65 /*    value_ptr                   To receive return value of terminating  */
66 /*                                thread                                  */
67 /*                                                                        */
68 /*  OUTPUT                                                                */
69 /*                                                                        */
70 /*    zero                        If successful                           */
71 /*    error number                If fails                                */
72 /*                                                                        */
73 /*  CALLS                                                                 */
74 /*                                                                        */
75 /*                                                                        */
76 /*  CALLED BY                                                             */
77 /*                                                                        */
78 /*    Application Code                                                    */
79 /*                                                                        */
80 /*  RELEASE HISTORY                                                       */
81 /*                                                                        */
82 /*    DATE              NAME                      DESCRIPTION             */
83 /*                                                                        */
84 /*  06-02-2021     William E. Lamie         Initial Version 6.1.7         */
85 /*                                                                        */
86 /**************************************************************************/
pthread_join(pthread_t thread,VOID ** value_ptr)87 INT pthread_join(pthread_t thread, VOID **value_ptr)
88 
89 {
90     TX_INTERRUPT_SAVE_AREA
91 
92         POSIX_TCB   *current_ptr, *target_ptr;
93     TX_THREAD   *target_thread;
94 
95 
96     /* Get the TCB for the currently running pthread   */
97     current_ptr = posix_thread2tcb(tx_thread_identify());
98 
99     /* Make sure that a TCB was returned.  */
100     if (!current_ptr)
101     {
102         posix_errno = ECANCELED;
103         posix_set_pthread_errno(ECANCELED);
104         return(ECANCELED);
105     }
106 
107     /* Check trying to join to self ! */
108     if ( current_ptr->pthreadID == thread)
109     {
110         posix_errno= EDEADLK;
111         posix_set_pthread_errno(EDEADLK);
112         return(EDEADLK);
113     }
114     /* Check if calling thread is already joined to other thread */
115 
116     if ( current_ptr->is_joined_to == TX_TRUE)
117     {
118         posix_errno= EINVAL;
119         posix_set_pthread_errno(EINVAL);
120         return(EINVAL);
121     }
122     /* Sure, calling pthread can be joined to target pthread */
123 
124     target_thread = posix_tid2thread(thread);
125 
126     if (!target_thread)
127     {
128         /* Invalid target pthread object */
129         posix_errno= ESRCH;
130         posix_set_pthread_errno(ESRCH);
131         return(ESRCH);
132     }
133 
134     if ( (target_thread->tx_thread_state == TX_COMPLETED) || (target_thread->tx_thread_state == TX_TERMINATED) )
135     {
136         /* but target pthread is already terminated */
137         /* return the return value of the terminated thread */
138         target_ptr = posix_tid2tcb(thread);
139         if(value_ptr)*value_ptr = target_ptr->value_ptr;
140         return(OK);
141     }
142 
143     /* Now check the target thread, whether it is already joined to any other thread? */
144     target_ptr = posix_tid2tcb(thread);
145 
146     /* Now check the target thread, whether it is already joined to any other thread? */
147     if (target_ptr->is_joined_by == TX_TRUE)
148     {
149         /* but target pthread is already terminated */
150         posix_errno= EINVAL;
151         posix_set_pthread_errno(EINVAL);
152         return(EINVAL);
153     }
154 
155     /* check joinability of target thread */
156     if ( target_ptr->detach_state != PTHREAD_CREATE_JOINABLE)
157     {
158         posix_errno= EINVAL;
159         posix_set_pthread_errno(EINVAL);
160         return(EINVAL);
161     }
162 
163     /* Now it is Okay to join */
164 
165     TX_DISABLE
166 
167     /* declare that this calling pthread is joined to other pthread */
168         current_ptr->is_joined_to  = TX_TRUE;
169     /* register the target pthread */
170     current_ptr->joined_to_pthreadID = thread;
171     /* declare that the target pthread is joined by calling pthread */
172     target_ptr->is_joined_by = TX_TRUE;
173     /* and register the caller pthread with target pthread */
174     target_ptr->joined_by_pthreadID = current_ptr->pthreadID ;
175 
176     TX_RESTORE
177 
178     /* Now calling pthread will suspend itself and wait till target pthread exits */
179         tx_thread_suspend ( &(current_ptr->thread_info));
180     /* target pthread exited and thus current pthread will resume now */
181     /* store return value if value_ptr is valid */
182     if(value_ptr)*value_ptr = target_ptr->value_ptr;
183     return(OK);
184 }
185