10b57cec5SDimitry Andric /* 20b57cec5SDimitry Andric * kmp_lock.h -- lock header file 30b57cec5SDimitry Andric */ 40b57cec5SDimitry Andric 50b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 80b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 90b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #ifndef KMP_LOCK_H 140b57cec5SDimitry Andric #define KMP_LOCK_H 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include <limits.h> // CHAR_BIT 170b57cec5SDimitry Andric #include <stddef.h> // offsetof 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric #include "kmp_debug.h" 200b57cec5SDimitry Andric #include "kmp_os.h" 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric #ifdef __cplusplus 230b57cec5SDimitry Andric #include <atomic> 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric extern "C" { 260b57cec5SDimitry Andric #endif // __cplusplus 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 290b57cec5SDimitry Andric // Have to copy these definitions from kmp.h because kmp.h cannot be included 300b57cec5SDimitry Andric // due to circular dependencies. Will undef these at end of file. 310b57cec5SDimitry Andric 320b57cec5SDimitry Andric #define KMP_PAD(type, sz) \ 330b57cec5SDimitry Andric (sizeof(type) + (sz - ((sizeof(type) - 1) % (sz)) - 1)) 340b57cec5SDimitry Andric #define KMP_GTID_DNE (-2) 350b57cec5SDimitry Andric 360b57cec5SDimitry Andric // Forward declaration of ident and ident_t 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric struct ident; 390b57cec5SDimitry Andric typedef struct ident ident_t; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric // End of copied code. 420b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric // We need to know the size of the area we can assume that the compiler(s) 450b57cec5SDimitry Andric // allocated for obects of type omp_lock_t and omp_nest_lock_t. The Intel 460b57cec5SDimitry Andric // compiler always allocates a pointer-sized area, as does visual studio. 470b57cec5SDimitry Andric // 480b57cec5SDimitry Andric // gcc however, only allocates 4 bytes for regular locks, even on 64-bit 490b57cec5SDimitry Andric // intel archs. It allocates at least 8 bytes for nested lock (more on 500b57cec5SDimitry Andric // recent versions), but we are bounded by the pointer-sized chunks that 510b57cec5SDimitry Andric // the Intel compiler allocates. 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric #if KMP_OS_LINUX && defined(KMP_GOMP_COMPAT) 540b57cec5SDimitry Andric #define OMP_LOCK_T_SIZE sizeof(int) 550b57cec5SDimitry Andric #define OMP_NEST_LOCK_T_SIZE sizeof(void *) 560b57cec5SDimitry Andric #else 570b57cec5SDimitry Andric #define OMP_LOCK_T_SIZE sizeof(void *) 580b57cec5SDimitry Andric #define OMP_NEST_LOCK_T_SIZE sizeof(void *) 590b57cec5SDimitry Andric #endif 600b57cec5SDimitry Andric 610b57cec5SDimitry Andric // The Intel compiler allocates a 32-byte chunk for a critical section. 620b57cec5SDimitry Andric // Both gcc and visual studio only allocate enough space for a pointer. 630b57cec5SDimitry Andric // Sometimes we know that the space was allocated by the Intel compiler. 640b57cec5SDimitry Andric #define OMP_CRITICAL_SIZE sizeof(void *) 650b57cec5SDimitry Andric #define INTEL_CRITICAL_SIZE 32 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric // lock flags 680b57cec5SDimitry Andric typedef kmp_uint32 kmp_lock_flags_t; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric #define kmp_lf_critical_section 1 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric // When a lock table is used, the indices are of kmp_lock_index_t 730b57cec5SDimitry Andric typedef kmp_uint32 kmp_lock_index_t; 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric // When memory allocated for locks are on the lock pool (free list), 760b57cec5SDimitry Andric // it is treated as structs of this type. 770b57cec5SDimitry Andric struct kmp_lock_pool { 780b57cec5SDimitry Andric union kmp_user_lock *next; 790b57cec5SDimitry Andric kmp_lock_index_t index; 800b57cec5SDimitry Andric }; 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric typedef struct kmp_lock_pool kmp_lock_pool_t; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric extern void __kmp_validate_locks(void); 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 870b57cec5SDimitry Andric // There are 5 lock implementations: 880b57cec5SDimitry Andric // 1. Test and set locks. 890b57cec5SDimitry Andric // 2. futex locks (Linux* OS on x86 and 900b57cec5SDimitry Andric // Intel(R) Many Integrated Core Architecture) 910b57cec5SDimitry Andric // 3. Ticket (Lamport bakery) locks. 920b57cec5SDimitry Andric // 4. Queuing locks (with separate spin fields). 930b57cec5SDimitry Andric // 5. DRPA (Dynamically Reconfigurable Distributed Polling Area) locks 940b57cec5SDimitry Andric // 950b57cec5SDimitry Andric // and 3 lock purposes: 960b57cec5SDimitry Andric // 1. Bootstrap locks -- Used for a few locks available at library 970b57cec5SDimitry Andric // startup-shutdown time. 980b57cec5SDimitry Andric // These do not require non-negative global thread ID's. 990b57cec5SDimitry Andric // 2. Internal RTL locks -- Used everywhere else in the RTL 1000b57cec5SDimitry Andric // 3. User locks (includes critical sections) 1010b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric // ============================================================================ 1040b57cec5SDimitry Andric // Lock implementations. 1050b57cec5SDimitry Andric // 1060b57cec5SDimitry Andric // Test and set locks. 1070b57cec5SDimitry Andric // 1080b57cec5SDimitry Andric // Non-nested test and set locks differ from the other lock kinds (except 1090b57cec5SDimitry Andric // futex) in that we use the memory allocated by the compiler for the lock, 1100b57cec5SDimitry Andric // rather than a pointer to it. 1110b57cec5SDimitry Andric // 1120b57cec5SDimitry Andric // On lin32, lin_32e, and win_32, the space allocated may be as small as 4 1130b57cec5SDimitry Andric // bytes, so we have to use a lock table for nested locks, and avoid accessing 1140b57cec5SDimitry Andric // the depth_locked field for non-nested locks. 1150b57cec5SDimitry Andric // 1160b57cec5SDimitry Andric // Information normally available to the tools, such as lock location, lock 1170b57cec5SDimitry Andric // usage (normal lock vs. critical section), etc. is not available with test and 1180b57cec5SDimitry Andric // set locks. 1190b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric struct kmp_base_tas_lock { 1220b57cec5SDimitry Andric // KMP_LOCK_FREE(tas) => unlocked; locked: (gtid+1) of owning thread 1230b57cec5SDimitry Andric std::atomic<kmp_int32> poll; 1240b57cec5SDimitry Andric kmp_int32 depth_locked; // depth locked, for nested locks only 1250b57cec5SDimitry Andric }; 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric typedef struct kmp_base_tas_lock kmp_base_tas_lock_t; 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric union kmp_tas_lock { 1300b57cec5SDimitry Andric kmp_base_tas_lock_t lk; 1310b57cec5SDimitry Andric kmp_lock_pool_t pool; // make certain struct is large enough 1320b57cec5SDimitry Andric double lk_align; // use worst case alignment; no cache line padding 1330b57cec5SDimitry Andric }; 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric typedef union kmp_tas_lock kmp_tas_lock_t; 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric // Static initializer for test and set lock variables. Usage: 1380b57cec5SDimitry Andric // kmp_tas_lock_t xlock = KMP_TAS_LOCK_INITIALIZER( xlock ); 1390b57cec5SDimitry Andric #define KMP_TAS_LOCK_INITIALIZER(lock) \ 1400b57cec5SDimitry Andric { \ 1410b57cec5SDimitry Andric { ATOMIC_VAR_INIT(KMP_LOCK_FREE(tas)), 0 } \ 1420b57cec5SDimitry Andric } 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric extern int __kmp_acquire_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1450b57cec5SDimitry Andric extern int __kmp_test_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1460b57cec5SDimitry Andric extern int __kmp_release_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1470b57cec5SDimitry Andric extern void __kmp_init_tas_lock(kmp_tas_lock_t *lck); 1480b57cec5SDimitry Andric extern void __kmp_destroy_tas_lock(kmp_tas_lock_t *lck); 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric extern int __kmp_acquire_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1510b57cec5SDimitry Andric extern int __kmp_test_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1520b57cec5SDimitry Andric extern int __kmp_release_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid); 1530b57cec5SDimitry Andric extern void __kmp_init_nested_tas_lock(kmp_tas_lock_t *lck); 1540b57cec5SDimitry Andric extern void __kmp_destroy_nested_tas_lock(kmp_tas_lock_t *lck); 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric #define KMP_LOCK_RELEASED 1 1570b57cec5SDimitry Andric #define KMP_LOCK_STILL_HELD 0 1580b57cec5SDimitry Andric #define KMP_LOCK_ACQUIRED_FIRST 1 1590b57cec5SDimitry Andric #define KMP_LOCK_ACQUIRED_NEXT 0 1600b57cec5SDimitry Andric #ifndef KMP_USE_FUTEX 1610b57cec5SDimitry Andric #define KMP_USE_FUTEX \ 1620b57cec5SDimitry Andric (KMP_OS_LINUX && !KMP_OS_CNK && \ 1630b57cec5SDimitry Andric (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64)) 1640b57cec5SDimitry Andric #endif 1650b57cec5SDimitry Andric #if KMP_USE_FUTEX 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 1680b57cec5SDimitry Andric // futex locks. futex locks are only available on Linux* OS. 1690b57cec5SDimitry Andric // 1700b57cec5SDimitry Andric // Like non-nested test and set lock, non-nested futex locks use the memory 1710b57cec5SDimitry Andric // allocated by the compiler for the lock, rather than a pointer to it. 1720b57cec5SDimitry Andric // 1730b57cec5SDimitry Andric // Information normally available to the tools, such as lock location, lock 1740b57cec5SDimitry Andric // usage (normal lock vs. critical section), etc. is not available with test and 1750b57cec5SDimitry Andric // set locks. With non-nested futex locks, the lock owner is not even available. 1760b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric struct kmp_base_futex_lock { 1790b57cec5SDimitry Andric volatile kmp_int32 poll; // KMP_LOCK_FREE(futex) => unlocked 1800b57cec5SDimitry Andric // 2*(gtid+1) of owning thread, 0 if unlocked 1810b57cec5SDimitry Andric // locked: (gtid+1) of owning thread 1820b57cec5SDimitry Andric kmp_int32 depth_locked; // depth locked, for nested locks only 1830b57cec5SDimitry Andric }; 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric typedef struct kmp_base_futex_lock kmp_base_futex_lock_t; 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric union kmp_futex_lock { 1880b57cec5SDimitry Andric kmp_base_futex_lock_t lk; 1890b57cec5SDimitry Andric kmp_lock_pool_t pool; // make certain struct is large enough 1900b57cec5SDimitry Andric double lk_align; // use worst case alignment 1910b57cec5SDimitry Andric // no cache line padding 1920b57cec5SDimitry Andric }; 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric typedef union kmp_futex_lock kmp_futex_lock_t; 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric // Static initializer for futex lock variables. Usage: 1970b57cec5SDimitry Andric // kmp_futex_lock_t xlock = KMP_FUTEX_LOCK_INITIALIZER( xlock ); 1980b57cec5SDimitry Andric #define KMP_FUTEX_LOCK_INITIALIZER(lock) \ 1990b57cec5SDimitry Andric { \ 2000b57cec5SDimitry Andric { KMP_LOCK_FREE(futex), 0 } \ 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric extern int __kmp_acquire_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid); 2040b57cec5SDimitry Andric extern int __kmp_test_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid); 2050b57cec5SDimitry Andric extern int __kmp_release_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid); 2060b57cec5SDimitry Andric extern void __kmp_init_futex_lock(kmp_futex_lock_t *lck); 2070b57cec5SDimitry Andric extern void __kmp_destroy_futex_lock(kmp_futex_lock_t *lck); 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric extern int __kmp_acquire_nested_futex_lock(kmp_futex_lock_t *lck, 2100b57cec5SDimitry Andric kmp_int32 gtid); 2110b57cec5SDimitry Andric extern int __kmp_test_nested_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid); 2120b57cec5SDimitry Andric extern int __kmp_release_nested_futex_lock(kmp_futex_lock_t *lck, 2130b57cec5SDimitry Andric kmp_int32 gtid); 2140b57cec5SDimitry Andric extern void __kmp_init_nested_futex_lock(kmp_futex_lock_t *lck); 2150b57cec5SDimitry Andric extern void __kmp_destroy_nested_futex_lock(kmp_futex_lock_t *lck); 2160b57cec5SDimitry Andric 2170b57cec5SDimitry Andric #endif // KMP_USE_FUTEX 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 2200b57cec5SDimitry Andric // Ticket locks. 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric #ifdef __cplusplus 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric #ifdef _MSC_VER 2250b57cec5SDimitry Andric // MSVC won't allow use of std::atomic<> in a union since it has non-trivial 2260b57cec5SDimitry Andric // copy constructor. 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric struct kmp_base_ticket_lock { 2290b57cec5SDimitry Andric // `initialized' must be the first entry in the lock data structure! 2300b57cec5SDimitry Andric std::atomic_bool initialized; 2310b57cec5SDimitry Andric volatile union kmp_ticket_lock *self; // points to the lock union 2320b57cec5SDimitry Andric ident_t const *location; // Source code location of omp_init_lock(). 2330b57cec5SDimitry Andric std::atomic_uint 2340b57cec5SDimitry Andric next_ticket; // ticket number to give to next thread which acquires 2350b57cec5SDimitry Andric std::atomic_uint now_serving; // ticket number for thread which holds the lock 2360b57cec5SDimitry Andric std::atomic_int owner_id; // (gtid+1) of owning thread, 0 if unlocked 2370b57cec5SDimitry Andric std::atomic_int depth_locked; // depth locked, for nested locks only 2380b57cec5SDimitry Andric kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock 2390b57cec5SDimitry Andric }; 2400b57cec5SDimitry Andric #else 2410b57cec5SDimitry Andric struct kmp_base_ticket_lock { 2420b57cec5SDimitry Andric // `initialized' must be the first entry in the lock data structure! 2430b57cec5SDimitry Andric std::atomic<bool> initialized; 2440b57cec5SDimitry Andric volatile union kmp_ticket_lock *self; // points to the lock union 2450b57cec5SDimitry Andric ident_t const *location; // Source code location of omp_init_lock(). 2460b57cec5SDimitry Andric std::atomic<unsigned> 2470b57cec5SDimitry Andric next_ticket; // ticket number to give to next thread which acquires 2480b57cec5SDimitry Andric std::atomic<unsigned> 2490b57cec5SDimitry Andric now_serving; // ticket number for thread which holds the lock 2500b57cec5SDimitry Andric std::atomic<int> owner_id; // (gtid+1) of owning thread, 0 if unlocked 2510b57cec5SDimitry Andric std::atomic<int> depth_locked; // depth locked, for nested locks only 2520b57cec5SDimitry Andric kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock 2530b57cec5SDimitry Andric }; 2540b57cec5SDimitry Andric #endif 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric #else // __cplusplus 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric struct kmp_base_ticket_lock; 2590b57cec5SDimitry Andric 2600b57cec5SDimitry Andric #endif // !__cplusplus 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric typedef struct kmp_base_ticket_lock kmp_base_ticket_lock_t; 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric union KMP_ALIGN_CACHE kmp_ticket_lock { 2650b57cec5SDimitry Andric kmp_base_ticket_lock_t 2660b57cec5SDimitry Andric lk; // This field must be first to allow static initializing. 2670b57cec5SDimitry Andric kmp_lock_pool_t pool; 2680b57cec5SDimitry Andric double lk_align; // use worst case alignment 2690b57cec5SDimitry Andric char lk_pad[KMP_PAD(kmp_base_ticket_lock_t, CACHE_LINE)]; 2700b57cec5SDimitry Andric }; 2710b57cec5SDimitry Andric 2720b57cec5SDimitry Andric typedef union kmp_ticket_lock kmp_ticket_lock_t; 2730b57cec5SDimitry Andric 2740b57cec5SDimitry Andric // Static initializer for simple ticket lock variables. Usage: 2750b57cec5SDimitry Andric // kmp_ticket_lock_t xlock = KMP_TICKET_LOCK_INITIALIZER( xlock ); 2760b57cec5SDimitry Andric // Note the macro argument. It is important to make var properly initialized. 2770b57cec5SDimitry Andric #define KMP_TICKET_LOCK_INITIALIZER(lock) \ 2780b57cec5SDimitry Andric { \ 2790b57cec5SDimitry Andric { \ 2800b57cec5SDimitry Andric ATOMIC_VAR_INIT(true) \ 2810b57cec5SDimitry Andric , &(lock), NULL, ATOMIC_VAR_INIT(0U), ATOMIC_VAR_INIT(0U), \ 2820b57cec5SDimitry Andric ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(-1) \ 2830b57cec5SDimitry Andric } \ 2840b57cec5SDimitry Andric } 2850b57cec5SDimitry Andric 2860b57cec5SDimitry Andric extern int __kmp_acquire_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid); 2870b57cec5SDimitry Andric extern int __kmp_test_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid); 2880b57cec5SDimitry Andric extern int __kmp_test_ticket_lock_with_cheks(kmp_ticket_lock_t *lck, 2890b57cec5SDimitry Andric kmp_int32 gtid); 2900b57cec5SDimitry Andric extern int __kmp_release_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid); 2910b57cec5SDimitry Andric extern void __kmp_init_ticket_lock(kmp_ticket_lock_t *lck); 2920b57cec5SDimitry Andric extern void __kmp_destroy_ticket_lock(kmp_ticket_lock_t *lck); 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric extern int __kmp_acquire_nested_ticket_lock(kmp_ticket_lock_t *lck, 2950b57cec5SDimitry Andric kmp_int32 gtid); 2960b57cec5SDimitry Andric extern int __kmp_test_nested_ticket_lock(kmp_ticket_lock_t *lck, 2970b57cec5SDimitry Andric kmp_int32 gtid); 2980b57cec5SDimitry Andric extern int __kmp_release_nested_ticket_lock(kmp_ticket_lock_t *lck, 2990b57cec5SDimitry Andric kmp_int32 gtid); 3000b57cec5SDimitry Andric extern void __kmp_init_nested_ticket_lock(kmp_ticket_lock_t *lck); 3010b57cec5SDimitry Andric extern void __kmp_destroy_nested_ticket_lock(kmp_ticket_lock_t *lck); 3020b57cec5SDimitry Andric 3030b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 3040b57cec5SDimitry Andric // Queuing locks. 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric #if KMP_USE_ADAPTIVE_LOCKS 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric struct kmp_adaptive_lock_info; 3090b57cec5SDimitry Andric 3100b57cec5SDimitry Andric typedef struct kmp_adaptive_lock_info kmp_adaptive_lock_info_t; 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric #if KMP_DEBUG_ADAPTIVE_LOCKS 3130b57cec5SDimitry Andric 3140b57cec5SDimitry Andric struct kmp_adaptive_lock_statistics { 3150b57cec5SDimitry Andric /* So we can get stats from locks that haven't been destroyed. */ 3160b57cec5SDimitry Andric kmp_adaptive_lock_info_t *next; 3170b57cec5SDimitry Andric kmp_adaptive_lock_info_t *prev; 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric /* Other statistics */ 3200b57cec5SDimitry Andric kmp_uint32 successfulSpeculations; 3210b57cec5SDimitry Andric kmp_uint32 hardFailedSpeculations; 3220b57cec5SDimitry Andric kmp_uint32 softFailedSpeculations; 3230b57cec5SDimitry Andric kmp_uint32 nonSpeculativeAcquires; 3240b57cec5SDimitry Andric kmp_uint32 nonSpeculativeAcquireAttempts; 3250b57cec5SDimitry Andric kmp_uint32 lemmingYields; 3260b57cec5SDimitry Andric }; 3270b57cec5SDimitry Andric 3280b57cec5SDimitry Andric typedef struct kmp_adaptive_lock_statistics kmp_adaptive_lock_statistics_t; 3290b57cec5SDimitry Andric 3300b57cec5SDimitry Andric extern void __kmp_print_speculative_stats(); 3310b57cec5SDimitry Andric extern void __kmp_init_speculative_stats(); 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric #endif // KMP_DEBUG_ADAPTIVE_LOCKS 3340b57cec5SDimitry Andric 3350b57cec5SDimitry Andric struct kmp_adaptive_lock_info { 3360b57cec5SDimitry Andric /* Values used for adaptivity. 3370b57cec5SDimitry Andric Although these are accessed from multiple threads we don't access them 3380b57cec5SDimitry Andric atomically, because if we miss updates it probably doesn't matter much. (It 3390b57cec5SDimitry Andric just affects our decision about whether to try speculation on the lock). */ 3400b57cec5SDimitry Andric kmp_uint32 volatile badness; 3410b57cec5SDimitry Andric kmp_uint32 volatile acquire_attempts; 3420b57cec5SDimitry Andric /* Parameters of the lock. */ 3430b57cec5SDimitry Andric kmp_uint32 max_badness; 3440b57cec5SDimitry Andric kmp_uint32 max_soft_retries; 3450b57cec5SDimitry Andric 3460b57cec5SDimitry Andric #if KMP_DEBUG_ADAPTIVE_LOCKS 3470b57cec5SDimitry Andric kmp_adaptive_lock_statistics_t volatile stats; 3480b57cec5SDimitry Andric #endif 3490b57cec5SDimitry Andric }; 3500b57cec5SDimitry Andric 3510b57cec5SDimitry Andric #endif // KMP_USE_ADAPTIVE_LOCKS 3520b57cec5SDimitry Andric 3530b57cec5SDimitry Andric struct kmp_base_queuing_lock { 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric // `initialized' must be the first entry in the lock data structure! 3560b57cec5SDimitry Andric volatile union kmp_queuing_lock 3570b57cec5SDimitry Andric *initialized; // Points to the lock union if in initialized state. 3580b57cec5SDimitry Andric 3590b57cec5SDimitry Andric ident_t const *location; // Source code location of omp_init_lock(). 3600b57cec5SDimitry Andric 3610b57cec5SDimitry Andric KMP_ALIGN(8) // tail_id must be 8-byte aligned! 3620b57cec5SDimitry Andric 3630b57cec5SDimitry Andric volatile kmp_int32 3640b57cec5SDimitry Andric tail_id; // (gtid+1) of thread at tail of wait queue, 0 if empty 3650b57cec5SDimitry Andric // Must be no padding here since head/tail used in 8-byte CAS 3660b57cec5SDimitry Andric volatile kmp_int32 3670b57cec5SDimitry Andric head_id; // (gtid+1) of thread at head of wait queue, 0 if empty 3680b57cec5SDimitry Andric // Decl order assumes little endian 3690b57cec5SDimitry Andric // bakery-style lock 3700b57cec5SDimitry Andric volatile kmp_uint32 3710b57cec5SDimitry Andric next_ticket; // ticket number to give to next thread which acquires 3720b57cec5SDimitry Andric volatile kmp_uint32 3730b57cec5SDimitry Andric now_serving; // ticket number for thread which holds the lock 3740b57cec5SDimitry Andric volatile kmp_int32 owner_id; // (gtid+1) of owning thread, 0 if unlocked 3750b57cec5SDimitry Andric kmp_int32 depth_locked; // depth locked, for nested locks only 3760b57cec5SDimitry Andric 3770b57cec5SDimitry Andric kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock 3780b57cec5SDimitry Andric }; 3790b57cec5SDimitry Andric 3800b57cec5SDimitry Andric typedef struct kmp_base_queuing_lock kmp_base_queuing_lock_t; 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric KMP_BUILD_ASSERT(offsetof(kmp_base_queuing_lock_t, tail_id) % 8 == 0); 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric union KMP_ALIGN_CACHE kmp_queuing_lock { 3850b57cec5SDimitry Andric kmp_base_queuing_lock_t 3860b57cec5SDimitry Andric lk; // This field must be first to allow static initializing. 3870b57cec5SDimitry Andric kmp_lock_pool_t pool; 3880b57cec5SDimitry Andric double lk_align; // use worst case alignment 3890b57cec5SDimitry Andric char lk_pad[KMP_PAD(kmp_base_queuing_lock_t, CACHE_LINE)]; 3900b57cec5SDimitry Andric }; 3910b57cec5SDimitry Andric 3920b57cec5SDimitry Andric typedef union kmp_queuing_lock kmp_queuing_lock_t; 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric extern int __kmp_acquire_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid); 3950b57cec5SDimitry Andric extern int __kmp_test_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid); 3960b57cec5SDimitry Andric extern int __kmp_release_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid); 3970b57cec5SDimitry Andric extern void __kmp_init_queuing_lock(kmp_queuing_lock_t *lck); 3980b57cec5SDimitry Andric extern void __kmp_destroy_queuing_lock(kmp_queuing_lock_t *lck); 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric extern int __kmp_acquire_nested_queuing_lock(kmp_queuing_lock_t *lck, 4010b57cec5SDimitry Andric kmp_int32 gtid); 4020b57cec5SDimitry Andric extern int __kmp_test_nested_queuing_lock(kmp_queuing_lock_t *lck, 4030b57cec5SDimitry Andric kmp_int32 gtid); 4040b57cec5SDimitry Andric extern int __kmp_release_nested_queuing_lock(kmp_queuing_lock_t *lck, 4050b57cec5SDimitry Andric kmp_int32 gtid); 4060b57cec5SDimitry Andric extern void __kmp_init_nested_queuing_lock(kmp_queuing_lock_t *lck); 4070b57cec5SDimitry Andric extern void __kmp_destroy_nested_queuing_lock(kmp_queuing_lock_t *lck); 4080b57cec5SDimitry Andric 4090b57cec5SDimitry Andric #if KMP_USE_ADAPTIVE_LOCKS 4100b57cec5SDimitry Andric 4110b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 4120b57cec5SDimitry Andric // Adaptive locks. 4130b57cec5SDimitry Andric struct kmp_base_adaptive_lock { 4140b57cec5SDimitry Andric kmp_base_queuing_lock qlk; 4150b57cec5SDimitry Andric KMP_ALIGN(CACHE_LINE) 4160b57cec5SDimitry Andric kmp_adaptive_lock_info_t 4170b57cec5SDimitry Andric adaptive; // Information for the speculative adaptive lock 4180b57cec5SDimitry Andric }; 4190b57cec5SDimitry Andric 4200b57cec5SDimitry Andric typedef struct kmp_base_adaptive_lock kmp_base_adaptive_lock_t; 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric union KMP_ALIGN_CACHE kmp_adaptive_lock { 4230b57cec5SDimitry Andric kmp_base_adaptive_lock_t lk; 4240b57cec5SDimitry Andric kmp_lock_pool_t pool; 4250b57cec5SDimitry Andric double lk_align; 4260b57cec5SDimitry Andric char lk_pad[KMP_PAD(kmp_base_adaptive_lock_t, CACHE_LINE)]; 4270b57cec5SDimitry Andric }; 4280b57cec5SDimitry Andric typedef union kmp_adaptive_lock kmp_adaptive_lock_t; 4290b57cec5SDimitry Andric 4300b57cec5SDimitry Andric #define GET_QLK_PTR(l) ((kmp_queuing_lock_t *)&(l)->lk.qlk) 4310b57cec5SDimitry Andric 4320b57cec5SDimitry Andric #endif // KMP_USE_ADAPTIVE_LOCKS 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric // ---------------------------------------------------------------------------- 4350b57cec5SDimitry Andric // DRDPA ticket locks. 4360b57cec5SDimitry Andric struct kmp_base_drdpa_lock { 4370b57cec5SDimitry Andric // All of the fields on the first cache line are only written when 4380b57cec5SDimitry Andric // initializing or reconfiguring the lock. These are relatively rare 4390b57cec5SDimitry Andric // operations, so data from the first cache line will usually stay resident in 4400b57cec5SDimitry Andric // the cache of each thread trying to acquire the lock. 4410b57cec5SDimitry Andric // 4420b57cec5SDimitry Andric // initialized must be the first entry in the lock data structure! 4430b57cec5SDimitry Andric KMP_ALIGN_CACHE 4440b57cec5SDimitry Andric 4450b57cec5SDimitry Andric volatile union kmp_drdpa_lock 4460b57cec5SDimitry Andric *initialized; // points to the lock union if in initialized state 4470b57cec5SDimitry Andric ident_t const *location; // Source code location of omp_init_lock(). 4480b57cec5SDimitry Andric std::atomic<std::atomic<kmp_uint64> *> polls; 4490b57cec5SDimitry Andric std::atomic<kmp_uint64> mask; // is 2**num_polls-1 for mod op 4500b57cec5SDimitry Andric kmp_uint64 cleanup_ticket; // thread with cleanup ticket 4510b57cec5SDimitry Andric std::atomic<kmp_uint64> *old_polls; // will deallocate old_polls 4520b57cec5SDimitry Andric kmp_uint32 num_polls; // must be power of 2 4530b57cec5SDimitry Andric 4540b57cec5SDimitry Andric // next_ticket it needs to exist in a separate cache line, as it is 4550b57cec5SDimitry Andric // invalidated every time a thread takes a new ticket. 4560b57cec5SDimitry Andric KMP_ALIGN_CACHE 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric std::atomic<kmp_uint64> next_ticket; 4590b57cec5SDimitry Andric 4600b57cec5SDimitry Andric // now_serving is used to store our ticket value while we hold the lock. It 4610b57cec5SDimitry Andric // has a slightly different meaning in the DRDPA ticket locks (where it is 4620b57cec5SDimitry Andric // written by the acquiring thread) than it does in the simple ticket locks 4630b57cec5SDimitry Andric // (where it is written by the releasing thread). 4640b57cec5SDimitry Andric // 4650b57cec5SDimitry Andric // Since now_serving is only read an written in the critical section, 4660b57cec5SDimitry Andric // it is non-volatile, but it needs to exist on a separate cache line, 4670b57cec5SDimitry Andric // as it is invalidated at every lock acquire. 4680b57cec5SDimitry Andric // 4690b57cec5SDimitry Andric // Likewise, the vars used for nested locks (owner_id and depth_locked) are 4700b57cec5SDimitry Andric // only written by the thread owning the lock, so they are put in this cache 4710b57cec5SDimitry Andric // line. owner_id is read by other threads, so it must be declared volatile. 4720b57cec5SDimitry Andric KMP_ALIGN_CACHE 4730b57cec5SDimitry Andric kmp_uint64 now_serving; // doesn't have to be volatile 4740b57cec5SDimitry Andric volatile kmp_uint32 owner_id; // (gtid+1) of owning thread, 0 if unlocked 4750b57cec5SDimitry Andric kmp_int32 depth_locked; // depth locked 4760b57cec5SDimitry Andric kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock 4770b57cec5SDimitry Andric }; 4780b57cec5SDimitry Andric 4790b57cec5SDimitry Andric typedef struct kmp_base_drdpa_lock kmp_base_drdpa_lock_t; 4800b57cec5SDimitry Andric 4810b57cec5SDimitry Andric union KMP_ALIGN_CACHE kmp_drdpa_lock { 4820b57cec5SDimitry Andric kmp_base_drdpa_lock_t 4830b57cec5SDimitry Andric lk; // This field must be first to allow static initializing. */ 4840b57cec5SDimitry Andric kmp_lock_pool_t pool; 4850b57cec5SDimitry Andric double lk_align; // use worst case alignment 4860b57cec5SDimitry Andric char lk_pad[KMP_PAD(kmp_base_drdpa_lock_t, CACHE_LINE)]; 4870b57cec5SDimitry Andric }; 4880b57cec5SDimitry Andric 4890b57cec5SDimitry Andric typedef union kmp_drdpa_lock kmp_drdpa_lock_t; 4900b57cec5SDimitry Andric 4910b57cec5SDimitry Andric extern int __kmp_acquire_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid); 4920b57cec5SDimitry Andric extern int __kmp_test_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid); 4930b57cec5SDimitry Andric extern int __kmp_release_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid); 4940b57cec5SDimitry Andric extern void __kmp_init_drdpa_lock(kmp_drdpa_lock_t *lck); 4950b57cec5SDimitry Andric extern void __kmp_destroy_drdpa_lock(kmp_drdpa_lock_t *lck); 4960b57cec5SDimitry Andric 4970b57cec5SDimitry Andric extern int __kmp_acquire_nested_drdpa_lock(kmp_drdpa_lock_t *lck, 4980b57cec5SDimitry Andric kmp_int32 gtid); 4990b57cec5SDimitry Andric extern int __kmp_test_nested_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid); 5000b57cec5SDimitry Andric extern int __kmp_release_nested_drdpa_lock(kmp_drdpa_lock_t *lck, 5010b57cec5SDimitry Andric kmp_int32 gtid); 5020b57cec5SDimitry Andric extern void __kmp_init_nested_drdpa_lock(kmp_drdpa_lock_t *lck); 5030b57cec5SDimitry Andric extern void __kmp_destroy_nested_drdpa_lock(kmp_drdpa_lock_t *lck); 5040b57cec5SDimitry Andric 5050b57cec5SDimitry Andric // ============================================================================ 5060b57cec5SDimitry Andric // Lock purposes. 5070b57cec5SDimitry Andric // ============================================================================ 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric // Bootstrap locks. 5100b57cec5SDimitry Andric // 5110b57cec5SDimitry Andric // Bootstrap locks -- very few locks used at library initialization time. 5120b57cec5SDimitry Andric // Bootstrap locks are currently implemented as ticket locks. 5130b57cec5SDimitry Andric // They could also be implemented as test and set lock, but cannot be 5140b57cec5SDimitry Andric // implemented with other lock kinds as they require gtids which are not 5150b57cec5SDimitry Andric // available at initialization time. 5160b57cec5SDimitry Andric 5170b57cec5SDimitry Andric typedef kmp_ticket_lock_t kmp_bootstrap_lock_t; 5180b57cec5SDimitry Andric 5190b57cec5SDimitry Andric #define KMP_BOOTSTRAP_LOCK_INITIALIZER(lock) KMP_TICKET_LOCK_INITIALIZER((lock)) 5200b57cec5SDimitry Andric #define KMP_BOOTSTRAP_LOCK_INIT(lock) \ 5210b57cec5SDimitry Andric kmp_bootstrap_lock_t lock = KMP_TICKET_LOCK_INITIALIZER(lock) 5220b57cec5SDimitry Andric 5230b57cec5SDimitry Andric static inline int __kmp_acquire_bootstrap_lock(kmp_bootstrap_lock_t *lck) { 5240b57cec5SDimitry Andric return __kmp_acquire_ticket_lock(lck, KMP_GTID_DNE); 5250b57cec5SDimitry Andric } 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric static inline int __kmp_test_bootstrap_lock(kmp_bootstrap_lock_t *lck) { 5280b57cec5SDimitry Andric return __kmp_test_ticket_lock(lck, KMP_GTID_DNE); 5290b57cec5SDimitry Andric } 5300b57cec5SDimitry Andric 5310b57cec5SDimitry Andric static inline void __kmp_release_bootstrap_lock(kmp_bootstrap_lock_t *lck) { 5320b57cec5SDimitry Andric __kmp_release_ticket_lock(lck, KMP_GTID_DNE); 5330b57cec5SDimitry Andric } 5340b57cec5SDimitry Andric 5350b57cec5SDimitry Andric static inline void __kmp_init_bootstrap_lock(kmp_bootstrap_lock_t *lck) { 5360b57cec5SDimitry Andric __kmp_init_ticket_lock(lck); 5370b57cec5SDimitry Andric } 5380b57cec5SDimitry Andric 5390b57cec5SDimitry Andric static inline void __kmp_destroy_bootstrap_lock(kmp_bootstrap_lock_t *lck) { 5400b57cec5SDimitry Andric __kmp_destroy_ticket_lock(lck); 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric 5430b57cec5SDimitry Andric // Internal RTL locks. 5440b57cec5SDimitry Andric // 5450b57cec5SDimitry Andric // Internal RTL locks are also implemented as ticket locks, for now. 5460b57cec5SDimitry Andric // 5470b57cec5SDimitry Andric // FIXME - We should go through and figure out which lock kind works best for 5480b57cec5SDimitry Andric // each internal lock, and use the type declaration and function calls for 5490b57cec5SDimitry Andric // that explicit lock kind (and get rid of this section). 5500b57cec5SDimitry Andric 5510b57cec5SDimitry Andric typedef kmp_ticket_lock_t kmp_lock_t; 5520b57cec5SDimitry Andric 5530b57cec5SDimitry Andric #define KMP_LOCK_INIT(lock) kmp_lock_t lock = KMP_TICKET_LOCK_INITIALIZER(lock) 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric static inline int __kmp_acquire_lock(kmp_lock_t *lck, kmp_int32 gtid) { 5560b57cec5SDimitry Andric return __kmp_acquire_ticket_lock(lck, gtid); 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric 5590b57cec5SDimitry Andric static inline int __kmp_test_lock(kmp_lock_t *lck, kmp_int32 gtid) { 5600b57cec5SDimitry Andric return __kmp_test_ticket_lock(lck, gtid); 5610b57cec5SDimitry Andric } 5620b57cec5SDimitry Andric 5630b57cec5SDimitry Andric static inline void __kmp_release_lock(kmp_lock_t *lck, kmp_int32 gtid) { 5640b57cec5SDimitry Andric __kmp_release_ticket_lock(lck, gtid); 5650b57cec5SDimitry Andric } 5660b57cec5SDimitry Andric 5670b57cec5SDimitry Andric static inline void __kmp_init_lock(kmp_lock_t *lck) { 5680b57cec5SDimitry Andric __kmp_init_ticket_lock(lck); 5690b57cec5SDimitry Andric } 5700b57cec5SDimitry Andric 5710b57cec5SDimitry Andric static inline void __kmp_destroy_lock(kmp_lock_t *lck) { 5720b57cec5SDimitry Andric __kmp_destroy_ticket_lock(lck); 5730b57cec5SDimitry Andric } 5740b57cec5SDimitry Andric 5750b57cec5SDimitry Andric // User locks. 5760b57cec5SDimitry Andric // 5770b57cec5SDimitry Andric // Do not allocate objects of type union kmp_user_lock!!! This will waste space 5780b57cec5SDimitry Andric // unless __kmp_user_lock_kind == lk_drdpa. Instead, check the value of 5790b57cec5SDimitry Andric // __kmp_user_lock_kind and allocate objects of the type of the appropriate 5800b57cec5SDimitry Andric // union member, and cast their addresses to kmp_user_lock_p. 5810b57cec5SDimitry Andric 5820b57cec5SDimitry Andric enum kmp_lock_kind { 5830b57cec5SDimitry Andric lk_default = 0, 5840b57cec5SDimitry Andric lk_tas, 5850b57cec5SDimitry Andric #if KMP_USE_FUTEX 5860b57cec5SDimitry Andric lk_futex, 5870b57cec5SDimitry Andric #endif 5880b57cec5SDimitry Andric #if KMP_USE_DYNAMIC_LOCK && KMP_USE_TSX 5890b57cec5SDimitry Andric lk_hle, 5900b57cec5SDimitry Andric lk_rtm, 5910b57cec5SDimitry Andric #endif 5920b57cec5SDimitry Andric lk_ticket, 5930b57cec5SDimitry Andric lk_queuing, 5940b57cec5SDimitry Andric lk_drdpa, 5950b57cec5SDimitry Andric #if KMP_USE_ADAPTIVE_LOCKS 5960b57cec5SDimitry Andric lk_adaptive 5970b57cec5SDimitry Andric #endif // KMP_USE_ADAPTIVE_LOCKS 5980b57cec5SDimitry Andric }; 5990b57cec5SDimitry Andric 6000b57cec5SDimitry Andric typedef enum kmp_lock_kind kmp_lock_kind_t; 6010b57cec5SDimitry Andric 6020b57cec5SDimitry Andric extern kmp_lock_kind_t __kmp_user_lock_kind; 6030b57cec5SDimitry Andric 6040b57cec5SDimitry Andric union kmp_user_lock { 6050b57cec5SDimitry Andric kmp_tas_lock_t tas; 6060b57cec5SDimitry Andric #if KMP_USE_FUTEX 6070b57cec5SDimitry Andric kmp_futex_lock_t futex; 6080b57cec5SDimitry Andric #endif 6090b57cec5SDimitry Andric kmp_ticket_lock_t ticket; 6100b57cec5SDimitry Andric kmp_queuing_lock_t queuing; 6110b57cec5SDimitry Andric kmp_drdpa_lock_t drdpa; 6120b57cec5SDimitry Andric #if KMP_USE_ADAPTIVE_LOCKS 6130b57cec5SDimitry Andric kmp_adaptive_lock_t adaptive; 6140b57cec5SDimitry Andric #endif // KMP_USE_ADAPTIVE_LOCKS 6150b57cec5SDimitry Andric kmp_lock_pool_t pool; 6160b57cec5SDimitry Andric }; 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric typedef union kmp_user_lock *kmp_user_lock_p; 6190b57cec5SDimitry Andric 6200b57cec5SDimitry Andric #if !KMP_USE_DYNAMIC_LOCK 6210b57cec5SDimitry Andric 6220b57cec5SDimitry Andric extern size_t __kmp_base_user_lock_size; 6230b57cec5SDimitry Andric extern size_t __kmp_user_lock_size; 6240b57cec5SDimitry Andric 6250b57cec5SDimitry Andric extern kmp_int32 (*__kmp_get_user_lock_owner_)(kmp_user_lock_p lck); 6260b57cec5SDimitry Andric 6270b57cec5SDimitry Andric static inline kmp_int32 __kmp_get_user_lock_owner(kmp_user_lock_p lck) { 6280b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_get_user_lock_owner_ != NULL); 6290b57cec5SDimitry Andric return (*__kmp_get_user_lock_owner_)(lck); 6300b57cec5SDimitry Andric } 6310b57cec5SDimitry Andric 6320b57cec5SDimitry Andric extern int (*__kmp_acquire_user_lock_with_checks_)(kmp_user_lock_p lck, 6330b57cec5SDimitry Andric kmp_int32 gtid); 6340b57cec5SDimitry Andric 6350b57cec5SDimitry Andric #if KMP_OS_LINUX && \ 6360b57cec5SDimitry Andric (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64) 6370b57cec5SDimitry Andric 6380b57cec5SDimitry Andric #define __kmp_acquire_user_lock_with_checks(lck, gtid) \ 6390b57cec5SDimitry Andric if (__kmp_user_lock_kind == lk_tas) { \ 6400b57cec5SDimitry Andric if (__kmp_env_consistency_check) { \ 6410b57cec5SDimitry Andric char const *const func = "omp_set_lock"; \ 6420b57cec5SDimitry Andric if ((sizeof(kmp_tas_lock_t) <= OMP_LOCK_T_SIZE) && \ 6430b57cec5SDimitry Andric lck->tas.lk.depth_locked != -1) { \ 6440b57cec5SDimitry Andric KMP_FATAL(LockNestableUsedAsSimple, func); \ 6450b57cec5SDimitry Andric } \ 6460b57cec5SDimitry Andric if ((gtid >= 0) && (lck->tas.lk.poll - 1 == gtid)) { \ 6470b57cec5SDimitry Andric KMP_FATAL(LockIsAlreadyOwned, func); \ 6480b57cec5SDimitry Andric } \ 6490b57cec5SDimitry Andric } \ 6500b57cec5SDimitry Andric if (lck->tas.lk.poll != 0 || \ 6510b57cec5SDimitry Andric !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)) { \ 6520b57cec5SDimitry Andric kmp_uint32 spins; \ 6530b57cec5SDimitry Andric KMP_FSYNC_PREPARE(lck); \ 6540b57cec5SDimitry Andric KMP_INIT_YIELD(spins); \ 6550b57cec5SDimitry Andric do { \ 6560b57cec5SDimitry Andric KMP_YIELD_OVERSUB_ELSE_SPIN(spins); \ 6570b57cec5SDimitry Andric } while ( \ 6580b57cec5SDimitry Andric lck->tas.lk.poll != 0 || \ 6590b57cec5SDimitry Andric !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); \ 6600b57cec5SDimitry Andric } \ 6610b57cec5SDimitry Andric KMP_FSYNC_ACQUIRED(lck); \ 6620b57cec5SDimitry Andric } else { \ 6630b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_acquire_user_lock_with_checks_ != NULL); \ 6640b57cec5SDimitry Andric (*__kmp_acquire_user_lock_with_checks_)(lck, gtid); \ 6650b57cec5SDimitry Andric } 6660b57cec5SDimitry Andric 6670b57cec5SDimitry Andric #else 6680b57cec5SDimitry Andric static inline int __kmp_acquire_user_lock_with_checks(kmp_user_lock_p lck, 6690b57cec5SDimitry Andric kmp_int32 gtid) { 6700b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_acquire_user_lock_with_checks_ != NULL); 6710b57cec5SDimitry Andric return (*__kmp_acquire_user_lock_with_checks_)(lck, gtid); 6720b57cec5SDimitry Andric } 6730b57cec5SDimitry Andric #endif 6740b57cec5SDimitry Andric 6750b57cec5SDimitry Andric extern int (*__kmp_test_user_lock_with_checks_)(kmp_user_lock_p lck, 6760b57cec5SDimitry Andric kmp_int32 gtid); 6770b57cec5SDimitry Andric 6780b57cec5SDimitry Andric #if KMP_OS_LINUX && \ 6790b57cec5SDimitry Andric (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64) 6800b57cec5SDimitry Andric 6810b57cec5SDimitry Andric #include "kmp_i18n.h" /* AC: KMP_FATAL definition */ 6820b57cec5SDimitry Andric extern int __kmp_env_consistency_check; /* AC: copy from kmp.h here */ 6830b57cec5SDimitry Andric static inline int __kmp_test_user_lock_with_checks(kmp_user_lock_p lck, 6840b57cec5SDimitry Andric kmp_int32 gtid) { 6850b57cec5SDimitry Andric if (__kmp_user_lock_kind == lk_tas) { 6860b57cec5SDimitry Andric if (__kmp_env_consistency_check) { 6870b57cec5SDimitry Andric char const *const func = "omp_test_lock"; 6880b57cec5SDimitry Andric if ((sizeof(kmp_tas_lock_t) <= OMP_LOCK_T_SIZE) && 6890b57cec5SDimitry Andric lck->tas.lk.depth_locked != -1) { 6900b57cec5SDimitry Andric KMP_FATAL(LockNestableUsedAsSimple, func); 6910b57cec5SDimitry Andric } 6920b57cec5SDimitry Andric } 6930b57cec5SDimitry Andric return ((lck->tas.lk.poll == 0) && 6940b57cec5SDimitry Andric __kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); 6950b57cec5SDimitry Andric } else { 6960b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_test_user_lock_with_checks_ != NULL); 6970b57cec5SDimitry Andric return (*__kmp_test_user_lock_with_checks_)(lck, gtid); 6980b57cec5SDimitry Andric } 6990b57cec5SDimitry Andric } 7000b57cec5SDimitry Andric #else 7010b57cec5SDimitry Andric static inline int __kmp_test_user_lock_with_checks(kmp_user_lock_p lck, 7020b57cec5SDimitry Andric kmp_int32 gtid) { 7030b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_test_user_lock_with_checks_ != NULL); 7040b57cec5SDimitry Andric return (*__kmp_test_user_lock_with_checks_)(lck, gtid); 7050b57cec5SDimitry Andric } 7060b57cec5SDimitry Andric #endif 7070b57cec5SDimitry Andric 7080b57cec5SDimitry Andric extern int (*__kmp_release_user_lock_with_checks_)(kmp_user_lock_p lck, 7090b57cec5SDimitry Andric kmp_int32 gtid); 7100b57cec5SDimitry Andric 7110b57cec5SDimitry Andric static inline void __kmp_release_user_lock_with_checks(kmp_user_lock_p lck, 7120b57cec5SDimitry Andric kmp_int32 gtid) { 7130b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_release_user_lock_with_checks_ != NULL); 7140b57cec5SDimitry Andric (*__kmp_release_user_lock_with_checks_)(lck, gtid); 7150b57cec5SDimitry Andric } 7160b57cec5SDimitry Andric 7170b57cec5SDimitry Andric extern void (*__kmp_init_user_lock_with_checks_)(kmp_user_lock_p lck); 7180b57cec5SDimitry Andric 7190b57cec5SDimitry Andric static inline void __kmp_init_user_lock_with_checks(kmp_user_lock_p lck) { 7200b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_init_user_lock_with_checks_ != NULL); 7210b57cec5SDimitry Andric (*__kmp_init_user_lock_with_checks_)(lck); 7220b57cec5SDimitry Andric } 7230b57cec5SDimitry Andric 7240b57cec5SDimitry Andric // We need a non-checking version of destroy lock for when the RTL is 7250b57cec5SDimitry Andric // doing the cleanup as it can't always tell if the lock is nested or not. 7260b57cec5SDimitry Andric extern void (*__kmp_destroy_user_lock_)(kmp_user_lock_p lck); 7270b57cec5SDimitry Andric 7280b57cec5SDimitry Andric static inline void __kmp_destroy_user_lock(kmp_user_lock_p lck) { 7290b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_destroy_user_lock_ != NULL); 7300b57cec5SDimitry Andric (*__kmp_destroy_user_lock_)(lck); 7310b57cec5SDimitry Andric } 7320b57cec5SDimitry Andric 7330b57cec5SDimitry Andric extern void (*__kmp_destroy_user_lock_with_checks_)(kmp_user_lock_p lck); 7340b57cec5SDimitry Andric 7350b57cec5SDimitry Andric static inline void __kmp_destroy_user_lock_with_checks(kmp_user_lock_p lck) { 7360b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_destroy_user_lock_with_checks_ != NULL); 7370b57cec5SDimitry Andric (*__kmp_destroy_user_lock_with_checks_)(lck); 7380b57cec5SDimitry Andric } 7390b57cec5SDimitry Andric 7400b57cec5SDimitry Andric extern int (*__kmp_acquire_nested_user_lock_with_checks_)(kmp_user_lock_p lck, 7410b57cec5SDimitry Andric kmp_int32 gtid); 7420b57cec5SDimitry Andric 7430b57cec5SDimitry Andric #if KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64) 7440b57cec5SDimitry Andric 7450b57cec5SDimitry Andric #define __kmp_acquire_nested_user_lock_with_checks(lck, gtid, depth) \ 7460b57cec5SDimitry Andric if (__kmp_user_lock_kind == lk_tas) { \ 7470b57cec5SDimitry Andric if (__kmp_env_consistency_check) { \ 7480b57cec5SDimitry Andric char const *const func = "omp_set_nest_lock"; \ 7490b57cec5SDimitry Andric if ((sizeof(kmp_tas_lock_t) <= OMP_NEST_LOCK_T_SIZE) && \ 7500b57cec5SDimitry Andric lck->tas.lk.depth_locked == -1) { \ 7510b57cec5SDimitry Andric KMP_FATAL(LockSimpleUsedAsNestable, func); \ 7520b57cec5SDimitry Andric } \ 7530b57cec5SDimitry Andric } \ 7540b57cec5SDimitry Andric if (lck->tas.lk.poll - 1 == gtid) { \ 7550b57cec5SDimitry Andric lck->tas.lk.depth_locked += 1; \ 7560b57cec5SDimitry Andric *depth = KMP_LOCK_ACQUIRED_NEXT; \ 7570b57cec5SDimitry Andric } else { \ 7580b57cec5SDimitry Andric if ((lck->tas.lk.poll != 0) || \ 7590b57cec5SDimitry Andric !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)) { \ 7600b57cec5SDimitry Andric kmp_uint32 spins; \ 7610b57cec5SDimitry Andric KMP_FSYNC_PREPARE(lck); \ 7620b57cec5SDimitry Andric KMP_INIT_YIELD(spins); \ 7630b57cec5SDimitry Andric do { \ 7640b57cec5SDimitry Andric KMP_YIELD_OVERSUB_ELSE_SPIN(spins); \ 7650b57cec5SDimitry Andric } while ( \ 7660b57cec5SDimitry Andric (lck->tas.lk.poll != 0) || \ 7670b57cec5SDimitry Andric !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); \ 7680b57cec5SDimitry Andric } \ 7690b57cec5SDimitry Andric lck->tas.lk.depth_locked = 1; \ 7700b57cec5SDimitry Andric *depth = KMP_LOCK_ACQUIRED_FIRST; \ 7710b57cec5SDimitry Andric } \ 7720b57cec5SDimitry Andric KMP_FSYNC_ACQUIRED(lck); \ 7730b57cec5SDimitry Andric } else { \ 7740b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_acquire_nested_user_lock_with_checks_ != NULL); \ 7750b57cec5SDimitry Andric *depth = (*__kmp_acquire_nested_user_lock_with_checks_)(lck, gtid); \ 7760b57cec5SDimitry Andric } 7770b57cec5SDimitry Andric 7780b57cec5SDimitry Andric #else 7790b57cec5SDimitry Andric static inline void 7800b57cec5SDimitry Andric __kmp_acquire_nested_user_lock_with_checks(kmp_user_lock_p lck, kmp_int32 gtid, 7810b57cec5SDimitry Andric int *depth) { 7820b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_acquire_nested_user_lock_with_checks_ != NULL); 7830b57cec5SDimitry Andric *depth = (*__kmp_acquire_nested_user_lock_with_checks_)(lck, gtid); 7840b57cec5SDimitry Andric } 7850b57cec5SDimitry Andric #endif 7860b57cec5SDimitry Andric 7870b57cec5SDimitry Andric extern int (*__kmp_test_nested_user_lock_with_checks_)(kmp_user_lock_p lck, 7880b57cec5SDimitry Andric kmp_int32 gtid); 7890b57cec5SDimitry Andric 7900b57cec5SDimitry Andric #if KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64) 7910b57cec5SDimitry Andric static inline int __kmp_test_nested_user_lock_with_checks(kmp_user_lock_p lck, 7920b57cec5SDimitry Andric kmp_int32 gtid) { 7930b57cec5SDimitry Andric if (__kmp_user_lock_kind == lk_tas) { 7940b57cec5SDimitry Andric int retval; 7950b57cec5SDimitry Andric if (__kmp_env_consistency_check) { 7960b57cec5SDimitry Andric char const *const func = "omp_test_nest_lock"; 7970b57cec5SDimitry Andric if ((sizeof(kmp_tas_lock_t) <= OMP_NEST_LOCK_T_SIZE) && 7980b57cec5SDimitry Andric lck->tas.lk.depth_locked == -1) { 7990b57cec5SDimitry Andric KMP_FATAL(LockSimpleUsedAsNestable, func); 8000b57cec5SDimitry Andric } 8010b57cec5SDimitry Andric } 8020b57cec5SDimitry Andric KMP_DEBUG_ASSERT(gtid >= 0); 8030b57cec5SDimitry Andric if (lck->tas.lk.poll - 1 == 8040b57cec5SDimitry Andric gtid) { /* __kmp_get_tas_lock_owner( lck ) == gtid */ 8050b57cec5SDimitry Andric return ++lck->tas.lk.depth_locked; /* same owner, depth increased */ 8060b57cec5SDimitry Andric } 8070b57cec5SDimitry Andric retval = ((lck->tas.lk.poll == 0) && 8080b57cec5SDimitry Andric __kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); 8090b57cec5SDimitry Andric if (retval) { 8100b57cec5SDimitry Andric KMP_MB(); 8110b57cec5SDimitry Andric lck->tas.lk.depth_locked = 1; 8120b57cec5SDimitry Andric } 8130b57cec5SDimitry Andric return retval; 8140b57cec5SDimitry Andric } else { 8150b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_test_nested_user_lock_with_checks_ != NULL); 8160b57cec5SDimitry Andric return (*__kmp_test_nested_user_lock_with_checks_)(lck, gtid); 8170b57cec5SDimitry Andric } 8180b57cec5SDimitry Andric } 8190b57cec5SDimitry Andric #else 8200b57cec5SDimitry Andric static inline int __kmp_test_nested_user_lock_with_checks(kmp_user_lock_p lck, 8210b57cec5SDimitry Andric kmp_int32 gtid) { 8220b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_test_nested_user_lock_with_checks_ != NULL); 8230b57cec5SDimitry Andric return (*__kmp_test_nested_user_lock_with_checks_)(lck, gtid); 8240b57cec5SDimitry Andric } 8250b57cec5SDimitry Andric #endif 8260b57cec5SDimitry Andric 8270b57cec5SDimitry Andric extern int (*__kmp_release_nested_user_lock_with_checks_)(kmp_user_lock_p lck, 8280b57cec5SDimitry Andric kmp_int32 gtid); 8290b57cec5SDimitry Andric 8300b57cec5SDimitry Andric static inline int 8310b57cec5SDimitry Andric __kmp_release_nested_user_lock_with_checks(kmp_user_lock_p lck, 8320b57cec5SDimitry Andric kmp_int32 gtid) { 8330b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_release_nested_user_lock_with_checks_ != NULL); 8340b57cec5SDimitry Andric return (*__kmp_release_nested_user_lock_with_checks_)(lck, gtid); 8350b57cec5SDimitry Andric } 8360b57cec5SDimitry Andric 8370b57cec5SDimitry Andric extern void (*__kmp_init_nested_user_lock_with_checks_)(kmp_user_lock_p lck); 8380b57cec5SDimitry Andric 8390b57cec5SDimitry Andric static inline void 8400b57cec5SDimitry Andric __kmp_init_nested_user_lock_with_checks(kmp_user_lock_p lck) { 8410b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_init_nested_user_lock_with_checks_ != NULL); 8420b57cec5SDimitry Andric (*__kmp_init_nested_user_lock_with_checks_)(lck); 8430b57cec5SDimitry Andric } 8440b57cec5SDimitry Andric 8450b57cec5SDimitry Andric extern void (*__kmp_destroy_nested_user_lock_with_checks_)(kmp_user_lock_p lck); 8460b57cec5SDimitry Andric 8470b57cec5SDimitry Andric static inline void 8480b57cec5SDimitry Andric __kmp_destroy_nested_user_lock_with_checks(kmp_user_lock_p lck) { 8490b57cec5SDimitry Andric KMP_DEBUG_ASSERT(__kmp_destroy_nested_user_lock_with_checks_ != NULL); 8500b57cec5SDimitry Andric (*__kmp_destroy_nested_user_lock_with_checks_)(lck); 8510b57cec5SDimitry Andric } 8520b57cec5SDimitry Andric 8530b57cec5SDimitry Andric // user lock functions which do not necessarily exist for all lock kinds. 8540b57cec5SDimitry Andric // 8550b57cec5SDimitry Andric // The "set" functions usually have wrapper routines that check for a NULL set 8560b57cec5SDimitry Andric // function pointer and call it if non-NULL. 8570b57cec5SDimitry Andric // 8580b57cec5SDimitry Andric // In some cases, it makes sense to have a "get" wrapper function check for a 8590b57cec5SDimitry Andric // NULL get function pointer and return NULL / invalid value / error code if 8600b57cec5SDimitry Andric // the function pointer is NULL. 8610b57cec5SDimitry Andric // 8620b57cec5SDimitry Andric // In other cases, the calling code really should differentiate between an 8630b57cec5SDimitry Andric // unimplemented function and one that is implemented but returning NULL / 8640b57cec5SDimitry Andric // invalied value. If this is the case, no get function wrapper exists. 8650b57cec5SDimitry Andric 8660b57cec5SDimitry Andric extern int (*__kmp_is_user_lock_initialized_)(kmp_user_lock_p lck); 8670b57cec5SDimitry Andric 8680b57cec5SDimitry Andric // no set function; fields set durining local allocation 8690b57cec5SDimitry Andric 8700b57cec5SDimitry Andric extern const ident_t *(*__kmp_get_user_lock_location_)(kmp_user_lock_p lck); 8710b57cec5SDimitry Andric 8720b57cec5SDimitry Andric static inline const ident_t *__kmp_get_user_lock_location(kmp_user_lock_p lck) { 8730b57cec5SDimitry Andric if (__kmp_get_user_lock_location_ != NULL) { 8740b57cec5SDimitry Andric return (*__kmp_get_user_lock_location_)(lck); 8750b57cec5SDimitry Andric } else { 8760b57cec5SDimitry Andric return NULL; 8770b57cec5SDimitry Andric } 8780b57cec5SDimitry Andric } 8790b57cec5SDimitry Andric 8800b57cec5SDimitry Andric extern void (*__kmp_set_user_lock_location_)(kmp_user_lock_p lck, 8810b57cec5SDimitry Andric const ident_t *loc); 8820b57cec5SDimitry Andric 8830b57cec5SDimitry Andric static inline void __kmp_set_user_lock_location(kmp_user_lock_p lck, 8840b57cec5SDimitry Andric const ident_t *loc) { 8850b57cec5SDimitry Andric if (__kmp_set_user_lock_location_ != NULL) { 8860b57cec5SDimitry Andric (*__kmp_set_user_lock_location_)(lck, loc); 8870b57cec5SDimitry Andric } 8880b57cec5SDimitry Andric } 8890b57cec5SDimitry Andric 8900b57cec5SDimitry Andric extern kmp_lock_flags_t (*__kmp_get_user_lock_flags_)(kmp_user_lock_p lck); 8910b57cec5SDimitry Andric 8920b57cec5SDimitry Andric extern void (*__kmp_set_user_lock_flags_)(kmp_user_lock_p lck, 8930b57cec5SDimitry Andric kmp_lock_flags_t flags); 8940b57cec5SDimitry Andric 8950b57cec5SDimitry Andric static inline void __kmp_set_user_lock_flags(kmp_user_lock_p lck, 8960b57cec5SDimitry Andric kmp_lock_flags_t flags) { 8970b57cec5SDimitry Andric if (__kmp_set_user_lock_flags_ != NULL) { 8980b57cec5SDimitry Andric (*__kmp_set_user_lock_flags_)(lck, flags); 8990b57cec5SDimitry Andric } 9000b57cec5SDimitry Andric } 9010b57cec5SDimitry Andric 9020b57cec5SDimitry Andric // The fuction which sets up all of the vtbl pointers for kmp_user_lock_t. 9030b57cec5SDimitry Andric extern void __kmp_set_user_lock_vptrs(kmp_lock_kind_t user_lock_kind); 9040b57cec5SDimitry Andric 9050b57cec5SDimitry Andric // Macros for binding user lock functions. 9060b57cec5SDimitry Andric #define KMP_BIND_USER_LOCK_TEMPLATE(nest, kind, suffix) \ 9070b57cec5SDimitry Andric { \ 9080b57cec5SDimitry Andric __kmp_acquire##nest##user_lock_with_checks_ = (int (*)( \ 9090b57cec5SDimitry Andric kmp_user_lock_p, kmp_int32))__kmp_acquire##nest##kind##_##suffix; \ 9100b57cec5SDimitry Andric __kmp_release##nest##user_lock_with_checks_ = (int (*)( \ 9110b57cec5SDimitry Andric kmp_user_lock_p, kmp_int32))__kmp_release##nest##kind##_##suffix; \ 9120b57cec5SDimitry Andric __kmp_test##nest##user_lock_with_checks_ = (int (*)( \ 9130b57cec5SDimitry Andric kmp_user_lock_p, kmp_int32))__kmp_test##nest##kind##_##suffix; \ 9140b57cec5SDimitry Andric __kmp_init##nest##user_lock_with_checks_ = \ 9150b57cec5SDimitry Andric (void (*)(kmp_user_lock_p))__kmp_init##nest##kind##_##suffix; \ 9160b57cec5SDimitry Andric __kmp_destroy##nest##user_lock_with_checks_ = \ 9170b57cec5SDimitry Andric (void (*)(kmp_user_lock_p))__kmp_destroy##nest##kind##_##suffix; \ 9180b57cec5SDimitry Andric } 9190b57cec5SDimitry Andric 9200b57cec5SDimitry Andric #define KMP_BIND_USER_LOCK(kind) KMP_BIND_USER_LOCK_TEMPLATE(_, kind, lock) 9210b57cec5SDimitry Andric #define KMP_BIND_USER_LOCK_WITH_CHECKS(kind) \ 9220b57cec5SDimitry Andric KMP_BIND_USER_LOCK_TEMPLATE(_, kind, lock_with_checks) 9230b57cec5SDimitry Andric #define KMP_BIND_NESTED_USER_LOCK(kind) \ 9240b57cec5SDimitry Andric KMP_BIND_USER_LOCK_TEMPLATE(_nested_, kind, lock) 9250b57cec5SDimitry Andric #define KMP_BIND_NESTED_USER_LOCK_WITH_CHECKS(kind) \ 9260b57cec5SDimitry Andric KMP_BIND_USER_LOCK_TEMPLATE(_nested_, kind, lock_with_checks) 9270b57cec5SDimitry Andric 9280b57cec5SDimitry Andric // User lock table & lock allocation 9290b57cec5SDimitry Andric /* On 64-bit Linux* OS (and OS X*) GNU compiler allocates only 4 bytems memory 9300b57cec5SDimitry Andric for lock variable, which is not enough to store a pointer, so we have to use 9310b57cec5SDimitry Andric lock indexes instead of pointers and maintain lock table to map indexes to 9320b57cec5SDimitry Andric pointers. 9330b57cec5SDimitry Andric 9340b57cec5SDimitry Andric 9350b57cec5SDimitry Andric Note: The first element of the table is not a pointer to lock! It is a 9360b57cec5SDimitry Andric pointer to previously allocated table (or NULL if it is the first table). 9370b57cec5SDimitry Andric 9380b57cec5SDimitry Andric Usage: 9390b57cec5SDimitry Andric 9400b57cec5SDimitry Andric if ( OMP_LOCK_T_SIZE < sizeof( <lock> ) ) { // or OMP_NEST_LOCK_T_SIZE 9410b57cec5SDimitry Andric Lock table is fully utilized. User locks are indexes, so table is used on 9420b57cec5SDimitry Andric user lock operation. 9430b57cec5SDimitry Andric Note: it may be the case (lin_32) that we don't need to use a lock 9440b57cec5SDimitry Andric table for regular locks, but do need the table for nested locks. 9450b57cec5SDimitry Andric } 9460b57cec5SDimitry Andric else { 9470b57cec5SDimitry Andric Lock table initialized but not actually used. 9480b57cec5SDimitry Andric } 9490b57cec5SDimitry Andric */ 9500b57cec5SDimitry Andric 9510b57cec5SDimitry Andric struct kmp_lock_table { 9520b57cec5SDimitry Andric kmp_lock_index_t used; // Number of used elements 9530b57cec5SDimitry Andric kmp_lock_index_t allocated; // Number of allocated elements 9540b57cec5SDimitry Andric kmp_user_lock_p *table; // Lock table. 9550b57cec5SDimitry Andric }; 9560b57cec5SDimitry Andric 9570b57cec5SDimitry Andric typedef struct kmp_lock_table kmp_lock_table_t; 9580b57cec5SDimitry Andric 9590b57cec5SDimitry Andric extern kmp_lock_table_t __kmp_user_lock_table; 9600b57cec5SDimitry Andric extern kmp_user_lock_p __kmp_lock_pool; 9610b57cec5SDimitry Andric 9620b57cec5SDimitry Andric struct kmp_block_of_locks { 9630b57cec5SDimitry Andric struct kmp_block_of_locks *next_block; 9640b57cec5SDimitry Andric void *locks; 9650b57cec5SDimitry Andric }; 9660b57cec5SDimitry Andric 9670b57cec5SDimitry Andric typedef struct kmp_block_of_locks kmp_block_of_locks_t; 9680b57cec5SDimitry Andric 9690b57cec5SDimitry Andric extern kmp_block_of_locks_t *__kmp_lock_blocks; 9700b57cec5SDimitry Andric extern int __kmp_num_locks_in_block; 9710b57cec5SDimitry Andric 9720b57cec5SDimitry Andric extern kmp_user_lock_p __kmp_user_lock_allocate(void **user_lock, 9730b57cec5SDimitry Andric kmp_int32 gtid, 9740b57cec5SDimitry Andric kmp_lock_flags_t flags); 9750b57cec5SDimitry Andric extern void __kmp_user_lock_free(void **user_lock, kmp_int32 gtid, 9760b57cec5SDimitry Andric kmp_user_lock_p lck); 9770b57cec5SDimitry Andric extern kmp_user_lock_p __kmp_lookup_user_lock(void **user_lock, 9780b57cec5SDimitry Andric char const *func); 9790b57cec5SDimitry Andric extern void __kmp_cleanup_user_locks(); 9800b57cec5SDimitry Andric 9810b57cec5SDimitry Andric #define KMP_CHECK_USER_LOCK_INIT() \ 9820b57cec5SDimitry Andric { \ 9830b57cec5SDimitry Andric if (!TCR_4(__kmp_init_user_locks)) { \ 9840b57cec5SDimitry Andric __kmp_acquire_bootstrap_lock(&__kmp_initz_lock); \ 9850b57cec5SDimitry Andric if (!TCR_4(__kmp_init_user_locks)) { \ 9860b57cec5SDimitry Andric TCW_4(__kmp_init_user_locks, TRUE); \ 9870b57cec5SDimitry Andric } \ 9880b57cec5SDimitry Andric __kmp_release_bootstrap_lock(&__kmp_initz_lock); \ 9890b57cec5SDimitry Andric } \ 9900b57cec5SDimitry Andric } 9910b57cec5SDimitry Andric 9920b57cec5SDimitry Andric #endif // KMP_USE_DYNAMIC_LOCK 9930b57cec5SDimitry Andric 9940b57cec5SDimitry Andric #undef KMP_PAD 9950b57cec5SDimitry Andric #undef KMP_GTID_DNE 9960b57cec5SDimitry Andric 9970b57cec5SDimitry Andric #if KMP_USE_DYNAMIC_LOCK 9980b57cec5SDimitry Andric // KMP_USE_DYNAMIC_LOCK enables dynamic dispatch of lock functions without 9990b57cec5SDimitry Andric // breaking the current compatibility. Essential functionality of this new code 10000b57cec5SDimitry Andric // is dynamic dispatch, but it also implements (or enables implementation of) 10010b57cec5SDimitry Andric // hinted user lock and critical section which will be part of OMP 4.5 soon. 10020b57cec5SDimitry Andric // 10030b57cec5SDimitry Andric // Lock type can be decided at creation time (i.e., lock initialization), and 10040b57cec5SDimitry Andric // subsequent lock function call on the created lock object requires type 10050b57cec5SDimitry Andric // extraction and call through jump table using the extracted type. This type 10060b57cec5SDimitry Andric // information is stored in two different ways depending on the size of the lock 10070b57cec5SDimitry Andric // object, and we differentiate lock types by this size requirement - direct and 10080b57cec5SDimitry Andric // indirect locks. 10090b57cec5SDimitry Andric // 10100b57cec5SDimitry Andric // Direct locks: 10110b57cec5SDimitry Andric // A direct lock object fits into the space created by the compiler for an 10120b57cec5SDimitry Andric // omp_lock_t object, and TAS/Futex lock falls into this category. We use low 10130b57cec5SDimitry Andric // one byte of the lock object as the storage for the lock type, and appropriate 10140b57cec5SDimitry Andric // bit operation is required to access the data meaningful to the lock 10150b57cec5SDimitry Andric // algorithms. Also, to differentiate direct lock from indirect lock, 1 is 10160b57cec5SDimitry Andric // written to LSB of the lock object. The newly introduced "hle" lock is also a 10170b57cec5SDimitry Andric // direct lock. 10180b57cec5SDimitry Andric // 10190b57cec5SDimitry Andric // Indirect locks: 10200b57cec5SDimitry Andric // An indirect lock object requires more space than the compiler-generated 10210b57cec5SDimitry Andric // space, and it should be allocated from heap. Depending on the size of the 10220b57cec5SDimitry Andric // compiler-generated space for the lock (i.e., size of omp_lock_t), this 10230b57cec5SDimitry Andric // omp_lock_t object stores either the address of the heap-allocated indirect 10240b57cec5SDimitry Andric // lock (void * fits in the object) or an index to the indirect lock table entry 10250b57cec5SDimitry Andric // that holds the address. Ticket/Queuing/DRDPA/Adaptive lock falls into this 10260b57cec5SDimitry Andric // category, and the newly introduced "rtm" lock is also an indirect lock which 10270b57cec5SDimitry Andric // was implemented on top of the Queuing lock. When the omp_lock_t object holds 10280b57cec5SDimitry Andric // an index (not lock address), 0 is written to LSB to differentiate the lock 10290b57cec5SDimitry Andric // from a direct lock, and the remaining part is the actual index to the 10300b57cec5SDimitry Andric // indirect lock table. 10310b57cec5SDimitry Andric 10320b57cec5SDimitry Andric #include <stdint.h> // for uintptr_t 10330b57cec5SDimitry Andric 10340b57cec5SDimitry Andric // Shortcuts 10350b57cec5SDimitry Andric #define KMP_USE_INLINED_TAS \ 10360b57cec5SDimitry Andric (KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM)) && 1 10370b57cec5SDimitry Andric #define KMP_USE_INLINED_FUTEX KMP_USE_FUTEX && 0 10380b57cec5SDimitry Andric 10390b57cec5SDimitry Andric // List of lock definitions; all nested locks are indirect locks. 10400b57cec5SDimitry Andric // hle lock is xchg lock prefixed with XACQUIRE/XRELEASE. 10410b57cec5SDimitry Andric // All nested locks are indirect lock types. 10420b57cec5SDimitry Andric #if KMP_USE_TSX 10430b57cec5SDimitry Andric #if KMP_USE_FUTEX 10440b57cec5SDimitry Andric #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(futex, a) m(hle, a) 10450b57cec5SDimitry Andric #define KMP_FOREACH_I_LOCK(m, a) \ 10460b57cec5SDimitry Andric m(ticket, a) m(queuing, a) m(adaptive, a) m(drdpa, a) m(rtm, a) \ 10470b57cec5SDimitry Andric m(nested_tas, a) m(nested_futex, a) m(nested_ticket, a) \ 10480b57cec5SDimitry Andric m(nested_queuing, a) m(nested_drdpa, a) 10490b57cec5SDimitry Andric #else 10500b57cec5SDimitry Andric #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(hle, a) 10510b57cec5SDimitry Andric #define KMP_FOREACH_I_LOCK(m, a) \ 10520b57cec5SDimitry Andric m(ticket, a) m(queuing, a) m(adaptive, a) m(drdpa, a) m(rtm, a) \ 10530b57cec5SDimitry Andric m(nested_tas, a) m(nested_ticket, a) m(nested_queuing, a) \ 10540b57cec5SDimitry Andric m(nested_drdpa, a) 10550b57cec5SDimitry Andric #endif // KMP_USE_FUTEX 10560b57cec5SDimitry Andric #define KMP_LAST_D_LOCK lockseq_hle 10570b57cec5SDimitry Andric #else 10580b57cec5SDimitry Andric #if KMP_USE_FUTEX 10590b57cec5SDimitry Andric #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(futex, a) 10600b57cec5SDimitry Andric #define KMP_FOREACH_I_LOCK(m, a) \ 10610b57cec5SDimitry Andric m(ticket, a) m(queuing, a) m(drdpa, a) m(nested_tas, a) m(nested_futex, a) \ 10620b57cec5SDimitry Andric m(nested_ticket, a) m(nested_queuing, a) m(nested_drdpa, a) 10630b57cec5SDimitry Andric #define KMP_LAST_D_LOCK lockseq_futex 10640b57cec5SDimitry Andric #else 10650b57cec5SDimitry Andric #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) 10660b57cec5SDimitry Andric #define KMP_FOREACH_I_LOCK(m, a) \ 10670b57cec5SDimitry Andric m(ticket, a) m(queuing, a) m(drdpa, a) m(nested_tas, a) m(nested_ticket, a) \ 10680b57cec5SDimitry Andric m(nested_queuing, a) m(nested_drdpa, a) 10690b57cec5SDimitry Andric #define KMP_LAST_D_LOCK lockseq_tas 10700b57cec5SDimitry Andric #endif // KMP_USE_FUTEX 10710b57cec5SDimitry Andric #endif // KMP_USE_TSX 10720b57cec5SDimitry Andric 10730b57cec5SDimitry Andric // Information used in dynamic dispatch 10740b57cec5SDimitry Andric #define KMP_LOCK_SHIFT \ 10750b57cec5SDimitry Andric 8 // number of low bits to be used as tag for direct locks 10760b57cec5SDimitry Andric #define KMP_FIRST_D_LOCK lockseq_tas 10770b57cec5SDimitry Andric #define KMP_FIRST_I_LOCK lockseq_ticket 10780b57cec5SDimitry Andric #define KMP_LAST_I_LOCK lockseq_nested_drdpa 10790b57cec5SDimitry Andric #define KMP_NUM_I_LOCKS \ 10800b57cec5SDimitry Andric (locktag_nested_drdpa + 1) // number of indirect lock types 10810b57cec5SDimitry Andric 10820b57cec5SDimitry Andric // Base type for dynamic locks. 10830b57cec5SDimitry Andric typedef kmp_uint32 kmp_dyna_lock_t; 10840b57cec5SDimitry Andric 10850b57cec5SDimitry Andric // Lock sequence that enumerates all lock kinds. Always make this enumeration 10860b57cec5SDimitry Andric // consistent with kmp_lockseq_t in the include directory. 10870b57cec5SDimitry Andric typedef enum { 10880b57cec5SDimitry Andric lockseq_indirect = 0, 10890b57cec5SDimitry Andric #define expand_seq(l, a) lockseq_##l, 10900b57cec5SDimitry Andric KMP_FOREACH_D_LOCK(expand_seq, 0) KMP_FOREACH_I_LOCK(expand_seq, 0) 10910b57cec5SDimitry Andric #undef expand_seq 10920b57cec5SDimitry Andric } kmp_dyna_lockseq_t; 10930b57cec5SDimitry Andric 10940b57cec5SDimitry Andric // Enumerates indirect lock tags. 10950b57cec5SDimitry Andric typedef enum { 10960b57cec5SDimitry Andric #define expand_tag(l, a) locktag_##l, 10970b57cec5SDimitry Andric KMP_FOREACH_I_LOCK(expand_tag, 0) 10980b57cec5SDimitry Andric #undef expand_tag 10990b57cec5SDimitry Andric } kmp_indirect_locktag_t; 11000b57cec5SDimitry Andric 11010b57cec5SDimitry Andric // Utility macros that extract information from lock sequences. 11020b57cec5SDimitry Andric #define KMP_IS_D_LOCK(seq) \ 11030b57cec5SDimitry Andric ((seq) >= KMP_FIRST_D_LOCK && (seq) <= KMP_LAST_D_LOCK) 11040b57cec5SDimitry Andric #define KMP_IS_I_LOCK(seq) \ 11050b57cec5SDimitry Andric ((seq) >= KMP_FIRST_I_LOCK && (seq) <= KMP_LAST_I_LOCK) 11060b57cec5SDimitry Andric #define KMP_GET_I_TAG(seq) (kmp_indirect_locktag_t)((seq)-KMP_FIRST_I_LOCK) 11070b57cec5SDimitry Andric #define KMP_GET_D_TAG(seq) ((seq) << 1 | 1) 11080b57cec5SDimitry Andric 11090b57cec5SDimitry Andric // Enumerates direct lock tags starting from indirect tag. 11100b57cec5SDimitry Andric typedef enum { 11110b57cec5SDimitry Andric #define expand_tag(l, a) locktag_##l = KMP_GET_D_TAG(lockseq_##l), 11120b57cec5SDimitry Andric KMP_FOREACH_D_LOCK(expand_tag, 0) 11130b57cec5SDimitry Andric #undef expand_tag 11140b57cec5SDimitry Andric } kmp_direct_locktag_t; 11150b57cec5SDimitry Andric 11160b57cec5SDimitry Andric // Indirect lock type 11170b57cec5SDimitry Andric typedef struct { 11180b57cec5SDimitry Andric kmp_user_lock_p lock; 11190b57cec5SDimitry Andric kmp_indirect_locktag_t type; 11200b57cec5SDimitry Andric } kmp_indirect_lock_t; 11210b57cec5SDimitry Andric 11220b57cec5SDimitry Andric // Function tables for direct locks. Set/unset/test differentiate functions 11230b57cec5SDimitry Andric // with/without consistency checking. 11240b57cec5SDimitry Andric extern void (*__kmp_direct_init[])(kmp_dyna_lock_t *, kmp_dyna_lockseq_t); 1125*489b1cf2SDimitry Andric extern void (**__kmp_direct_destroy)(kmp_dyna_lock_t *); 1126*489b1cf2SDimitry Andric extern int (**__kmp_direct_set)(kmp_dyna_lock_t *, kmp_int32); 1127*489b1cf2SDimitry Andric extern int (**__kmp_direct_unset)(kmp_dyna_lock_t *, kmp_int32); 1128*489b1cf2SDimitry Andric extern int (**__kmp_direct_test)(kmp_dyna_lock_t *, kmp_int32); 11290b57cec5SDimitry Andric 11300b57cec5SDimitry Andric // Function tables for indirect locks. Set/unset/test differentiate functions 11310b57cec5SDimitry Andric // with/withuot consistency checking. 11320b57cec5SDimitry Andric extern void (*__kmp_indirect_init[])(kmp_user_lock_p); 1133*489b1cf2SDimitry Andric extern void (**__kmp_indirect_destroy)(kmp_user_lock_p); 1134*489b1cf2SDimitry Andric extern int (**__kmp_indirect_set)(kmp_user_lock_p, kmp_int32); 1135*489b1cf2SDimitry Andric extern int (**__kmp_indirect_unset)(kmp_user_lock_p, kmp_int32); 1136*489b1cf2SDimitry Andric extern int (**__kmp_indirect_test)(kmp_user_lock_p, kmp_int32); 11370b57cec5SDimitry Andric 11380b57cec5SDimitry Andric // Extracts direct lock tag from a user lock pointer 11390b57cec5SDimitry Andric #define KMP_EXTRACT_D_TAG(l) \ 11400b57cec5SDimitry Andric (*((kmp_dyna_lock_t *)(l)) & ((1 << KMP_LOCK_SHIFT) - 1) & \ 11410b57cec5SDimitry Andric -(*((kmp_dyna_lock_t *)(l)) & 1)) 11420b57cec5SDimitry Andric 11430b57cec5SDimitry Andric // Extracts indirect lock index from a user lock pointer 11440b57cec5SDimitry Andric #define KMP_EXTRACT_I_INDEX(l) (*(kmp_lock_index_t *)(l) >> 1) 11450b57cec5SDimitry Andric 11460b57cec5SDimitry Andric // Returns function pointer to the direct lock function with l (kmp_dyna_lock_t 11470b57cec5SDimitry Andric // *) and op (operation type). 11480b57cec5SDimitry Andric #define KMP_D_LOCK_FUNC(l, op) __kmp_direct_##op[KMP_EXTRACT_D_TAG(l)] 11490b57cec5SDimitry Andric 11500b57cec5SDimitry Andric // Returns function pointer to the indirect lock function with l 11510b57cec5SDimitry Andric // (kmp_indirect_lock_t *) and op (operation type). 11520b57cec5SDimitry Andric #define KMP_I_LOCK_FUNC(l, op) \ 11530b57cec5SDimitry Andric __kmp_indirect_##op[((kmp_indirect_lock_t *)(l))->type] 11540b57cec5SDimitry Andric 11550b57cec5SDimitry Andric // Initializes a direct lock with the given lock pointer and lock sequence. 11560b57cec5SDimitry Andric #define KMP_INIT_D_LOCK(l, seq) \ 11570b57cec5SDimitry Andric __kmp_direct_init[KMP_GET_D_TAG(seq)]((kmp_dyna_lock_t *)l, seq) 11580b57cec5SDimitry Andric 11590b57cec5SDimitry Andric // Initializes an indirect lock with the given lock pointer and lock sequence. 11600b57cec5SDimitry Andric #define KMP_INIT_I_LOCK(l, seq) \ 11610b57cec5SDimitry Andric __kmp_direct_init[0]((kmp_dyna_lock_t *)(l), seq) 11620b57cec5SDimitry Andric 11630b57cec5SDimitry Andric // Returns "free" lock value for the given lock type. 11640b57cec5SDimitry Andric #define KMP_LOCK_FREE(type) (locktag_##type) 11650b57cec5SDimitry Andric 11660b57cec5SDimitry Andric // Returns "busy" lock value for the given lock teyp. 11670b57cec5SDimitry Andric #define KMP_LOCK_BUSY(v, type) ((v) << KMP_LOCK_SHIFT | locktag_##type) 11680b57cec5SDimitry Andric 11690b57cec5SDimitry Andric // Returns lock value after removing (shifting) lock tag. 11700b57cec5SDimitry Andric #define KMP_LOCK_STRIP(v) ((v) >> KMP_LOCK_SHIFT) 11710b57cec5SDimitry Andric 11720b57cec5SDimitry Andric // Initializes global states and data structures for managing dynamic user 11730b57cec5SDimitry Andric // locks. 11740b57cec5SDimitry Andric extern void __kmp_init_dynamic_user_locks(); 11750b57cec5SDimitry Andric 11760b57cec5SDimitry Andric // Allocates and returns an indirect lock with the given indirect lock tag. 11770b57cec5SDimitry Andric extern kmp_indirect_lock_t * 11780b57cec5SDimitry Andric __kmp_allocate_indirect_lock(void **, kmp_int32, kmp_indirect_locktag_t); 11790b57cec5SDimitry Andric 11800b57cec5SDimitry Andric // Cleans up global states and data structures for managing dynamic user locks. 11810b57cec5SDimitry Andric extern void __kmp_cleanup_indirect_user_locks(); 11820b57cec5SDimitry Andric 11830b57cec5SDimitry Andric // Default user lock sequence when not using hinted locks. 11840b57cec5SDimitry Andric extern kmp_dyna_lockseq_t __kmp_user_lock_seq; 11850b57cec5SDimitry Andric 11860b57cec5SDimitry Andric // Jump table for "set lock location", available only for indirect locks. 11870b57cec5SDimitry Andric extern void (*__kmp_indirect_set_location[KMP_NUM_I_LOCKS])(kmp_user_lock_p, 11880b57cec5SDimitry Andric const ident_t *); 11890b57cec5SDimitry Andric #define KMP_SET_I_LOCK_LOCATION(lck, loc) \ 11900b57cec5SDimitry Andric { \ 11910b57cec5SDimitry Andric if (__kmp_indirect_set_location[(lck)->type] != NULL) \ 11920b57cec5SDimitry Andric __kmp_indirect_set_location[(lck)->type]((lck)->lock, loc); \ 11930b57cec5SDimitry Andric } 11940b57cec5SDimitry Andric 11950b57cec5SDimitry Andric // Jump table for "set lock flags", available only for indirect locks. 11960b57cec5SDimitry Andric extern void (*__kmp_indirect_set_flags[KMP_NUM_I_LOCKS])(kmp_user_lock_p, 11970b57cec5SDimitry Andric kmp_lock_flags_t); 11980b57cec5SDimitry Andric #define KMP_SET_I_LOCK_FLAGS(lck, flag) \ 11990b57cec5SDimitry Andric { \ 12000b57cec5SDimitry Andric if (__kmp_indirect_set_flags[(lck)->type] != NULL) \ 12010b57cec5SDimitry Andric __kmp_indirect_set_flags[(lck)->type]((lck)->lock, flag); \ 12020b57cec5SDimitry Andric } 12030b57cec5SDimitry Andric 12040b57cec5SDimitry Andric // Jump table for "get lock location", available only for indirect locks. 12050b57cec5SDimitry Andric extern const ident_t *(*__kmp_indirect_get_location[KMP_NUM_I_LOCKS])( 12060b57cec5SDimitry Andric kmp_user_lock_p); 12070b57cec5SDimitry Andric #define KMP_GET_I_LOCK_LOCATION(lck) \ 12080b57cec5SDimitry Andric (__kmp_indirect_get_location[(lck)->type] != NULL \ 12090b57cec5SDimitry Andric ? __kmp_indirect_get_location[(lck)->type]((lck)->lock) \ 12100b57cec5SDimitry Andric : NULL) 12110b57cec5SDimitry Andric 12120b57cec5SDimitry Andric // Jump table for "get lock flags", available only for indirect locks. 12130b57cec5SDimitry Andric extern kmp_lock_flags_t (*__kmp_indirect_get_flags[KMP_NUM_I_LOCKS])( 12140b57cec5SDimitry Andric kmp_user_lock_p); 12150b57cec5SDimitry Andric #define KMP_GET_I_LOCK_FLAGS(lck) \ 12160b57cec5SDimitry Andric (__kmp_indirect_get_flags[(lck)->type] != NULL \ 12170b57cec5SDimitry Andric ? __kmp_indirect_get_flags[(lck)->type]((lck)->lock) \ 12180b57cec5SDimitry Andric : NULL) 12190b57cec5SDimitry Andric 12200b57cec5SDimitry Andric #define KMP_I_LOCK_CHUNK \ 12210b57cec5SDimitry Andric 1024 // number of kmp_indirect_lock_t objects to be allocated together 12220b57cec5SDimitry Andric 12230b57cec5SDimitry Andric // Lock table for indirect locks. 12240b57cec5SDimitry Andric typedef struct kmp_indirect_lock_table { 12250b57cec5SDimitry Andric kmp_indirect_lock_t **table; // blocks of indirect locks allocated 12260b57cec5SDimitry Andric kmp_lock_index_t size; // size of the indirect lock table 12270b57cec5SDimitry Andric kmp_lock_index_t next; // index to the next lock to be allocated 12280b57cec5SDimitry Andric } kmp_indirect_lock_table_t; 12290b57cec5SDimitry Andric 12300b57cec5SDimitry Andric extern kmp_indirect_lock_table_t __kmp_i_lock_table; 12310b57cec5SDimitry Andric 12320b57cec5SDimitry Andric // Returns the indirect lock associated with the given index. 12330b57cec5SDimitry Andric #define KMP_GET_I_LOCK(index) \ 12340b57cec5SDimitry Andric (*(__kmp_i_lock_table.table + (index) / KMP_I_LOCK_CHUNK) + \ 12350b57cec5SDimitry Andric (index) % KMP_I_LOCK_CHUNK) 12360b57cec5SDimitry Andric 12370b57cec5SDimitry Andric // Number of locks in a lock block, which is fixed to "1" now. 12380b57cec5SDimitry Andric // TODO: No lock block implementation now. If we do support, we need to manage 12390b57cec5SDimitry Andric // lock block data structure for each indirect lock type. 12400b57cec5SDimitry Andric extern int __kmp_num_locks_in_block; 12410b57cec5SDimitry Andric 12420b57cec5SDimitry Andric // Fast lock table lookup without consistency checking 12430b57cec5SDimitry Andric #define KMP_LOOKUP_I_LOCK(l) \ 12440b57cec5SDimitry Andric ((OMP_LOCK_T_SIZE < sizeof(void *)) ? KMP_GET_I_LOCK(KMP_EXTRACT_I_INDEX(l)) \ 12450b57cec5SDimitry Andric : *((kmp_indirect_lock_t **)(l))) 12460b57cec5SDimitry Andric 12470b57cec5SDimitry Andric // Used once in kmp_error.cpp 12480b57cec5SDimitry Andric extern kmp_int32 __kmp_get_user_lock_owner(kmp_user_lock_p, kmp_uint32); 12490b57cec5SDimitry Andric 12500b57cec5SDimitry Andric #else // KMP_USE_DYNAMIC_LOCK 12510b57cec5SDimitry Andric 12520b57cec5SDimitry Andric #define KMP_LOCK_BUSY(v, type) (v) 12530b57cec5SDimitry Andric #define KMP_LOCK_FREE(type) 0 12540b57cec5SDimitry Andric #define KMP_LOCK_STRIP(v) (v) 12550b57cec5SDimitry Andric 12560b57cec5SDimitry Andric #endif // KMP_USE_DYNAMIC_LOCK 12570b57cec5SDimitry Andric 12580b57cec5SDimitry Andric // data structure for using backoff within spin locks. 12590b57cec5SDimitry Andric typedef struct { 12600b57cec5SDimitry Andric kmp_uint32 step; // current step 12610b57cec5SDimitry Andric kmp_uint32 max_backoff; // upper bound of outer delay loop 12620b57cec5SDimitry Andric kmp_uint32 min_tick; // size of inner delay loop in ticks (machine-dependent) 12630b57cec5SDimitry Andric } kmp_backoff_t; 12640b57cec5SDimitry Andric 12650b57cec5SDimitry Andric // Runtime's default backoff parameters 12660b57cec5SDimitry Andric extern kmp_backoff_t __kmp_spin_backoff_params; 12670b57cec5SDimitry Andric 12680b57cec5SDimitry Andric // Backoff function 12690b57cec5SDimitry Andric extern void __kmp_spin_backoff(kmp_backoff_t *); 12700b57cec5SDimitry Andric 12710b57cec5SDimitry Andric #ifdef __cplusplus 12720b57cec5SDimitry Andric } // extern "C" 12730b57cec5SDimitry Andric #endif // __cplusplus 12740b57cec5SDimitry Andric 12750b57cec5SDimitry Andric #endif /* KMP_LOCK_H */ 1276