xref: /relibc/pthreads-emb/pte_callUserDestroyRoutines.c (revision a12978ccca4c576f02971f5cfc2cbefe23e4daba)
1 /*
2  * pte_callUserDestroyRoutines.c
3  *
4  * Description:
5  * This translation unit implements routines which are private to
6  * the implementation and may be used throughout it.
7  *
8  * --------------------------------------------------------------------------
9  *
10  *      Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
11  *      Copyright(C) 2008 Jason Schmidlapp
12  *
13  *      Contact Email: jschmidlapp@users.sourceforge.net
14  *
15  *
16  *      Based upon Pthreads-win32 - POSIX Threads Library for Win32
17  *      Copyright(C) 1998 John E. Bossom
18  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
19  *
20  *      Contact Email: rpj@callisto.canberra.edu.au
21  *
22  *      The original list of contributors to the Pthreads-win32 project
23  *      is contained in the file CONTRIBUTORS.ptw32 included with the
24  *      source code distribution. The list can also be seen at the
25  *      following World Wide Web location:
26  *      http://sources.redhat.com/pthreads-win32/contributors.html
27  *
28  *      This library is free software; you can redistribute it and/or
29  *      modify it under the terms of the GNU Lesser General Public
30  *      License as published by the Free Software Foundation; either
31  *      version 2 of the License, or (at your option) any later version.
32  *
33  *      This library is distributed in the hope that it will be useful,
34  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
35  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36  *      Lesser General Public License for more details.
37  *
38  *      You should have received a copy of the GNU Lesser General Public
39  *      License along with this library in the file COPYING.LIB;
40  *      if not, write to the Free Software Foundation, Inc.,
41  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
42  */
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 
47 #include "pthread.h"
48 #include "implement.h"
49 
50 #include <pte_osal.h>
51 
52 #ifdef __cplusplus
53 #include <exception>
54 using std::terminate;
55 #endif
56 
57 void
pte_callUserDestroyRoutines(pthread_t thread)58 pte_callUserDestroyRoutines (pthread_t thread)
59 /*
60  * -------------------------------------------------------------------
61  * DOCPRIVATE
62  *
63  * This the routine runs through all thread keys and calls
64  * the destroy routines on the user's data for the current thread.
65  * It simulates the behaviour of POSIX Threads.
66  *
67  * PARAMETERS
68  *              thread
69  *                      an instance of pthread_t
70  *
71  * RETURNS
72  *              N/A
73  * -------------------------------------------------------------------
74  */
75 {
76   ThreadKeyAssoc * assoc;
77 
78   if (thread != NULL)
79     {
80       int assocsRemaining;
81       int iterations = 0;
82       pte_thread_t * sp = (pte_thread_t *) thread;
83 
84       /*
85        * Run through all Thread<-->Key associations
86        * for the current thread.
87        *
88        * Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times.
89        */
90       do
91         {
92           assocsRemaining = 0;
93           iterations++;
94 
95           (void) pthread_mutex_lock(&(sp->threadLock));
96           /*
97            * The pointer to the next assoc is stored in the thread struct so that
98            * the assoc destructor in pthread_key_delete can adjust it
99            * if it deletes this assoc. This can happen if we fail to acquire
100            * both locks below, and are forced to release all of our locks,
101            * leaving open the opportunity for pthread_key_delete to get in
102            * before us.
103            */
104           sp->nextAssoc = sp->keys;
105           (void) pthread_mutex_unlock(&(sp->threadLock));
106 
107           for (;;)
108             {
109               void * value;
110               pthread_key_t k;
111               void (*destructor) (void *);
112 
113               /*
114                * First we need to serialise with pthread_key_delete by locking
115                * both assoc guards, but in the reverse order to our convention,
116                * so we must be careful to avoid deadlock.
117                */
118               (void) pthread_mutex_lock(&(sp->threadLock));
119 
120               if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL)
121                 {
122                   /* Finished */
123                   pthread_mutex_unlock(&(sp->threadLock));
124                   break;
125                 }
126               else
127                 {
128                   /*
129                    * assoc->key must be valid because assoc can't change or be
130                    * removed from our chain while we hold at least one lock. If
131                    * the assoc was on our key chain then the key has not been
132                    * deleted yet.
133                    *
134                    * Now try to acquire the second lock without deadlocking.
135                    * If we fail, we need to relinquish the first lock and the
136                    * processor and then try to acquire them all again.
137                    */
138                   if (pthread_mutex_trylock(&(assoc->key->keyLock)) == EBUSY)
139                     {
140                       pthread_mutex_unlock(&(sp->threadLock));
141                       pte_osThreadSleep(1); // Ugly but necessary to avoid priority effects.
142                       /*
143                        * Go around again.
144                        * If pthread_key_delete has removed this assoc in the meantime,
145                        * sp->nextAssoc will point to a new assoc.
146                        */
147                       continue;
148                     }
149                 }
150 
151               /* We now hold both locks */
152 
153               sp->nextAssoc = assoc->nextKey;
154 
155               /*
156                * Key still active; pthread_key_delete
157                * will block on these same mutexes before
158                * it can release actual key; therefore,
159                * key is valid and we can call the destroy
160                * routine;
161                */
162               k = assoc->key;
163               destructor = k->destructor;
164               value = pte_osTlsGetValue(k->key);
165               pte_osTlsSetValue (k->key, NULL);
166 
167               // Every assoc->key exists and has a destructor
168               if (value != NULL && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS)
169                 {
170                   /*
171                    * Unlock both locks before the destructor runs.
172                    * POSIX says pthread_key_delete can be run from destructors,
173                    * and that probably includes with this key as target.
174                    * pthread_setspecific can also be run from destructors and
175                    * also needs to be able to access the assocs.
176                    */
177                   (void) pthread_mutex_unlock(&(sp->threadLock));
178                   (void) pthread_mutex_unlock(&(k->keyLock));
179 
180                   assocsRemaining++;
181 
182 #ifdef __cplusplus
183 
184                   try
185                     {
186                       /*
187                        * Run the caller's cleanup routine.
188                        */
189                       destructor (value);
190                     }
191                   catch (...)
192                     {
193                       /*
194                        * A system unexpected exception has occurred
195                        * running the user's destructor.
196                        * We get control back within this block in case
197                        * the application has set up it's own terminate
198                        * handler. Since we are leaving the thread we
199                        * should not get any internal pthreads
200                        * exceptions.
201                        */
202                       terminate ();
203                     }
204 
205 #else /* __cplusplus */
206 
207                   /*
208                   * Run the caller's cleanup routine.
209                   */
210                   destructor (value);
211 
212 #endif /* __cplusplus */
213 
214                 }
215               else
216                 {
217                   /*
218                    * Remove association from both the key and thread chains
219                    * and reclaim it's memory resources.
220                    */
221                   pte_tkAssocDestroy (assoc);
222                   (void) pthread_mutex_unlock(&(sp->threadLock));
223                   (void) pthread_mutex_unlock(&(k->keyLock));
224                 }
225             }
226         }
227       while (assocsRemaining);
228     }
229 }				/* pte_callUserDestroyRoutines */
230