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://www.hdfgroup.org/licenses.               *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /*
15  * Purpose: This file contains the framework for ensuring that the global
16  *          library lock is held when an API routine is called.  This
17  *          framework works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API
18  *          macros defined in H5private.h.
19  *
20  * Note:    Because this threadsafety framework operates outside the library,
21  *          it does not use the error stack.
22  */
23 
24 /****************/
25 /* Module Setup */
26 /****************/
27 
28 /***********/
29 /* Headers */
30 /***********/
31 #include "H5private.h"  /* Generic Functions                        */
32 #include "H5Eprivate.h" /* Error handling                           */
33 
34 #ifdef H5_HAVE_THREADSAFE
35 
36 /****************/
37 /* Local Macros */
38 /****************/
39 
40 /******************/
41 /* Local Typedefs */
42 /******************/
43 
44 /* Cancelability structure */
45 typedef struct H5TS_cancel_struct {
46     int          previous_state;
47     unsigned int cancel_count;
48 } H5TS_cancel_t;
49 
50 #ifndef H5_HAVE_WIN_THREADS
51 /* An H5TS_tid_t is a record of a thread identifier that is
52  * available for reuse.
53  */
54 struct _tid;
55 typedef struct _tid H5TS_tid_t;
56 
57 struct _tid {
58     H5TS_tid_t *next;
59     uint64_t    id;
60 };
61 #endif
62 
63 /********************/
64 /* Local Prototypes */
65 /********************/
66 static void H5TS__key_destructor(void *key_val);
67 #ifndef H5_HAVE_WIN_THREADS
68 static void H5TS_tid_destructor(void *_v);
69 static void H5TS_tid_init(void);
70 #endif
71 
72 /*********************/
73 /* Package Variables */
74 /*********************/
75 
76 /*****************************/
77 /* Library Private Variables */
78 /*****************************/
79 
80 /* Global variable definitions */
81 #ifdef H5_HAVE_WIN_THREADS
82 H5TS_once_t H5TS_first_init_g;
83 #else
84 H5TS_once_t H5TS_first_init_g = PTHREAD_ONCE_INIT;
85 #endif
86 
87 /* Thread-local keys, used by other interfaces */
88 
89 /* Error stack */
90 H5TS_key_t H5TS_errstk_key_g;
91 
92 /* Function stack */
93 #ifdef H5_HAVE_CODESTACK
94 H5TS_key_t H5TS_funcstk_key_g;
95 #endif
96 
97 /* API context */
98 H5TS_key_t H5TS_apictx_key_g;
99 
100 /*******************/
101 /* Local Variables */
102 /*******************/
103 
104 /* Thread-local keys, used in this module */
105 static H5TS_key_t H5TS_cancel_key_s; /* Thread cancellation state */
106 
107 #ifndef H5_HAVE_WIN_THREADS
108 
109 /* Pointer to first free thread ID record or NULL. */
110 static H5TS_tid_t *H5TS_tid_next_free = NULL;
111 static uint64_t    H5TS_tid_next_id   = 0;
112 
113 /* Mutual exclusion for access to H5TS_tid_next_free and H5TS_tid_next_id. */
114 static pthread_mutex_t H5TS_tid_mtx;
115 
116 /* Key for thread-local storage of the thread ID. */
117 static H5TS_key_t H5TS_tid_key;
118 
119 #endif
120 
121 /*--------------------------------------------------------------------------
122  * Function:    H5TS__key_destructor
123  *
124  * Returns:     void
125  *
126  * Description:
127  *   Frees the memory for a key.  Called by each thread as it exits.
128  *   Currently all the thread-specific information for all keys are simple
129  *   structures allocated with malloc, so we can free them all uniformly.
130  *
131  * Programmer:  Quincey Koziol
132  *              February 7, 2003
133  *--------------------------------------------------------------------------
134  */
135 static void
H5TS__key_destructor(void * key_val)136 H5TS__key_destructor(void *key_val)
137 {
138     /* Use HDfree here instead of H5MM_xfree(), to avoid calling the H5CS routines */
139     if (key_val != NULL)
140         HDfree(key_val);
141 } /* end H5TS__key_destructor() */
142 
143 #ifndef H5_HAVE_WIN_THREADS
144 
145 /*--------------------------------------------------------------------------
146  * Function:    H5TS_tid_destructor
147  *
148  * Returns:     void
149  *
150  * Description:
151  *   When a thread shuts down, put its ID record on the free list.
152  *--------------------------------------------------------------------------
153  */
154 static void
H5TS_tid_destructor(void * _v)155 H5TS_tid_destructor(void *_v)
156 {
157     H5TS_tid_t *tid = _v;
158 
159     if (tid == NULL)
160         return;
161 
162     /* TBD use an atomic CAS */
163     pthread_mutex_lock(&H5TS_tid_mtx);
164     tid->next          = H5TS_tid_next_free;
165     H5TS_tid_next_free = tid;
166     pthread_mutex_unlock(&H5TS_tid_mtx);
167 }
168 
169 /*--------------------------------------------------------------------------
170  * Function:    H5TS_tid_init
171  *
172  * Returns:     void
173  *
174  * Description:
175  *   Initialization for integer thread identifiers.
176  *--------------------------------------------------------------------------
177  */
178 static void
H5TS_tid_init(void)179 H5TS_tid_init(void)
180 {
181     pthread_mutex_init(&H5TS_tid_mtx, NULL);
182     pthread_key_create(&H5TS_tid_key, H5TS_tid_destructor);
183 }
184 
185 #endif
186 
187 #ifdef H5_HAVE_WIN_THREADS
188 /*--------------------------------------------------------------------------
189  * Function:    H5TS_win32_process_enter
190  *
191  * Returns:     TRUE on success, FALSE on failure
192  *
193  * Description:
194  *    Per-process setup on Windows when using Win32 threads.
195  *
196  *--------------------------------------------------------------------------
197  */
198 H5_DLL BOOL CALLBACK
H5TS_win32_process_enter(PINIT_ONCE InitOnce,PVOID Parameter,PVOID * lpContex)199 H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
200 {
201     BOOL ret_value = TRUE;
202 
203     /* initialize global API mutex lock */
204     InitializeCriticalSection(&H5_g.init_lock.CriticalSection);
205 
206     /* Set up thread local storage */
207     if (TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc()))
208         ret_value = FALSE;
209 
210 #ifdef H5_HAVE_CODESTACK
211     if (TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc()))
212         ret_value = FALSE;
213 #endif
214 
215     if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc()))
216         ret_value = FALSE;
217 
218     return ret_value;
219 } /* H5TS_win32_process_enter() */
220 
221 /*--------------------------------------------------------------------------
222  * Function:    H5TS_win32_thread_enter
223  *
224  * Returns:     SUCCEED/FAIL
225  *
226  * Description:
227  *    Per-thread setup on Windows when using Win32 threads.
228  *
229  *--------------------------------------------------------------------------
230  */
231 herr_t
H5TS_win32_thread_enter(void)232 H5TS_win32_thread_enter(void)
233 {
234     herr_t ret_value = SUCCEED;
235 
236     /* Currently a placeholder function.  TLS setup is performed
237      * elsewhere in the library.
238      *
239      * WARNING: Do NOT use C standard library functions here.
240      * CRT functions are not allowed in DllMain, which is where this code
241      * is used.
242      */
243 
244     return ret_value;
245 } /* H5TS_win32_thread_enter() */
246 
247 /*--------------------------------------------------------------------------
248  * Function:    H5TS_win32_process_exit
249  *
250  * Returns:     void
251  *
252  * Description:
253  *    Per-process cleanup on Windows when using Win32 threads.
254  *
255  *--------------------------------------------------------------------------
256  */
257 void
H5TS_win32_process_exit(void)258 H5TS_win32_process_exit(void)
259 {
260 
261     /* Windows uses a different thread local storage mechanism which does
262      * not support auto-freeing like pthreads' keys.
263      *
264      * This function is currently registered via atexit() and is called
265      * AFTER H5_term_library().
266      */
267 
268     /* Clean up critical section resources (can't fail) */
269     DeleteCriticalSection(&H5_g.init_lock.CriticalSection);
270 
271     /* Clean up per-process thread local storage */
272     TlsFree(H5TS_errstk_key_g);
273 #ifdef H5_HAVE_CODESTACK
274     TlsFree(H5TS_funcstk_key_g);
275 #endif
276     TlsFree(H5TS_apictx_key_g);
277 
278     return;
279 } /* end H5TS_win32_process_exit() */
280 
281 /*--------------------------------------------------------------------------
282  * Function:    H5TS_win32_thread_exit
283  *
284  * Returns:     SUCCEED/FAIL
285  *
286  * Description:
287  *    Per-thread cleanup on Windows when using Win32 threads.
288  *
289  *--------------------------------------------------------------------------
290  */
291 herr_t
H5TS_win32_thread_exit(void)292 H5TS_win32_thread_exit(void)
293 {
294     LPVOID lpvData;
295     herr_t ret_value = SUCCEED;
296 
297     /* Windows uses a different thread local storage mechanism which does
298      * not support auto-freeing like pthreads' keys.
299      *
300      * WARNING: Do NOT use C standard library functions here.
301      * CRT functions are not allowed in DllMain, which is where this code
302      * is used.
303      */
304 
305     /* Clean up per-thread thread local storage */
306     lpvData = TlsGetValue(H5TS_errstk_key_g);
307     if (lpvData)
308         LocalFree((HLOCAL)lpvData);
309 
310 #ifdef H5_HAVE_CODESTACK
311     lpvData = TlsGetValue(H5TS_funcstk_key_g);
312     if (lpvData)
313         LocalFree((HLOCAL)lpvData);
314 #endif
315 
316     lpvData = TlsGetValue(H5TS_apictx_key_g);
317     if (lpvData)
318         LocalFree((HLOCAL)lpvData);
319 
320     return ret_value;
321 } /* end H5TS_win32_thread_exit() */
322 
323 #endif /* H5_HAVE_WIN_THREADS */
324 
325 #ifndef H5_HAVE_WIN_THREADS
326 
327 /*--------------------------------------------------------------------------
328  * Function:    H5TS_thread_id
329  *
330  * Returns:     An integer identifier for the current thread
331  *
332  * Description:
333  *   The ID satisfies the following properties:
334  *
335  *   1 1 <= ID <= UINT64_MAX
336  *   2 ID is constant over the thread's lifetime.
337  *   3 No two threads share an ID during their lifetimes.
338  *   4 A thread's ID is available for reuse as soon as it is joined.
339  *
340  *   ID 0 is reserved.  H5TS_thread_id() returns 0 if the library was not
341  *   built with thread safety or if an error prevents it from assigning an
342  *   ID.
343  *
344  *--------------------------------------------------------------------------
345  */
346 uint64_t
H5TS_thread_id(void)347 H5TS_thread_id(void)
348 {
349     H5TS_tid_t *tid = pthread_getspecific(H5TS_tid_key);
350     H5TS_tid_t  proto_tid;
351 
352     /* An ID is already assigned. */
353     if (tid != NULL)
354         return tid->id;
355 
356     /* An ID is *not* already assigned: reuse an ID that's on the
357      * free list, or else generate a new ID.
358      *
359      * Allocating memory while holding a mutex is bad form, so
360      * point `tid` at `proto_tid` if we need to allocate some
361      * memory.
362      */
363     pthread_mutex_lock(&H5TS_tid_mtx);
364     if ((tid = H5TS_tid_next_free) != NULL)
365         H5TS_tid_next_free = tid->next;
366     else if (H5TS_tid_next_id != UINT64_MAX) {
367         tid     = &proto_tid;
368         tid->id = ++H5TS_tid_next_id;
369     }
370     pthread_mutex_unlock(&H5TS_tid_mtx);
371 
372     /* If a prototype ID record was established, copy it to the heap. */
373     if (tid == &proto_tid)
374         if ((tid = HDmalloc(sizeof(*tid))) != NULL)
375             *tid = proto_tid;
376 
377     if (tid == NULL)
378         return 0;
379 
380     /* Finish initializing the ID record and set a thread-local pointer
381      * to it.
382      */
383     tid->next = NULL;
384     if (pthread_setspecific(H5TS_tid_key, tid) != 0) {
385         H5TS_tid_destructor(tid);
386         return 0;
387     }
388 
389     return tid->id;
390 }
391 
392 /*--------------------------------------------------------------------------
393  * Function:    H5TS_pthread_first_thread_init
394  *
395  * Returns:     void
396  *
397  * Description:
398  *   Initialization of global API lock, keys for per-thread error stacks and
399  *   cancallability information. Called by the first thread that enters the
400  *   library.
401  *
402  * Programmer: Chee Wai LEE
403  *             May 2, 2000
404  *
405  *--------------------------------------------------------------------------
406  */
407 void
H5TS_pthread_first_thread_init(void)408 H5TS_pthread_first_thread_init(void)
409 {
410     H5_g.H5_libinit_g = FALSE; /* Library hasn't been initialized */
411     H5_g.H5_libterm_g = FALSE; /* Library isn't being shutdown */
412 
413 #ifdef H5_HAVE_WIN32_API
414 #ifdef PTW32_STATIC_LIB
415     pthread_win32_process_attach_np();
416 #endif
417 #endif
418 
419     /* initialize global API mutex lock */
420 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
421     H5TS_rw_lock_init(&H5_g.init_rw_lock, H5TS_RW_LOCK_POLICY_FAVOR_WRITERS);
422 #else
423     pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
424     pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
425     H5_g.init_lock.lock_count = 0;
426 #endif
427 
428     /* Initialize integer thread identifiers. */
429     H5TS_tid_init();
430 
431     /* initialize key for thread-specific error stacks */
432     pthread_key_create(&H5TS_errstk_key_g, H5TS__key_destructor);
433 
434 #ifdef H5_HAVE_CODESTACK
435     /* initialize key for thread-specific function stacks */
436     pthread_key_create(&H5TS_funcstk_key_g, H5TS__key_destructor);
437 #endif
438 
439     /* initialize key for thread-specific API contexts */
440     pthread_key_create(&H5TS_apictx_key_g, H5TS__key_destructor);
441 
442     /* initialize key for thread cancellability mechanism */
443     pthread_key_create(&H5TS_cancel_key_s, H5TS__key_destructor);
444 
445 } /* end H5TS_pthread_first_thread_init() */
446 
447 #endif /* H5_HAVE_WIN_THREADS */
448 
449 /*--------------------------------------------------------------------------
450  * Function:    H5TS_mutex_lock
451  *
452  *
453  * Returns:     SUCCEED/FAIL
454  *
455  * Description:
456  *    Recursive lock semantics for HDF5 (locking) -
457  *    Multiple acquisition of a lock by a thread is permitted with a
458  *    corresponding unlock operation required.
459  *
460  * Programmer: Chee Wai LEE
461  *             May 2, 2000
462  *
463  *--------------------------------------------------------------------------
464  */
465 herr_t
H5TS_mutex_lock(H5TS_mutex_t * mutex)466 H5TS_mutex_lock(H5TS_mutex_t *mutex)
467 {
468     herr_t ret_value = SUCCEED;
469 
470 #ifdef H5_HAVE_WIN_THREADS
471     EnterCriticalSection(&mutex->CriticalSection);
472 #else
473     /* Acquire the library lock */
474     if (pthread_mutex_lock(&mutex->atomic_lock) != 0)
475         return FAIL;
476 
477     /* Check if this thread already owns the lock */
478     if (mutex->lock_count && pthread_equal(pthread_self(), mutex->owner_thread))
479         /* already owned by self - increment count */
480         mutex->lock_count++;
481     else {
482         /* Wait until the lock is released by current owner thread */
483         while (mutex->lock_count)
484             pthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock);
485 
486         /* After we've received the signal, take ownership of the mutex */
487         mutex->owner_thread = pthread_self();
488         mutex->lock_count   = 1;
489     } /* end else */
490 
491     /* Release the library lock */
492     if (pthread_mutex_unlock(&mutex->atomic_lock) != 0)
493         ret_value = FAIL;
494 #endif
495 
496     return ret_value;
497 } /* end H5TS_mutex_lock() */
498 
499 /*--------------------------------------------------------------------------
500  * Function:    H5TS_mutex_unlock
501  *
502  * Returns:     SUCCEED/FAIL
503  *
504  * Description:
505  *    Recursive lock semantics for HDF5 (unlocking) -
506  *    Multiple acquisition of a lock by a thread is permitted with a
507  *    corresponding unlock operation required.
508  *
509  * Programmer: Chee Wai LEE
510  *             May 2, 2000
511  *
512  *--------------------------------------------------------------------------
513  */
514 herr_t
H5TS_mutex_unlock(H5TS_mutex_t * mutex)515 H5TS_mutex_unlock(H5TS_mutex_t *mutex)
516 {
517     herr_t ret_value = SUCCEED;
518 
519 #ifdef H5_HAVE_WIN_THREADS
520     /* Releases ownership of the specified critical section object. */
521     LeaveCriticalSection(&mutex->CriticalSection);
522 #else
523 
524     /* Decrement the lock count for this thread */
525     if (pthread_mutex_lock(&mutex->atomic_lock) != 0)
526         return FAIL;
527     mutex->lock_count--;
528     if (pthread_mutex_unlock(&mutex->atomic_lock) != 0)
529         ret_value = FAIL;
530 
531     /* If the lock count drops to zero, signal the condition variable, to
532      * wake another thread.
533      */
534     if (mutex->lock_count == 0)
535         if (pthread_cond_signal(&mutex->cond_var) != 0)
536             ret_value = FAIL;
537 #endif /* H5_HAVE_WIN_THREADS */
538 
539     return ret_value;
540 } /* H5TS_mutex_unlock */
541 
542 /*--------------------------------------------------------------------------
543  * Function:    H5TS_cancel_count_inc
544  *
545  * Returns:     SUCCEED/FAIL
546  *
547  * Description:
548  *    Creates a cancellation counter for a thread if it is the first time
549  *    the thread is entering the library.
550  *
551  *    if counter value is zero, then set cancelability type of the thread
552  *    to PTHREAD_CANCEL_DISABLE as thread is entering the library and store
553  *    the previous cancelability type into cancellation counter.
554  *
555  *    Increase the counter value by 1.
556  *
557  * Programmer: Chee Wai LEE
558  *            May 2, 2000
559  *
560  *--------------------------------------------------------------------------
561  */
562 herr_t
H5TS_cancel_count_inc(void)563 H5TS_cancel_count_inc(void)
564 {
565     herr_t ret_value = SUCCEED;
566 
567 #ifndef H5_HAVE_WIN_THREADS
568     H5TS_cancel_t *cancel_counter;
569 
570     /* Acquire the thread's cancellation counter */
571     cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s);
572 
573     /* Check if it's created yet */
574     if (!cancel_counter) {
575         /*
576          * First time thread calls library - create new counter and associate
577          * with key.
578          *
579          * Don't use H5MM calls here since the destructor has to use HDfree in
580          * order to avoid codestack calls.
581          */
582         cancel_counter = HDcalloc(1, sizeof(*cancel_counter));
583         if (NULL == cancel_counter) {
584             HERROR(H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed");
585             return FAIL;
586         }
587 
588         /* Set the thread's cancellation counter with the new object */
589         if (pthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter) != 0) {
590             HDfree(cancel_counter);
591             return FAIL;
592         }
593     }
594 
595     /* Check if thread entering library */
596     if (cancel_counter->cancel_count == 0)
597         /* Set cancellation state to 'disable', and remember previous state */
598         if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state) != 0)
599             ret_value = FAIL;
600 
601     /* Increment # of times the library API was re-entered, to avoid resetting
602      * previous cancellation state until the final API routine is returning.
603      */
604     ++cancel_counter->cancel_count;
605 #endif /* H5_HAVE_WIN_THREADS */
606 
607     return ret_value;
608 } /* end H5TS_cancel_count_inc() */
609 
610 /*--------------------------------------------------------------------------
611  * Function:    H5TS_cancel_count_dec
612  *
613  * Returns:     SUCCEED/FAIL
614  *
615  * Description:
616  *    If counter value is one, then set cancelability type of the thread
617  *    to the previous cancelability type stored in the cancellation counter.
618  *    (the thread is leaving the library).
619  *
620  *    Decrement the counter value by 1.
621  *
622  * Programmer: Chee Wai LEE
623  *             May 2, 2000
624  *
625  *--------------------------------------------------------------------------
626  */
627 herr_t
H5TS_cancel_count_dec(void)628 H5TS_cancel_count_dec(void)
629 {
630     herr_t ret_value = SUCCEED;
631 
632 #ifndef H5_HAVE_WIN_THREADS
633     H5TS_cancel_t *cancel_counter;
634 
635     /* Acquire the thread's cancellation counter */
636     cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s);
637 
638     /* Check for leaving last API routine */
639     if (cancel_counter->cancel_count == 1)
640         /* Reset to previous thread cancellation state, if last API */
641         if (pthread_setcancelstate(cancel_counter->previous_state, NULL) != 0)
642             ret_value = FAIL;
643 
644     /* Decrement cancellation counter */
645     --cancel_counter->cancel_count;
646 #endif /* H5_HAVE_WIN_THREADS */
647 
648     return ret_value;
649 } /* end H5TS_cancel_count_dec() */
650 
651 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
652 
653 /*--------------------------------------------------------------------------
654  * Function:    H5TS_alloc_rec_entry_count
655  *
656  * Returns:
657  *    Pointer to allocated and initialized instance of
658  *    H5TS_rec_entry_count, or NULL on failure.
659  *
660  * Description:
661  *    Allocate and initalize an instance of H5TS_rec_entry_count.
662  *
663  * Programmer:  John Mainzer
664  *              August 28, 2020
665  *
666  *--------------------------------------------------------------------------
667  */
668 H5TS_rec_entry_count *
H5TS_alloc_rec_entry_count(hbool_t write_lock)669 H5TS_alloc_rec_entry_count(hbool_t write_lock)
670 {
671     H5TS_rec_entry_count *ret_value = NULL;
672 
673     ret_value = HDmalloc(sizeof(*ret_value));
674 
675     if (ret_value) {
676 
677         ret_value->magic          = H5TS_RW_ENTRY_COUNT_MAGIC;
678         ret_value->write_lock     = write_lock;
679         ret_value->rec_lock_count = 1;
680     }
681 
682     return ret_value;
683 
684 } /* end H5TS_alloc_rec_entry_count() */
685 
686 /*--------------------------------------------------------------------------
687  * Function:    H5TS_free_rec_entry_count
688  *
689  * Returns:     void
690  *
691  * Description:
692  *    Frees the supplied instance of H5TS_rec_entry_count.
693  *
694  * Programmer:  John Mainzer
695  *              August 28, 2020
696  *
697  *--------------------------------------------------------------------------
698  */
699 void
H5TS_free_rec_entry_count(void * target)700 H5TS_free_rec_entry_count(void *target)
701 {
702     H5TS_rec_entry_count *count;
703 
704     count = (H5TS_rec_entry_count *)target;
705 
706     HDassert(count);
707     HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
708 
709     count->magic = 0;
710 
711     HDfree(count);
712 
713     return;
714 
715 } /* end H5TS_free_rec_entry_count() */
716 
717 /*--------------------------------------------------------------------------
718  * Function:    H5TS_rw_lock_init
719  *
720  * Returns:     SUCCEED/FAIL
721  *
722  * Description:
723  *    Initialize the supplied instance of H5TS_rw_lock_t.
724  *
725  * Programmer:  John Mainzer
726  *              August 28, 2020
727  *
728  *--------------------------------------------------------------------------
729  */
730 herr_t
H5TS_rw_lock_init(H5TS_rw_lock_t * rw_lock,int policy)731 H5TS_rw_lock_init(H5TS_rw_lock_t *rw_lock, int policy)
732 {
733     herr_t ret_value = SUCCEED;
734 
735     /* Sanity checks -- until other policies are implemented,
736      * policy must equal H5TS_RW_LOCK_POLICY_FAVOR_WRITERS.
737      */
738     if ((rw_lock == NULL) || (policy != H5TS_RW_LOCK_POLICY_FAVOR_WRITERS)) {
739 
740         ret_value = FAIL;
741     }
742 
743     /* NOTE: Win32 thread init functions tend to have a return type of void while
744      *        Pthreads return an int. We've gone with the lowest common denominator
745      *        here, but we're going to have to better abstract this in the future.
746      */
747 
748     /* Initialize the mutex */
749     if (ret_value == SUCCEED)
750         if (H5TS_mutex_init(&(rw_lock->mutex)) != 0)
751             ret_value = FAIL;
752 
753     /* Initialize the waiting readers cv */
754     if (ret_value == SUCCEED)
755         if (H5TS_cond_init(&(rw_lock->readers_cv)) != 0)
756             ret_value = FAIL;
757 
758     /* Initialize the waiting writers cv */
759     if (ret_value == SUCCEED)
760         if (H5TS_cond_init(&(rw_lock->writers_cv)) != 0)
761             ret_value = FAIL;
762 
763     /* Initialize the counts key */
764     if (ret_value == SUCCEED)
765         if (pthread_key_create(&(rw_lock->rec_entry_count_key), H5TS_free_rec_entry_count) != 0)
766             ret_value = FAIL;
767 
768     if (ret_value == SUCCEED) {
769 
770         /* Initialize scalar fields */
771 
772         rw_lock->magic                                = H5TS_RW_LOCK_MAGIC;
773         rw_lock->policy                               = policy;
774         rw_lock->waiting_readers_count                = 0;
775         rw_lock->waiting_writers_count                = 0;
776         rw_lock->active_readers                       = 0;
777         rw_lock->active_writers                       = 0;
778         rw_lock->stats.read_locks_granted             = 0;
779         rw_lock->stats.read_locks_released            = 0;
780         rw_lock->stats.real_read_locks_granted        = 0;
781         rw_lock->stats.real_read_locks_released       = 0;
782         rw_lock->stats.max_read_locks                 = 0;
783         rw_lock->stats.max_read_lock_recursion_depth  = 0;
784         rw_lock->stats.read_locks_delayed             = 0;
785         rw_lock->stats.max_read_locks_pending         = 0;
786         rw_lock->stats.write_locks_granted            = 0;
787         rw_lock->stats.write_locks_released           = 0;
788         rw_lock->stats.real_write_locks_granted       = 0;
789         rw_lock->stats.real_write_locks_released      = 0;
790         rw_lock->stats.max_write_locks                = 0;
791         rw_lock->stats.max_write_lock_recursion_depth = 0;
792         rw_lock->stats.write_locks_delayed            = 0;
793         rw_lock->stats.max_write_locks_pending        = 0;
794     }
795 
796     return ret_value;
797 
798 } /* end H5TS_rw_lock_init() */
799 
800 /*--------------------------------------------------------------------------
801  * Function:    H5TS_rw_lock_destroy
802  *
803  * Returns:     SUCCEED/FAIL
804  *
805  * Description:
806  *    Take down an instance of H5TS_rw_lock_t.  All mutex, condition
807  *    variables, and keys are destroyed, and magic is set to an invalid
808  *    value.  However, the instance of H5TS_rw_lock_t is not
809  *    freed.
810  *
811  * Programmer:  John Mainzer
812  *              August 28, 2020
813  *
814  *--------------------------------------------------------------------------
815  */
816 herr_t
H5TS_rw_lock_destroy(H5TS_rw_lock_t * rw_lock)817 H5TS_rw_lock_destroy(H5TS_rw_lock_t *rw_lock)
818 {
819     herr_t ret_value = SUCCEED;
820 
821     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
822 
823         ret_value = FAIL;
824     }
825     else {
826 
827         /* We are commited to the destroy at this point.  Set magic
828          * to an invalid value, and call the appropriate pthread
829          * destroy routines.  Call them all, even if one fails along
830          * the way.
831          */
832         rw_lock->magic = 0;
833 
834         if (H5TS_mutex_destroy(&(rw_lock->mutex)) < 0)
835             ret_value = FAIL;
836 
837         if (H5TS_cond_destroy(&(rw_lock->readers_cv)) < 0)
838             ret_value = FAIL;
839 
840         if (H5TS_cond_destroy(&(rw_lock->writers_cv)) < 0)
841             ret_value = FAIL;
842 
843         if (pthread_key_delete(rw_lock->rec_entry_count_key) < 0)
844             ret_value = FAIL;
845     }
846 
847     return ret_value;
848 
849 } /* end H5TS_rw_lock_destroy() */
850 
851 /*--------------------------------------------------------------------------
852  * Function:    H5TS_rw_rdlock
853  *
854  * Returns:     SUCCEED/FAIL
855  *
856  * Description:
857  *    Attempt to obtain a read lock on the associated recursive read / write
858  *    lock.
859  *
860  * Programmer:  John Mainzer
861  *              August 28, 2020
862  *
863  *--------------------------------------------------------------------------
864  */
865 herr_t
H5TS_rw_rdlock(H5TS_rw_lock_t * rw_lock)866 H5TS_rw_rdlock(H5TS_rw_lock_t *rw_lock)
867 {
868     hbool_t               have_mutex = FALSE;
869     int                   result;
870     H5TS_rec_entry_count *count;
871     herr_t                ret_value = SUCCEED;
872 
873     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
874 
875         ret_value = FAIL;
876     }
877 
878     /* Obtain the mutex */
879     if (ret_value == SUCCEED) {
880         if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
881             ret_value = FAIL;
882         else
883             have_mutex = TRUE;
884     }
885 
886     /* If there is no specific data for this thread, this is an
887      * initial read lock request.
888      */
889     if (ret_value == SUCCEED) {
890 
891         count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
892 
893         if (count) { /* This is a recursive lock */
894 
895             if ((count->write_lock) || (rw_lock->active_readers == 0) || (rw_lock->active_writers != 0)) {
896 
897                 ret_value = FAIL;
898             }
899             else {
900 
901                 count->rec_lock_count++;
902 
903                 H5TS_update_stats_rd_lock(rw_lock, count);
904             }
905         }
906         else { /* This is an initial read lock request */
907 
908             switch (rw_lock->policy) {
909 
910                 case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
911                     if ((rw_lock->active_writers != 0) || (rw_lock->waiting_writers_count != 0)) {
912 
913                         int delayed = rw_lock->waiting_readers_count + 1;
914 
915                         H5TS_update_stats_rd_lock_delay(rw_lock, delayed);
916                     }
917 
918                     while ((rw_lock->active_writers != 0) || (rw_lock->waiting_writers_count != 0)) {
919 
920                         rw_lock->waiting_readers_count++;
921 
922                         result = H5TS_cond_wait(&(rw_lock->readers_cv), &(rw_lock->mutex));
923 
924                         rw_lock->waiting_readers_count--;
925 
926                         if (result != 0) {
927 
928                             ret_value = FAIL;
929                             break;
930                         }
931                     }
932                     break;
933 
934                 default:
935                     ret_value = FAIL;
936                     break;
937             }
938 
939             if ((ret_value == SUCCEED) && (NULL == (count = H5TS_alloc_rec_entry_count(FALSE)))) {
940 
941                 ret_value = FAIL;
942             }
943 
944             if ((ret_value == SUCCEED) &&
945                 (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)count) != 0)) {
946 
947                 ret_value = FAIL;
948             }
949 
950             if (ret_value == SUCCEED) {
951 
952                 rw_lock->active_readers++;
953 
954                 HDassert(count->rec_lock_count == 1);
955 
956                 H5TS_update_stats_rd_lock(rw_lock, count);
957             }
958         }
959     }
960 
961     if (have_mutex) {
962 
963         H5TS_mutex_unlock_simple(&(rw_lock->mutex));
964     }
965 
966     return ret_value;
967 
968 } /* end H5TS_rw_rdlock() */
969 
970 /*--------------------------------------------------------------------------
971  * Function:    H5TS_rw_wrlock
972  *
973  * Returns:     SUCCEED/FAIL
974  *
975  * Description:
976  *    Attempt to obtain a write lock on the associated recursive read / write
977  *    lock.
978  *
979  * Programmer:  John Mainzer
980  *              August 28, 2020
981  *
982  *--------------------------------------------------------------------------
983  */
984 herr_t
H5TS_rw_wrlock(H5TS_rw_lock_t * rw_lock)985 H5TS_rw_wrlock(H5TS_rw_lock_t *rw_lock)
986 {
987     hbool_t               have_mutex = FALSE;
988     int                   result;
989     H5TS_rec_entry_count *count;
990     herr_t                ret_value = SUCCEED;
991 
992     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
993 
994         ret_value = FAIL;
995     }
996 
997     /* Obtain the mutex */
998     if (ret_value == SUCCEED) {
999         if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
1000             ret_value = FAIL;
1001         else
1002             have_mutex = TRUE;
1003     }
1004 
1005     /* If there is no specific data for this thread, this is an
1006      * initial write lock request.
1007      */
1008     if (ret_value == SUCCEED) {
1009 
1010         count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
1011 
1012         if (count) { /* this is a recursive lock */
1013 
1014             if ((!(count->write_lock)) || (rw_lock->active_readers != 0) || (rw_lock->active_writers != 1)) {
1015 
1016                 ret_value = FAIL;
1017             }
1018             else {
1019 
1020                 count->rec_lock_count++;
1021 
1022                 H5TS_update_stats_wr_lock(rw_lock, count);
1023             }
1024         }
1025         else { /* This is an initial write lock request */
1026 
1027             switch (rw_lock->policy) {
1028 
1029                 case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
1030                     if ((rw_lock->active_readers > 0) || (rw_lock->active_writers > 0)) {
1031 
1032                         int delayed = rw_lock->waiting_writers_count + 1;
1033 
1034                         H5TS_update_stats_wr_lock_delay(rw_lock, delayed);
1035                     }
1036 
1037                     while ((rw_lock->active_readers > 0) || (rw_lock->active_writers > 0)) {
1038 
1039                         rw_lock->waiting_writers_count++;
1040 
1041                         result = H5TS_cond_wait(&(rw_lock->writers_cv), &(rw_lock->mutex));
1042 
1043                         rw_lock->waiting_writers_count--;
1044 
1045                         if (result != 0) {
1046 
1047                             ret_value = FAIL;
1048                             break;
1049                         }
1050                     }
1051                     break;
1052 
1053                 default:
1054                     ret_value = FAIL;
1055                     break;
1056             }
1057 
1058             if ((ret_value == SUCCEED) && (NULL == (count = H5TS_alloc_rec_entry_count(TRUE)))) {
1059 
1060                 ret_value = FAIL;
1061             }
1062 
1063             if ((ret_value == SUCCEED) &&
1064                 (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)count) != 0)) {
1065 
1066                 ret_value = FAIL;
1067             }
1068 
1069             if (ret_value == SUCCEED) {
1070 
1071                 rw_lock->active_writers++;
1072 
1073                 HDassert(count->rec_lock_count == 1);
1074 
1075                 H5TS_update_stats_wr_lock(rw_lock, count);
1076             }
1077         }
1078     }
1079 
1080     if (have_mutex) {
1081 
1082         H5TS_mutex_unlock_simple(&(rw_lock->mutex));
1083     }
1084 
1085     return ret_value;
1086 
1087 } /* end H5TS_rw_wrlock() */
1088 
1089 /*--------------------------------------------------------------------------
1090  * Function:    H5TS_rw_unlock
1091  *
1092  * Returns:     SUCCEED/FAIL
1093  *
1094  * Description:
1095  *    Attempt to unlock either a read or a write lock on the supplied
1096  *    recursive read / write lock.
1097  *
1098  * Programmer:  John Mainzer
1099  *              August 28, 2020
1100  *
1101  *--------------------------------------------------------------------------
1102  */
1103 herr_t
H5TS_rw_unlock(H5TS_rw_lock_t * rw_lock)1104 H5TS_rw_unlock(H5TS_rw_lock_t *rw_lock)
1105 {
1106     hbool_t               have_mutex        = FALSE;
1107     hbool_t               discard_rec_count = FALSE;
1108     H5TS_rec_entry_count *count;
1109     herr_t                ret_value = SUCCEED;
1110 
1111     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
1112 
1113         ret_value = FAIL;
1114     }
1115 
1116     /* Obtain the mutex */
1117     if (ret_value == SUCCEED) {
1118         if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
1119             ret_value = FAIL;
1120         else
1121             have_mutex = TRUE;
1122     }
1123 
1124     /* If there is no specific data for this thread, no lock was held,
1125      * and thus the unlock call must fail.
1126      */
1127     if (ret_value == SUCCEED) {
1128 
1129         count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
1130 
1131         HDassert(count);
1132         HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
1133         HDassert(count->rec_lock_count > 0);
1134 
1135         if (NULL == count) {
1136 
1137             ret_value = FAIL;
1138         }
1139         else if (count->magic != H5TS_RW_ENTRY_COUNT_MAGIC) {
1140 
1141             ret_value = FAIL;
1142         }
1143         else if (count->rec_lock_count <= 0) { /* Corrupt count? */
1144 
1145             ret_value = FAIL;
1146         }
1147         else if (count->write_lock) { /* Drop a write lock */
1148 
1149             HDassert((rw_lock->active_readers == 0) && (rw_lock->active_writers == 1));
1150 
1151             if ((rw_lock->active_readers != 0) || (rw_lock->active_writers != 1)) {
1152 
1153                 ret_value = FAIL;
1154             }
1155             else {
1156 
1157                 count->rec_lock_count--;
1158 
1159                 HDassert(count->rec_lock_count >= 0);
1160 
1161                 if (count->rec_lock_count == 0) {
1162 
1163                     /* Make note that we must discard the
1164                      * recursive entry counter so it will not
1165                      * confuse us on the next lock request.
1166                      */
1167                     discard_rec_count = TRUE;
1168 
1169                     /* Drop the write lock -- will signal later if needed */
1170                     rw_lock->active_writers--;
1171 
1172                     HDassert(rw_lock->active_writers == 0);
1173                 }
1174             }
1175 
1176             H5TS_update_stats_wr_unlock(rw_lock, count);
1177         }
1178         else { /* drop a read lock */
1179 
1180             HDassert((rw_lock->active_readers > 0) && (rw_lock->active_writers == 0));
1181 
1182             if ((rw_lock->active_readers <= 0) || (rw_lock->active_writers != 0)) {
1183 
1184                 ret_value = FAIL;
1185             }
1186             else {
1187 
1188                 count->rec_lock_count--;
1189 
1190                 HDassert(count->rec_lock_count >= 0);
1191 
1192                 if (count->rec_lock_count == 0) {
1193 
1194                     /* Make note that we must discard the
1195                      * recursive entry counter so it will not
1196                      * confuse us on the next lock request.
1197                      */
1198                     discard_rec_count = TRUE;
1199 
1200                     /* Drop the read lock -- will signal later if needed */
1201                     rw_lock->active_readers--;
1202                 }
1203             }
1204 
1205             H5TS_update_stats_rd_unlock(rw_lock, count);
1206         }
1207 
1208         if ((ret_value == SUCCEED) && (rw_lock->active_readers == 0) && (rw_lock->active_writers == 0)) {
1209 
1210             /* No locks held -- signal condition variables if required */
1211 
1212             switch (rw_lock->policy) {
1213 
1214                 case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
1215 #ifdef H5_HAVE_WIN_THREADS
1216                     if (rw_lock->waiting_writers_count > 0)
1217                         H5TS_cond_signal(&(rw_lock->writers_cv));
1218                     else if (rw_lock->waiting_readers_count > 0)
1219                         H5TS_cond_broadcast(&(rw_lock->readers_cv));
1220 #else
1221                     if (rw_lock->waiting_writers_count > 0) {
1222 
1223                         if (H5TS_cond_signal(&(rw_lock->writers_cv)) != 0)
1224                             ret_value = FAIL;
1225                     }
1226                     else if (rw_lock->waiting_readers_count > 0) {
1227 
1228                         if (H5TS_cond_broadcast(&(rw_lock->readers_cv)) != 0)
1229                             ret_value = FAIL;
1230                     }
1231 #endif
1232                     break;
1233                 default:
1234                     ret_value = FAIL;
1235                     break;
1236             }
1237         }
1238     }
1239 
1240     /* If we are really dropping the lock, must set the value of
1241      * rec_entry_count_key for this thread to NULL, so that
1242      * when this thread next requests a lock, it will appear
1243      * as an initial lock, not a recursive lock.
1244      */
1245     if (discard_rec_count) {
1246 
1247         HDassert(count);
1248 
1249         if (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)NULL) != 0) {
1250 
1251             ret_value = FAIL;
1252         }
1253 
1254         H5TS_free_rec_entry_count((void *)count);
1255         count = NULL;
1256     }
1257 
1258     if (have_mutex) {
1259 
1260         H5TS_mutex_unlock_simple(&(rw_lock->mutex));
1261     }
1262 
1263     return ret_value;
1264 
1265 } /* end H5TS_rw_unlock() */
1266 
1267 /*--------------------------------------------------------------------------
1268  * Function:    H5TS_rw_lock_get_stats
1269  *
1270  * Returns:     SUCCEED/FAIL
1271  *
1272  * Description:
1273  *    Obtain a copy of the current statistics on the supplied
1274  *    recursive read / write lock.  Note that to obtain a consistent
1275  *    set of statistics, the function must obtain the lock mutex.
1276  *
1277  * Programmer:  John Mainzer
1278  *              August 28, 2020
1279  *
1280  *--------------------------------------------------------------------------
1281  */
1282 herr_t
H5TS_rw_lock_get_stats(H5TS_rw_lock_t * rw_lock,H5TS_rw_lock_stats_t * stats)1283 H5TS_rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats)
1284 {
1285     hbool_t have_mutex = FALSE;
1286     herr_t  ret_value  = SUCCEED;
1287 
1288     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC) || (stats == NULL)) {
1289 
1290         ret_value = FAIL;
1291     }
1292 
1293     /* Obtain the mutex */
1294     if (ret_value == SUCCEED) {
1295         if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
1296             ret_value = FAIL;
1297         else
1298             have_mutex = TRUE;
1299     }
1300 
1301     if (ret_value == SUCCEED) {
1302 
1303         *stats = rw_lock->stats;
1304     }
1305 
1306     if (have_mutex) {
1307 
1308         H5TS_mutex_unlock_simple(&(rw_lock->mutex));
1309     }
1310 
1311     return ret_value;
1312 
1313 } /* end H5TS_rw_lock_get_stats() */
1314 
1315 /*--------------------------------------------------------------------------
1316  * Function:    H5TS_rw_lock_reset_stats
1317  *
1318  * Returns:     SUCCEED/FAIL
1319  *
1320  * Description:
1321  *    Reset the statistics for the supplied recursive read / write lock.
1322  *    Note that to reset the statistics consistently, the function must
1323  *    obtain the lock mutex.
1324  *
1325  * Programmer:  John Mainzer
1326  *              August 28, 2020
1327  *
1328  *--------------------------------------------------------------------------
1329  */
1330 herr_t
H5TS_rw_lock_reset_stats(H5TS_rw_lock_t * rw_lock)1331 H5TS_rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock)
1332 {
1333     hbool_t have_mutex = FALSE;
1334     /* NOTE: Update this initializer if you modify H5TS_rw_lock_stats_t */
1335     static const H5TS_rw_lock_stats_t reset_stats = {/* read_locks_granted             = */ 0,
1336                                                      /* read_locks_released            = */ 0,
1337                                                      /* real_read_locks_granted        = */ 0,
1338                                                      /* real_read_locks_released       = */ 0,
1339                                                      /* max_read_locks                 = */ 0,
1340                                                      /* max_read_lock_recursion_depth  = */ 0,
1341                                                      /* read_locks_delayed             = */ 0,
1342                                                      /* max_read_locks_pending         = */ 0,
1343                                                      /* write_locks_granted            = */ 0,
1344                                                      /* write_locks_released           = */ 0,
1345                                                      /* real_write_locks_granted       = */ 0,
1346                                                      /* real_write_locks_released      = */ 0,
1347                                                      /* max_write_locks                = */ 0,
1348                                                      /* max_write_lock_recursion_depth = */ 0,
1349                                                      /* write_locks_delayed            = */ 0,
1350                                                      /* max_write_locks_pending        = */ 0};
1351     herr_t                            ret_value   = SUCCEED;
1352 
1353     if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
1354 
1355         ret_value = FAIL;
1356     }
1357 
1358     /* Obtain the mutex */
1359     if (ret_value == SUCCEED) {
1360         if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
1361             ret_value = FAIL;
1362         else
1363             have_mutex = TRUE;
1364     }
1365 
1366     if (ret_value == SUCCEED) {
1367 
1368         rw_lock->stats = reset_stats;
1369     }
1370 
1371     if (have_mutex) {
1372 
1373         H5TS_mutex_unlock_simple(&(rw_lock->mutex));
1374     }
1375 
1376     return ret_value;
1377 
1378 } /* end H5TS_rw_lock_reset_stats() */
1379 
1380 /*--------------------------------------------------------------------------
1381  * Function:    H5TS_rw_lock_print_stats
1382  *
1383  * Returns:     SUCCEED/FAIL
1384  *
1385  * Description:
1386  *    Print the supplied pthresds recursive R/W lock statistics to
1387  *    standard out.
1388  *
1389  *    UPDATE THIS FUNCTION IF YOU MODIFY H5TS_rw_lock_stats_t.
1390  *
1391  * Programmer:  John Mainzer
1392  *              August 28, 2020
1393  *
1394  *--------------------------------------------------------------------------
1395  */
1396 herr_t
H5TS_rw_lock_print_stats(const char * header_str,H5TS_rw_lock_stats_t * stats)1397 H5TS_rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats)
1398 {
1399     herr_t ret_value = SUCCEED;
1400 
1401     if ((header_str == NULL) || (stats == NULL)) {
1402 
1403         ret_value = FAIL;
1404     }
1405     else {
1406 
1407         HDfprintf(stdout, "\n\n%s\n\n", header_str);
1408         HDfprintf(stdout, "  read_locks_granted             = %" PRId64 "\n", stats->read_locks_granted);
1409         HDfprintf(stdout, "  read_locks_released            = %" PRId64 "\n", stats->read_locks_released);
1410         HDfprintf(stdout, "  real_read_locks_granted        = %" PRId64 "\n", stats->real_read_locks_granted);
1411         HDfprintf(stdout, "  real_read_locks_released       = %" PRId64 "\n",
1412                   stats->real_read_locks_released);
1413         HDfprintf(stdout, "  max_read_locks                 = %" PRId64 "\n", stats->max_read_locks);
1414         HDfprintf(stdout, "  max_read_lock_recursion_depth  = %" PRId64 "\n",
1415                   stats->max_read_lock_recursion_depth);
1416         HDfprintf(stdout, "  read_locks_delayed             = %" PRId64 "\n", stats->read_locks_delayed);
1417         HDfprintf(stdout, "  max_read_locks_pending         = %" PRId64 "\n", stats->max_read_locks_pending);
1418         HDfprintf(stdout, "  write_locks_granted            = %" PRId64 "\n", stats->write_locks_granted);
1419         HDfprintf(stdout, "  write_locks_released           = %" PRId64 "\n", stats->write_locks_released);
1420         HDfprintf(stdout, "  real_write_locks_granted       = %" PRId64 "\n",
1421                   stats->real_write_locks_granted);
1422         HDfprintf(stdout, "  real_write_locks_released      = %" PRId64 "\n",
1423                   stats->real_write_locks_released);
1424         HDfprintf(stdout, "  max_write_locks                = %" PRId64 "\n", stats->max_write_locks);
1425         HDfprintf(stdout, "  max_write_lock_recursion_depth = %" PRId64 "\n",
1426                   stats->max_write_lock_recursion_depth);
1427         HDfprintf(stdout, "  write_locks_delayed            = %" PRId64 "\n", stats->write_locks_delayed);
1428         HDfprintf(stdout, "  max_write_locks_pending        = %" PRId64 "\n\n",
1429                   stats->max_write_locks_pending);
1430     }
1431 
1432     return ret_value;
1433 
1434 } /* end H5TS_rw_lock_print_stats() */
1435 
1436 #endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
1437 
1438 /*--------------------------------------------------------------------------
1439  * NAME
1440  *    H5TS_create_thread
1441  *
1442  * RETURNS
1443  *    Thread identifier.
1444  *
1445  * DESCRIPTION
1446  *    Spawn off a new thread calling function 'func' with input 'udata'.
1447  *
1448  * PROGRAMMER: Mike McGreevy
1449  *             August 31, 2010
1450  *
1451  *--------------------------------------------------------------------------
1452  */
1453 H5TS_thread_t
H5TS_create_thread(void * (* func)(void *),H5TS_attr_t * attr,void * udata)1454 H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
1455 {
1456     H5TS_thread_t ret_value;
1457 
1458 #ifdef H5_HAVE_WIN_THREADS
1459     /* When calling C runtime functions, you should use _beginthread or
1460      * _beginthreadex instead of CreateThread.  Threads created with
1461      * CreateThread risk being killed in low-memory situations. Since we
1462      * only create threads in our test code, this is unlikely to be an issue
1463      * and we'll use the easier-to-deal-with CreateThread for now.
1464      *
1465      * NOTE: _beginthread() auto-recycles its handle when execution completes
1466      *       so you can't wait on it, making it unsuitable for the existing
1467      *       test code.
1468      */
1469     ret_value = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, udata, 0, NULL);
1470 
1471 #else /* H5_HAVE_WIN_THREADS */
1472 
1473     pthread_create(&ret_value, attr, (void *(*)(void *))func, udata);
1474 
1475 #endif /* H5_HAVE_WIN_THREADS */
1476 
1477     return ret_value;
1478 } /* end H5TS_create_thread() */
1479 
1480 #endif /* H5_HAVE_THREADSAFE */
1481