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