1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Copyright by The HDF Group. *
3 * Copyright by the Board of Trustees of the University of Illinois. *
4 * All rights reserved. *
5 * *
6 * This file is part of HDF5. The full HDF5 copyright notice, including *
7 * terms governing use, modification, and redistribution, is contained in *
8 * the COPYING file, which can be found at the root of the source code *
9 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
10 * If you do not have access to either file, you may request a copy from *
11 * help@hdfgroup.org. *
12 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13
14 /* private headers */
15 #include "H5private.h" /*library */
16 #include "H5Eprivate.h" /*error handling */
17 #include "H5MMprivate.h" /*memory management functions */
18
19 #ifdef H5_HAVE_THREADSAFE
20
21 /* Module specific data structures */
22
23 /* cancelability structure */
24 typedef struct H5TS_cancel_struct {
25 int previous_state;
26 unsigned int cancel_count;
27 } H5TS_cancel_t;
28
29 /* Global variable definitions */
30 #ifdef H5_HAVE_WIN_THREADS
31 H5TS_once_t H5TS_first_init_g;
32 #else /* H5_HAVE_WIN_THREADS */
33 H5TS_once_t H5TS_first_init_g = PTHREAD_ONCE_INIT;
34 #endif /* H5_HAVE_WIN_THREADS */
35 H5TS_key_t H5TS_errstk_key_g;
36 H5TS_key_t H5TS_funcstk_key_g;
37 H5TS_key_t H5TS_apictx_key_g;
38 H5TS_key_t H5TS_cancel_key_g;
39
40
41 /*--------------------------------------------------------------------------
42 * NAME
43 * H5TS_key_destructor
44 *
45 * USAGE
46 * H5TS_key_destructor()
47 *
48 * RETURNS
49 *
50 * DESCRIPTION
51 * Frees the memory for a key. Called by each thread as it exits.
52 * Currently all the thread-specific information for all keys are simple
53 * structures allocated with malloc, so we can free them all uniformly.
54 *
55 * PROGRAMMER: Quincey Koziol
56 * February 7, 2003
57 *
58 *--------------------------------------------------------------------------
59 */
60 static void
H5TS_key_destructor(void * key_val)61 H5TS_key_destructor(void *key_val)
62 {
63 /* Use HDfree here instead of H5MM_xfree(), to avoid calling the H5CS routines */
64 if(key_val != NULL)
65 HDfree(key_val);
66 }
67
68
69 #ifndef H5_HAVE_WIN_THREADS
70
71 /*--------------------------------------------------------------------------
72 * NAME
73 * H5TS_pthread_first_thread_init
74 *
75 * USAGE
76 * H5TS_pthread_first_thread_init()
77 *
78 * RETURNS
79 *
80 * DESCRIPTION
81 * Initialization of global API lock, keys for per-thread error stacks and
82 * cancallability information. Called by the first thread that enters the
83 * library.
84 *
85 * PROGRAMMER: Chee Wai LEE
86 * May 2, 2000
87 *
88 *--------------------------------------------------------------------------
89 */
90 void
H5TS_pthread_first_thread_init(void)91 H5TS_pthread_first_thread_init(void)
92 {
93 H5_g.H5_libinit_g = FALSE; /* Library hasn't been initialized */
94 H5_g.H5_libterm_g = FALSE; /* Library isn't being shutdown */
95
96 #ifdef H5_HAVE_WIN32_API
97 # ifdef PTW32_STATIC_LIB
98 pthread_win32_process_attach_np();
99 # endif
100 #endif
101
102 /* initialize global API mutex lock */
103 pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
104 pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
105 H5_g.init_lock.lock_count = 0;
106
107 /* initialize key for thread-specific error stacks */
108 pthread_key_create(&H5TS_errstk_key_g, H5TS_key_destructor);
109
110 /* initialize key for thread-specific function stacks */
111 pthread_key_create(&H5TS_funcstk_key_g, H5TS_key_destructor);
112
113 /* initialize key for thread-specific API contexts */
114 pthread_key_create(&H5TS_apictx_key_g, H5TS_key_destructor);
115
116 /* initialize key for thread cancellability mechanism */
117 pthread_key_create(&H5TS_cancel_key_g, H5TS_key_destructor);
118 }
119 #endif /* H5_HAVE_WIN_THREADS */
120
121
122 /*--------------------------------------------------------------------------
123 * NAME
124 * H5TS_mutex_lock
125 *
126 * USAGE
127 * H5TS_mutex_lock(&mutex_var)
128 *
129 * RETURNS
130 * 0 on success and non-zero on error.
131 *
132 * DESCRIPTION
133 * Recursive lock semantics for HDF5 (locking) -
134 * Multiple acquisition of a lock by a thread is permitted with a
135 * corresponding unlock operation required.
136 *
137 * PROGRAMMER: Chee Wai LEE
138 * May 2, 2000
139 *
140 *--------------------------------------------------------------------------
141 */
142 herr_t
H5TS_mutex_lock(H5TS_mutex_t * mutex)143 H5TS_mutex_lock(H5TS_mutex_t *mutex)
144 {
145 #ifdef H5_HAVE_WIN_THREADS
146 EnterCriticalSection( &mutex->CriticalSection);
147 return 0;
148 #else /* H5_HAVE_WIN_THREADS */
149 herr_t ret_value = pthread_mutex_lock(&mutex->atomic_lock);
150
151 if (ret_value)
152 return ret_value;
153
154 if(mutex->lock_count && pthread_equal(HDpthread_self(), mutex->owner_thread)) {
155 /* already owned by self - increment count */
156 mutex->lock_count++;
157 } else {
158 /* if owned by other thread, wait for condition signal */
159 while(mutex->lock_count)
160 pthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock);
161
162 /* After we've received the signal, take ownership of the mutex */
163 mutex->owner_thread = HDpthread_self();
164 mutex->lock_count = 1;
165 }
166
167 return pthread_mutex_unlock(&mutex->atomic_lock);
168 #endif /* H5_HAVE_WIN_THREADS */
169 }
170
171
172 /*--------------------------------------------------------------------------
173 * NAME
174 * H5TS_mutex_unlock
175 *
176 * USAGE
177 * H5TS_mutex_unlock(&mutex_var)
178 *
179 * RETURNS
180 * 0 on success and non-zero on error.
181 *
182 * DESCRIPTION
183 * Recursive lock semantics for HDF5 (unlocking) -
184 * Multiple acquisition of a lock by a thread is permitted with a
185 * corresponding unlock operation required.
186 *
187 * PROGRAMMER: Chee Wai LEE
188 * May 2, 2000
189 *
190 *--------------------------------------------------------------------------
191 */
192 herr_t
H5TS_mutex_unlock(H5TS_mutex_t * mutex)193 H5TS_mutex_unlock(H5TS_mutex_t *mutex)
194 {
195 #ifdef H5_HAVE_WIN_THREADS
196 /* Releases ownership of the specified critical section object. */
197 LeaveCriticalSection(&mutex->CriticalSection);
198 return 0;
199 #else /* H5_HAVE_WIN_THREADS */
200 herr_t ret_value = pthread_mutex_lock(&mutex->atomic_lock);
201
202 if(ret_value)
203 return ret_value;
204
205 mutex->lock_count--;
206
207 ret_value = pthread_mutex_unlock(&mutex->atomic_lock);
208
209 if(mutex->lock_count == 0) {
210 int err;
211
212 err = pthread_cond_signal(&mutex->cond_var);
213 if(err != 0)
214 ret_value = err;
215 } /* end if */
216
217 return ret_value;
218 #endif /* H5_HAVE_WIN_THREADS */
219 } /* H5TS_mutex_unlock */
220
221
222 /*--------------------------------------------------------------------------
223 * NAME
224 * H5TS_cancel_count_inc
225 *
226 * USAGE
227 * H5TS_cancel_count_inc()
228 *
229 * RETURNS
230 * 0 on success non-zero error code on error.
231 *
232 * DESCRIPTION
233 * Creates a cancelation counter for a thread if it is the first time
234 * the thread is entering the library.
235 *
236 * if counter value is zero, then set cancelability type of the thread
237 * to PTHREAD_CANCEL_DISABLE as thread is entering the library and store
238 * the previous cancelability type into cancelation counter.
239 * Increase the counter value by 1.
240 *
241 * PROGRAMMER: Chee Wai LEE
242 * May 2, 2000
243 *
244 *--------------------------------------------------------------------------
245 */
246 herr_t
H5TS_cancel_count_inc(void)247 H5TS_cancel_count_inc(void)
248 {
249 #ifdef H5_HAVE_WIN_THREADS
250 /* unsupported; just return 0 */
251 return SUCCEED;
252 #else /* H5_HAVE_WIN_THREADS */
253 H5TS_cancel_t *cancel_counter;
254 herr_t ret_value = SUCCEED;
255
256 cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_g);
257
258 if (!cancel_counter) {
259 /*
260 * First time thread calls library - create new counter and associate
261 * with key.
262 *
263 * Don't use H5MM calls here since the destructor has to use HDfree in
264 * order to avoid codestack calls.
265 */
266 cancel_counter = (H5TS_cancel_t *)HDcalloc(1, sizeof(H5TS_cancel_t));
267
268 if (!cancel_counter) {
269 HERROR(H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed");
270 return FAIL;
271 }
272
273 ret_value = pthread_setspecific(H5TS_cancel_key_g, (void *)cancel_counter);
274 }
275
276 if (cancel_counter->cancel_count == 0)
277 /* thread entering library */
278 ret_value = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
279 &cancel_counter->previous_state);
280
281 ++cancel_counter->cancel_count;
282
283 return ret_value;
284 #endif /* H5_HAVE_WIN_THREADS */
285 }
286
287
288 /*--------------------------------------------------------------------------
289 * NAME
290 * H5TS_cancel_count_dec
291 *
292 * USAGE
293 * H5TS_cancel_count_dec()
294 *
295 * RETURNS
296 * 0 on success and a non-zero error code on error.
297 *
298 * DESCRIPTION
299 * If counter value is one, then set cancelability type of the thread
300 * to the previous cancelability type stored in the cancelation counter.
301 * (the thread is leaving the library).
302 *
303 * Decrement the counter value by 1.
304 *
305 * PROGRAMMER: Chee Wai LEE
306 * May 2, 2000
307 *
308 *--------------------------------------------------------------------------
309 */
310 herr_t
H5TS_cancel_count_dec(void)311 H5TS_cancel_count_dec(void)
312 {
313 #ifdef H5_HAVE_WIN_THREADS
314 /* unsupported; will just return 0 */
315 return SUCCEED;
316 #else /* H5_HAVE_WIN_THREADS */
317 register H5TS_cancel_t *cancel_counter;
318 herr_t ret_value = SUCCEED;
319
320 cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_g);
321
322 if (cancel_counter->cancel_count == 1)
323 ret_value = pthread_setcancelstate(cancel_counter->previous_state, NULL);
324
325 --cancel_counter->cancel_count;
326
327 return ret_value;
328 #endif /* H5_HAVE_WIN_THREADS */
329 }
330
331
332 #ifdef H5_HAVE_WIN_THREADS
333 /*--------------------------------------------------------------------------
334 * NAME
335 * H5TS_win32_process_enter
336 *
337 * RETURNS
338 * SUCCEED/FAIL
339 *
340 * DESCRIPTION
341 * Per-process setup on Windows when using Win32 threads.
342 *
343 *--------------------------------------------------------------------------
344 */
345 H5_DLL BOOL CALLBACK
H5TS_win32_process_enter(PINIT_ONCE InitOnce,PVOID Parameter,PVOID * lpContex)346 H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
347 {
348 BOOL ret_value = TRUE;
349
350 /* Initialize the critical section (can't fail) */
351 InitializeCriticalSection(&H5_g.init_lock.CriticalSection);
352
353 /* Set up thread local storage */
354 if(TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc()))
355 ret_value = FALSE;
356
357 #ifdef H5_HAVE_CODESTACK
358 if(TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc()))
359 ret_value = FALSE;
360 #endif /* H5_HAVE_CODESTACK */
361
362 if(TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc()))
363 ret_value = FALSE;
364
365 return ret_value;
366 } /* H5TS_win32_process_enter() */
367 #endif /* H5_HAVE_WIN_THREADS */
368
369
370 #ifdef H5_HAVE_WIN_THREADS
371 /*--------------------------------------------------------------------------
372 * NAME
373 * H5TS_win32_thread_enter
374 *
375 * RETURNS
376 * SUCCEED/FAIL
377 *
378 * DESCRIPTION
379 * Per-thread setup on Windows when using Win32 threads.
380 *
381 *--------------------------------------------------------------------------
382 */
383 herr_t
H5TS_win32_thread_enter(void)384 H5TS_win32_thread_enter(void)
385 {
386 herr_t ret_value = SUCCEED;
387
388 /* Currently a placeholder function. TLS setup is performed
389 * elsewhere in the library.
390 *
391 * WARNING: Do NOT use C standard library functions here.
392 * CRT functions are not allowed in DllMain, which is where this code
393 * is used.
394 */
395
396 return ret_value;
397 } /* H5TS_win32_thread_enter() */
398 #endif /* H5_HAVE_WIN_THREADS */
399
400
401 #ifdef H5_HAVE_WIN_THREADS
402 /*--------------------------------------------------------------------------
403 * NAME
404 * H5TS_win32_process_exit
405 *
406 * RETURNS
407 * SUCCEED/FAIL
408 *
409 * DESCRIPTION
410 * Per-process cleanup on Windows when using Win32 threads.
411 *
412 *--------------------------------------------------------------------------
413 */
414 void
H5TS_win32_process_exit(void)415 H5TS_win32_process_exit(void)
416 {
417
418 /* Windows uses a different thread local storage mechanism which does
419 * not support auto-freeing like pthreads' keys.
420 *
421 * This function is currently registered via atexit() and is called
422 * AFTER H5_term_library().
423 */
424
425 /* Clean up critical section resources (can't fail) */
426 DeleteCriticalSection(&H5_g.init_lock.CriticalSection);
427
428 /* Clean up per-process thread local storage */
429 TlsFree(H5TS_errstk_key_g);
430 #ifdef H5_HAVE_CODESTACK
431 TlsFree(H5TS_funcstk_key_g);
432 #endif /* H5_HAVE_CODESTACK */
433 TlsFree(H5TS_apictx_key_g);
434
435 return;
436 } /* H5TS_win32_process_exit() */
437 #endif /* H5_HAVE_WIN_THREADS */
438
439
440 #ifdef H5_HAVE_WIN_THREADS
441 /*--------------------------------------------------------------------------
442 * NAME
443 * H5TS_win32_thread_exit
444 *
445 * RETURNS
446 * SUCCEED/FAIL
447 *
448 * DESCRIPTION
449 * Per-thread cleanup on Windows when using Win32 threads.
450 *
451 *--------------------------------------------------------------------------
452 */
453 herr_t
H5TS_win32_thread_exit(void)454 H5TS_win32_thread_exit(void)
455 {
456 LPVOID lpvData;
457 herr_t ret_value = SUCCEED;
458
459 /* Windows uses a different thread local storage mechanism which does
460 * not support auto-freeing like pthreads' keys.
461 *
462 * WARNING: Do NOT use C standard library functions here.
463 * CRT functions are not allowed in DllMain, which is where this code
464 * is used.
465 */
466
467 /* Clean up per-thread thread local storage */
468 lpvData = TlsGetValue(H5TS_errstk_key_g);
469 if(lpvData)
470 LocalFree((HLOCAL)lpvData);
471
472 #ifdef H5_HAVE_CODESTACK
473 lpvData = TlsGetValue(H5TS_funcstk_key_g);
474 if(lpvData)
475 LocalFree((HLOCAL)lpvData);
476 #endif /* H5_HAVE_CODESTACK */
477
478 lpvData = TlsGetValue(H5TS_apictx_key_g);
479 if(lpvData)
480 LocalFree((HLOCAL)lpvData);
481
482 return ret_value;
483 } /* H5TS_win32_thread_exit() */
484 #endif /* H5_HAVE_WIN_THREADS */
485
486
487 /*--------------------------------------------------------------------------
488 * NAME
489 * H5TS_create_thread
490 *
491 * RETURNS
492 * Thread identifier.
493 *
494 * DESCRIPTION
495 * Spawn off a new thread calling function 'func' with input 'udata'.
496 *
497 * PROGRAMMER: Mike McGreevy
498 * August 31, 2010
499 *
500 *--------------------------------------------------------------------------
501 */
502 H5TS_thread_t
H5TS_create_thread(void * (* func)(void *),H5TS_attr_t * attr,void * udata)503 H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
504 {
505 H5TS_thread_t ret_value;
506
507 #ifdef H5_HAVE_WIN_THREADS
508
509 /* When calling C runtime functions, you should use _beginthread or
510 * _beginthreadex instead of CreateThread. Threads created with
511 * CreateThread risk being killed in low-memory situations. Since we
512 * only create threads in our test code, this is unlikely to be an issue
513 * and we'll use the easier-to-deal-with CreateThread for now.
514 *
515 * NOTE: _beginthread() auto-recycles its handle when execution completes
516 * so you can't wait on it, making it unsuitable for the existing
517 * test code.
518 */
519 ret_value = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, udata, 0, NULL);
520
521 #else /* H5_HAVE_WIN_THREADS */
522
523 pthread_create(&ret_value, attr, (void * (*)(void *))func, udata);
524
525 #endif /* H5_HAVE_WIN_THREADS */
526
527 return ret_value;
528
529 } /* H5TS_create_thread */
530
531 #endif /* H5_HAVE_THREADSAFE */
532
533