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