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