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