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 *
16 * Created: H5TSprivate.h
17 *
18 * Purpose: Thread-safety abstractions used by the library
19 *
20 *-------------------------------------------------------------------------
21 */
22 #ifndef H5TSprivate_H_
23 #define H5TSprivate_H_
24
25 #ifdef H5_HAVE_THREADSAFE
26
27 /* Public headers needed by this file */
28 #ifdef LATER
29 #include "H5TSpublic.h" /* Public API prototypes */
30 #endif /* LATER */
31
32 /**************************/
33 /* Library Private Macros */
34 /**************************/
35
36 /* Defines */
37
38 #ifdef H5_HAVE_WIN_THREADS
39
40 /* Scope Definitions (Pthreads only) */
41 #define H5TS_SCOPE_SYSTEM 0
42 #define H5TS_SCOPE_PROCESS 0
43
44 /* Calling convention (Windows only) */
45 #define H5TS_CALL_CONV WINAPI
46
47 /* Portability function aliases */
48 #define H5TS_get_thread_local_value(key) TlsGetValue(key)
49 #define H5TS_set_thread_local_value(key, value) TlsSetValue(key, value)
50 #define H5TS_attr_init(attr) 0
51 #define H5TS_attr_setscope(attr, scope) 0
52 #define H5TS_attr_destroy(attr) 0
53 #define H5TS_wait_for_thread(thread) WaitForSingleObject(thread, INFINITE)
54 #define H5TS_mutex_init(mutex) InitializeCriticalSection(mutex)
55 #define H5TS_mutex_lock_simple(mutex) EnterCriticalSection(mutex)
56 #define H5TS_mutex_unlock_simple(mutex) LeaveCriticalSection(mutex)
57
58 /* No Pthreads equivalent - we use a custom H5TS call with that thread library */
59 #define H5TS_thread_id() ((uint64_t)GetCurrentThreadId())
60
61 #else
62
63 /* Scope Definitions (Pthreads only) */
64 #define H5TS_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM
65 #define H5TS_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS
66
67 /* Calling convention (Windows only) */
68 #define H5TS_CALL_CONV /* N/A */
69
70 /* Portability function aliases */
71 #define H5TS_get_thread_local_value(key) pthread_getspecific(key)
72 #define H5TS_set_thread_local_value(key, value) pthread_setspecific(key, value)
73 #define H5TS_attr_init(attr) pthread_attr_init((attr))
74 #define H5TS_attr_setscope(attr, scope) pthread_attr_setscope(attr, scope)
75 #define H5TS_attr_destroy(attr) pthread_attr_destroy(attr)
76 #define H5TS_wait_for_thread(thread) pthread_join(thread, NULL)
77 #define H5TS_mutex_init(mutex) pthread_mutex_init(mutex, NULL)
78 #define H5TS_mutex_lock_simple(mutex) pthread_mutex_lock(mutex)
79 #define H5TS_mutex_unlock_simple(mutex) pthread_mutex_unlock(mutex)
80
81 /* No Win32 thread equivalent - only needed for RW locks which are not supported
82 * under Windows threads.
83 */
84 #define H5TS_mutex_destroy(mutex) pthread_mutex_destroy(mutex)
85 #define H5TS_cond_init(cond) pthread_cond_init(cond, NULL)
86 #define H5TS_cond_destroy(cond) pthread_cond_destroy(cond)
87 #define H5TS_cond_wait(cond, mutex) pthread_cond_wait(cond, mutex)
88 #define H5TS_cond_signal(cond) pthread_cond_signal(cond)
89 #define H5TS_cond_broadcast(cond) pthread_cond_broadcast(cond)
90
91 #endif /* H5_HAVE_WIN_THREADS */
92
93 /******************************************************************************
94 * Macros to maintain statistics on the Pthreads recursive R/W lock.
95 ******************************************************************************/
96
97 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
98
99 /* Magic values for struct sanity checking */
100
101 /* RW lock */
102 #define H5TS_RW_LOCK_MAGIC 0XABCD
103
104 /* RW lock entry counts */
105 #define H5TS_RW_ENTRY_COUNT_MAGIC 0XABBA
106
107 /* Flag for favoring writers */
108 /* THIS SHOULD BE AN ENUM */
109 #define H5TS_RW_LOCK_POLICY_FAVOR_WRITERS 0
110
111 #endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
112
113 /****************************/
114 /* Library Private Typedefs */
115 /****************************/
116
117 /* Mutexes, Threads, and Attributes */
118
119 #ifdef H5_HAVE_WIN_THREADS
120
121 typedef struct H5TS_mutex_struct {
122 CRITICAL_SECTION CriticalSection;
123 } H5TS_mutex_t;
124
125 #else
126
127 typedef struct H5TS_mutex_struct {
128 pthread_t owner_thread; /* current lock owner */
129 pthread_mutex_t atomic_lock; /* lock for atomicity of new mechanism */
130 pthread_cond_t cond_var; /* condition variable */
131 unsigned int lock_count;
132 } H5TS_mutex_t;
133
134 #endif /* H5_HAVE_WIN_THREADS */
135
136 /* Portability wrappers */
137
138 #ifdef H5_HAVE_WIN_THREADS
139
140 typedef HANDLE H5TS_thread_t;
141 typedef HANDLE H5TS_attr_t;
142 typedef CRITICAL_SECTION H5TS_mutex_simple_t;
143 typedef DWORD H5TS_key_t;
144 typedef INIT_ONCE H5TS_once_t;
145 typedef CONDITION_VARIABLE H5TS_cond_t;
146
147 #else
148
149 typedef pthread_t H5TS_thread_t;
150 typedef pthread_attr_t H5TS_attr_t;
151 typedef pthread_mutex_t H5TS_mutex_simple_t;
152 typedef pthread_key_t H5TS_key_t;
153 typedef pthread_once_t H5TS_once_t;
154 typedef pthread_cond_t H5TS_cond_t;
155
156 #endif /* H5_HAVE_WIN_THREADS */
157
158 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
159
160 /******************************************************************************
161 *
162 * Structure H5TS_rw_lock_stats_t
163 *
164 * Catchall structure for statistics on the recursive p-threads based
165 * recursive R/W lock (see declaration of H5TS_rw_lock_t below).
166 *
167 * Since the mutex must be held when reading a consistent set of statistics
168 * from the recursibe R/W lock, it simplifies matters to bundle them into
169 * a single structure. This structure exists for that purpose.
170 *
171 * If you modify this structure, be sure to make equivalent changes to
172 * the reset_stats initializer in H5TS_rw_lock_reset_stats().
173 *
174 * Individual fields are discussed below.
175 *
176 * JRM -- 8/28/20
177 *
178 * Read lock stats:
179 *
180 * read_locks_granted: 64 bit integer used to count the total number of read
181 * locks granted. Note that this includes recursive lock
182 * requests.
183 *
184 * read_locks_released: 64 bit integer used to count the total number of read
185 * locks released. Note that this includes recursive lock
186 * release requests.
187 *
188 * real_read_locks_granted: 64 bit integer used to count the total number of
189 * read locks granted, less any recursive lock requests.
190 *
191 * real_read_locks_released: 64 bit integer used to count the total number of
192 * read locks released, less any recursive lock releases.
193 *
194 * max_read_locks; 64 bit integer used to track the maximum number of read
195 * locks active at any point in time.
196 *
197 * max_read_lock_recursion_depth; 64 bit integer used to track the maximum
198 * recursion depth observed for any read lock.
199 *
200 * read_locks_delayed: 64 bit integer used to track the number of read locks
201 * that were not granted immediately.
202 *
203 * max_read_locks_delayed; 64 bit integer used to track the maximum number of
204 * pending read locks at any point in time.
205 *
206 *
207 * Write lock stats:
208 *
209 * write_locks_granted: 64 bit integer used to count the total number of write
210 * locks granted. Note that this includes recursive lock
211 * requests.
212 *
213 * write_locks_released: 64 bit integer used to count the total number of write
214 * locks released. Note that this includes recursive lock
215 * release requests.
216 *
217 * real_write_locks_granted: 64 bit integer used to count the total number of
218 * write locks granted, less any recursive lock requests.
219 *
220 * real_write_locks_released: 64 bit integer used to count the total number of
221 * write locks released, less any recursive lock releases.
222 *
223 * max_write_locks; 64 bit integer used to track the maximum number of write
224 * locks active at any point in time. Must be either zero or one.
225 *
226 * max_write_lock_recursion_depth; 64 bit integer used to track the maximum
227 * recursion depth observed for any write lock.
228 *
229 * write_locks_delayed: 64 bit integer used to track the number of write locks
230 * that were not granted immediately.
231 *
232 * max_write_locks_delayed; 64 bit integer used to track the maximum number of
233 * pending write locks at any point in time.
234 *
235 ******************************************************************************/
236
237 typedef struct H5TS_rw_lock_stats_t {
238
239 int64_t read_locks_granted;
240 int64_t read_locks_released;
241 int64_t real_read_locks_granted;
242 int64_t real_read_locks_released;
243 int64_t max_read_locks;
244 int64_t max_read_lock_recursion_depth;
245 int64_t read_locks_delayed;
246 int64_t max_read_locks_pending;
247 int64_t write_locks_granted;
248 int64_t write_locks_released;
249 int64_t real_write_locks_granted;
250 int64_t real_write_locks_released;
251 int64_t max_write_locks;
252 int64_t max_write_lock_recursion_depth;
253 int64_t write_locks_delayed;
254 int64_t max_write_locks_pending;
255
256 } H5TS_rw_lock_stats_t;
257
258 /******************************************************************************
259 *
260 * Structure H5TS_rw_lock_t
261 *
262 * A read / write lock, is a lock that allows either an arbitrary number
263 * of readers, or a single writer into a critical region. A recurssive
264 * lock is one that allows a thread that already has a lock (be it read or
265 * write) to successfully request the lock again, only dropping the lock
266 * when the number of un-lock calls equals the number of lock calls.
267 *
268 * Note that we can't use the Pthreads or Win32 R/W locks, as while they
269 * permit recursive read locks, they disallow recursive write locks.
270 *
271 * This structure is a catchall for the fields needed to implement a
272 * recursive R/W lock that allows recursive write locks, and for the
273 * associate statistics collection fields.
274 *
275 * This recursive R/W lock implementation is an extension of the R/W lock
276 * implementation given in "UNIX network programming" Volume 2, Chapter 8
277 * by w. Richard Stevens, 2nd edition.
278 *
279 * Individual fields are discussed below.
280 *
281 * JRM -- 8/28/20
282 *
283 * magic: Unsigned 32 bit integer field used for sanity checking. This
284 * fields must always be set to H5TS_RW_LOCK_MAGIC.
285 * If this structure is allocated dynamically, remember to set
286 * it to some invalid value before discarding the structure.
287 *
288 * policy Integer containing a code indicating the precidence policy
289 * used by the R/W lock. The supported policies are listed
290 * below:
291 *
292 * H5TS__RW_LOCK_POLICY__FAVOR_WRITERS:
293 *
294 * If selected, the R/W lock will grant access to a pending
295 * writer if there are both pending readers and writers.
296 *
297 *
298 * --- Define other policies here ---
299 *
300 *
301 * mutex: Mutex used to maintain mutual exclusion on the fields of
302 * of this structure.
303 *
304 * readers_cv: Condition variable used for waiting readers.
305 *
306 * writers_cv: Condition variable used for waiting writers.
307 *
308 * waiting_readers_count: 32 bit integer used to maintain a count of
309 * waiting readers. This value should always be non-negative.
310 *
311 * waiting_writers_count: 32 bit integer used to maintain a count of
312 * waiting writers. This value should always be non-negative.
313 *
314 * The following two fields could be combined into a single field, with
315 * the count of active readers being represented by a positive value, and
316 * the number of writers by a negative value. Two fields are used to
317 * facilitate sanity checking.
318 *
319 * active_readers: 32 bit integer used to maintain a count of
320 * readers that currently hold a read lock. This value
321 * must be zero if active_writers is positive. It should
322 * never be negative.
323 *
324 * active_writers: 32 bit integer used to maintain a count of
325 * writers that currently hold a write lock. This value
326 * must always be either 0 or 1, and must be zero if
327 * active_readers is positive. It should never be negative.
328 *
329 * rec_entry_count_key: Instance of thread-local key used to maintain
330 * a thread specific lock type and recursive entry count
331 * for all threads holding a lock.
332 *
333 * stats: Instance of H5TS_rw_lock_stats_t used to track
334 * statistics on the recursive R/W lock. See the declaration
335 * of the structure for discussion of its fields.
336 *
337 * Note that the stats are gathered into a structure because
338 * we must obtain the mutex when reading the statistics to
339 * avoid changes while the statistics are being read. Collecting
340 * them into a structure facilitates this.
341 *
342 ******************************************************************************/
343
344 typedef struct H5TS_rw_lock_t {
345
346 uint32_t magic;
347 int32_t policy;
348 H5TS_mutex_simple_t mutex;
349 H5TS_cond_t readers_cv;
350 H5TS_cond_t writers_cv;
351 int32_t waiting_readers_count;
352 int32_t waiting_writers_count;
353 int32_t active_readers;
354 int32_t active_writers;
355 H5TS_key_t rec_entry_count_key;
356 int32_t writer_rec_entry_count;
357 struct H5TS_rw_lock_stats_t stats;
358
359 } H5TS_rw_lock_t;
360
361 /******************************************************************************
362 *
363 * Structure H5TS_rec_entry_count
364 *
365 * Strucure associated with the reader_rec_entry_count_key defined in
366 * H5TS_rw_lock_t.
367 *
368 * The primary purpose of this structure is to maintain a count of recursive
369 * locks so that the lock can be dropped when the count drops to zero.
370 *
371 * Aditional fields are included for purposes of sanity checking.
372 *
373 * Individual fields are discussed below.
374 *
375 * JRM -- 8/28/20
376 *
377 * magic: Unsigned 32 bit integer field used for sanity checking. This
378 * field must always be set to H5TS_RW_ENTRY_COUNT_MAGIC, and
379 * should be set to some invalid value just before the structure
380 * is freed.
381 *
382 * write_lock: Boolean field that is set to TRUE if the count is for a write
383 * lock, and to FALSE if it is for a read lock.
384 *
385 * rec_lock_count: Count of the number of recursive lock calls, less
386 * the number of recursive unlock calls. The lock in question
387 * is dropped when the count drops to zero.
388 *
389 ******************************************************************************/
390
391 typedef struct H5TS_rec_entry_count {
392
393 uint32_t magic;
394 hbool_t write_lock;
395 int64_t rec_lock_count;
396
397 } H5TS_rec_entry_count;
398
399 #endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
400
401 /*****************************/
402 /* Library-private Variables */
403 /*****************************/
404
405 /* Library-scope global variables */
406
407 /* Library initialization */
408 extern H5TS_once_t H5TS_first_init_g;
409
410 /* Error stacks */
411 extern H5TS_key_t H5TS_errstk_key_g;
412
413 /* Function stacks */
414 #ifdef H5_HAVE_CODESTACK
415 extern H5TS_key_t H5TS_funcstk_key_g;
416 #endif
417
418 /* API contexts */
419 extern H5TS_key_t H5TS_apictx_key_g;
420
421 /***********************************/
422 /* Private static inline functions */
423 /***********************************/
424
425 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
426
427 static inline void
H5TS_update_stats_rd_lock(H5TS_rw_lock_t * rw_lock,H5TS_rec_entry_count * count)428 H5TS_update_stats_rd_lock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
429 {
430 HDassert(rw_lock);
431 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
432 HDassert(count);
433 HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
434 HDassert(count->rec_lock_count >= 1);
435 HDassert(!count->write_lock);
436
437 rw_lock->stats.read_locks_granted++;
438
439 if (count->rec_lock_count == 1) {
440
441 rw_lock->stats.real_read_locks_granted++;
442
443 if (rw_lock->active_readers > rw_lock->stats.max_read_locks)
444 rw_lock->stats.max_read_locks = rw_lock->active_readers;
445 }
446
447 if (count->rec_lock_count > rw_lock->stats.max_read_lock_recursion_depth)
448 rw_lock->stats.max_read_lock_recursion_depth = count->rec_lock_count;
449
450 } /* end H5TS_update_stats_rd_lock() */
451
452 static inline void
H5TS_update_stats_rd_lock_delay(H5TS_rw_lock_t * rw_lock,int waiting_count)453 H5TS_update_stats_rd_lock_delay(H5TS_rw_lock_t *rw_lock, int waiting_count)
454 {
455 HDassert(rw_lock);
456 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
457 HDassert((waiting_count) > 0);
458
459 rw_lock->stats.read_locks_delayed++;
460
461 if (rw_lock->stats.max_read_locks_pending < waiting_count)
462 rw_lock->stats.max_read_locks_pending = (waiting_count);
463
464 } /* end H5TS_update_stats_rd_lock_delay() */
465
466 static inline void
H5TS_update_stats_rd_unlock(H5TS_rw_lock_t * rw_lock,H5TS_rec_entry_count * count)467 H5TS_update_stats_rd_unlock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
468 {
469 HDassert(rw_lock);
470 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
471 HDassert(count);
472 HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
473 HDassert(count->rec_lock_count >= 0);
474 HDassert(!count->write_lock);
475
476 rw_lock->stats.read_locks_released++;
477
478 if (count->rec_lock_count == 0)
479 rw_lock->stats.real_read_locks_released++;
480
481 } /* end H5TS_update_stats_rd_unlock() */
482
483 static inline void
H5TS_update_stats_wr_lock(H5TS_rw_lock_t * rw_lock,H5TS_rec_entry_count * count)484 H5TS_update_stats_wr_lock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
485 {
486 HDassert(rw_lock);
487 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
488 HDassert(count);
489 HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
490 HDassert(count->rec_lock_count >= 1);
491 HDassert(count->write_lock);
492
493 rw_lock->stats.write_locks_granted++;
494
495 if (count->rec_lock_count == 1) {
496
497 rw_lock->stats.real_write_locks_granted++;
498
499 if (rw_lock->active_writers > rw_lock->stats.max_write_locks)
500 rw_lock->stats.max_write_locks = rw_lock->active_writers;
501 }
502
503 if (count->rec_lock_count > rw_lock->stats.max_write_lock_recursion_depth)
504 rw_lock->stats.max_write_lock_recursion_depth = count->rec_lock_count;
505
506 } /* end H5TS_update_stats_wr_lock() */
507
508 static inline void
H5TS_update_stats_wr_lock_delay(H5TS_rw_lock_t * rw_lock,int waiting_count)509 H5TS_update_stats_wr_lock_delay(H5TS_rw_lock_t *rw_lock, int waiting_count)
510 {
511 HDassert(rw_lock);
512 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
513 HDassert(waiting_count > 0);
514
515 rw_lock->stats.write_locks_delayed++;
516
517 if (rw_lock->stats.max_write_locks_pending < waiting_count)
518 rw_lock->stats.max_write_locks_pending = waiting_count;
519
520 } /* end H5TS_update_stats_wr_lock_delay() */
521
522 static inline void
H5TS_update_stats_wr_unlock(H5TS_rw_lock_t * rw_lock,H5TS_rec_entry_count * count)523 H5TS_update_stats_wr_unlock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
524 {
525 HDassert(rw_lock);
526 HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
527 HDassert(count);
528 HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
529 HDassert(count->rec_lock_count >= 0);
530 HDassert(count->write_lock);
531
532 rw_lock->stats.write_locks_released++;
533
534 if (count->rec_lock_count == 0)
535 rw_lock->stats.real_write_locks_released++;
536
537 } /* end H5TS_update_stats_wr_unlock() */
538 #endif
539
540 /***************************************/
541 /* Library-private Function Prototypes */
542 /***************************************/
543
544 /* Platform-specific functions */
545 #ifdef H5_HAVE_WIN_THREADS
546
547 /* Functions called from DllMain */
548 H5_DLL BOOL CALLBACK H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex);
549 H5_DLL void H5TS_win32_process_exit(void);
550 H5_DLL herr_t H5TS_win32_thread_enter(void);
551 H5_DLL herr_t H5TS_win32_thread_exit(void);
552
553 #else
554
555 H5_DLL uint64_t H5TS_thread_id(void);
556 H5_DLL void H5TS_pthread_first_thread_init(void);
557
558 #endif /* H5_HAVE_WIN_THREADS */
559
560 /* Library-scope routines */
561 /* (Only used within H5private.h macros) */
562 H5_DLL herr_t H5TS_mutex_lock(H5TS_mutex_t *mutex);
563 H5_DLL herr_t H5TS_mutex_unlock(H5TS_mutex_t *mutex);
564 H5_DLL herr_t H5TS_cancel_count_inc(void);
565 H5_DLL herr_t H5TS_cancel_count_dec(void);
566
567 /* Fully recursive R/W lock related function declarations */
568 #ifdef H5_USE_RECURSIVE_WRITER_LOCKS
569 H5_DLL H5TS_rec_entry_count *H5TS_alloc_rec_entry_count(hbool_t write_lock);
570 H5_DLL void H5TS_free_rec_entry_count(void *target);
571 H5_DLL herr_t H5TS_rw_lock_init(H5TS_rw_lock_t *rw_lock, int policy);
572 H5_DLL herr_t H5TS_rw_lock_destroy(H5TS_rw_lock_t *rw_lock);
573 H5_DLL herr_t H5TS_rw_rdlock(H5TS_rw_lock_t *rw_lock);
574 H5_DLL herr_t H5TS_rw_wrlock(H5TS_rw_lock_t *rw_lock);
575 H5_DLL herr_t H5TS_rw_unlock(H5TS_rw_lock_t *rw_lock);
576 H5_DLL herr_t H5TS_rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats);
577 H5_DLL herr_t H5TS_rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock);
578 H5_DLL herr_t H5TS_rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats);
579 #endif
580
581 /* Testing routines */
582 H5_DLL H5TS_thread_t H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata);
583
584 #else /* H5_HAVE_THREADSAFE */
585
586 /* Non-threadsafe code needs this */
587 #define H5TS_thread_id() ((uint64_t)0)
588
589 #endif /* H5_HAVE_THREADSAFE */
590
591 #endif /* H5TSprivate_H_ */
592