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