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