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