1 /**
2  * Copyright (C) Mellanox Technologies Ltd. 2001-2018.  ALL RIGHTS RESERVED.
3  *
4  * See file LICENSE for terms.
5  */
6 
7 #ifndef UCS_TYPE_INIT_ONCE_H_
8 #define UCS_TYPE_INIT_ONCE_H_
9 
10 
11 #include <pthread.h>
12 
13 
14 /*
15  * Synchronization object for one-time initialization.
16  */
17 typedef struct ucs_init_once {
18     pthread_mutex_t lock;        /* Protects the initialization */
19     int             initialized; /* Whether the initialization took place */
20 } ucs_init_once_t;
21 
22 
23 /* Static initializer for @ref ucs_init_once_t */
24 #define UCS_INIT_ONCE_INITIALIZER \
25     { PTHREAD_MUTEX_INITIALIZER, 0 }
26 
27 
28 /* Wrapper to unlock a mutex that always returns 0 to avoid endless loop
29  * and make static analyzers happy - they report "double unlock" warning */
30 unsigned ucs_init_once_mutex_unlock(pthread_mutex_t *lock);
31 
32 
33 /*
34  * Start a code block to perform an arbitrary initialization step only once
35  * during the lifetime of the provided synchronization object.
36  *
37  * @param [in] _once Pointer to @ref ucs_init_once_t synchronization object.
38  *
39  * Usage:
40  *     UCS_INIT_ONCE(&once) {
41  *         ... code ...
42  *     }
43  *
44  * @note It's safe to use a "continue" statement in order to exit the code block,
45  * but "return" and "break" statements may lead to unexpected behavior.
46  *
47  * How does it work? First, lock the mutex. Then check if already initialized,
48  * if yes unlock the mutex and exit the loop (pthread_mutex_unlock is expected
49  * to return 0). Otherwise, perform the "body" of the for loop, and then set
50  * "initialized" to 1. On the next condition check, unlock the mutex and exit.
51  */
52 #define UCS_INIT_ONCE(_once) \
53     for (pthread_mutex_lock(&(_once)->lock); \
54          !(_once)->initialized || ucs_init_once_mutex_unlock(&(_once)->lock); \
55          (_once)->initialized = 1)
56 
57 #endif
58