1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- /*
- * kmp_lock.h -- lock header file
- */
- //===----------------------------------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef KMP_LOCK_H
- #define KMP_LOCK_H
- #include <limits.h> // CHAR_BIT
- #include <stddef.h> // offsetof
- #include "kmp_debug.h"
- #include "kmp_os.h"
- #ifdef __cplusplus
- #include <atomic>
- extern "C" {
- #endif // __cplusplus
- // ----------------------------------------------------------------------------
- // Have to copy these definitions from kmp.h because kmp.h cannot be included
- // due to circular dependencies. Will undef these at end of file.
- #define KMP_PAD(type, sz) \
- (sizeof(type) + (sz - ((sizeof(type) - 1) % (sz)) - 1))
- #define KMP_GTID_DNE (-2)
- // Forward declaration of ident and ident_t
- struct ident;
- typedef struct ident ident_t;
- // End of copied code.
- // ----------------------------------------------------------------------------
- // We need to know the size of the area we can assume that the compiler(s)
- // allocated for objects of type omp_lock_t and omp_nest_lock_t. The Intel
- // compiler always allocates a pointer-sized area, as does visual studio.
- //
- // gcc however, only allocates 4 bytes for regular locks, even on 64-bit
- // intel archs. It allocates at least 8 bytes for nested lock (more on
- // recent versions), but we are bounded by the pointer-sized chunks that
- // the Intel compiler allocates.
- #if KMP_OS_LINUX && defined(KMP_GOMP_COMPAT)
- #define OMP_LOCK_T_SIZE sizeof(int)
- #define OMP_NEST_LOCK_T_SIZE sizeof(void *)
- #else
- #define OMP_LOCK_T_SIZE sizeof(void *)
- #define OMP_NEST_LOCK_T_SIZE sizeof(void *)
- #endif
- // The Intel compiler allocates a 32-byte chunk for a critical section.
- // Both gcc and visual studio only allocate enough space for a pointer.
- // Sometimes we know that the space was allocated by the Intel compiler.
- #define OMP_CRITICAL_SIZE sizeof(void *)
- #define INTEL_CRITICAL_SIZE 32
- // lock flags
- typedef kmp_uint32 kmp_lock_flags_t;
- #define kmp_lf_critical_section 1
- // When a lock table is used, the indices are of kmp_lock_index_t
- typedef kmp_uint32 kmp_lock_index_t;
- // When memory allocated for locks are on the lock pool (free list),
- // it is treated as structs of this type.
- struct kmp_lock_pool {
- union kmp_user_lock *next;
- kmp_lock_index_t index;
- };
- typedef struct kmp_lock_pool kmp_lock_pool_t;
- extern void __kmp_validate_locks(void);
- // ----------------------------------------------------------------------------
- // There are 5 lock implementations:
- // 1. Test and set locks.
- // 2. futex locks (Linux* OS on x86 and
- // Intel(R) Many Integrated Core Architecture)
- // 3. Ticket (Lamport bakery) locks.
- // 4. Queuing locks (with separate spin fields).
- // 5. DRPA (Dynamically Reconfigurable Distributed Polling Area) locks
- //
- // and 3 lock purposes:
- // 1. Bootstrap locks -- Used for a few locks available at library
- // startup-shutdown time.
- // These do not require non-negative global thread ID's.
- // 2. Internal RTL locks -- Used everywhere else in the RTL
- // 3. User locks (includes critical sections)
- // ----------------------------------------------------------------------------
- // ============================================================================
- // Lock implementations.
- //
- // Test and set locks.
- //
- // Non-nested test and set locks differ from the other lock kinds (except
- // futex) in that we use the memory allocated by the compiler for the lock,
- // rather than a pointer to it.
- //
- // On lin32, lin_32e, and win_32, the space allocated may be as small as 4
- // bytes, so we have to use a lock table for nested locks, and avoid accessing
- // the depth_locked field for non-nested locks.
- //
- // Information normally available to the tools, such as lock location, lock
- // usage (normal lock vs. critical section), etc. is not available with test and
- // set locks.
- // ----------------------------------------------------------------------------
- struct kmp_base_tas_lock {
- // KMP_LOCK_FREE(tas) => unlocked; locked: (gtid+1) of owning thread
- std::atomic<kmp_int32> poll;
- kmp_int32 depth_locked; // depth locked, for nested locks only
- };
- typedef struct kmp_base_tas_lock kmp_base_tas_lock_t;
- union kmp_tas_lock {
- kmp_base_tas_lock_t lk;
- kmp_lock_pool_t pool; // make certain struct is large enough
- double lk_align; // use worst case alignment; no cache line padding
- };
- typedef union kmp_tas_lock kmp_tas_lock_t;
- // Static initializer for test and set lock variables. Usage:
- // kmp_tas_lock_t xlock = KMP_TAS_LOCK_INITIALIZER( xlock );
- #define KMP_TAS_LOCK_INITIALIZER(lock) \
- { \
- { ATOMIC_VAR_INIT(KMP_LOCK_FREE(tas)), 0 } \
- }
- extern int __kmp_acquire_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_tas_lock(kmp_tas_lock_t *lck);
- extern void __kmp_destroy_tas_lock(kmp_tas_lock_t *lck);
- extern int __kmp_acquire_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_nested_tas_lock(kmp_tas_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_nested_tas_lock(kmp_tas_lock_t *lck);
- extern void __kmp_destroy_nested_tas_lock(kmp_tas_lock_t *lck);
- #define KMP_LOCK_RELEASED 1
- #define KMP_LOCK_STILL_HELD 0
- #define KMP_LOCK_ACQUIRED_FIRST 1
- #define KMP_LOCK_ACQUIRED_NEXT 0
- #ifndef KMP_USE_FUTEX
- #define KMP_USE_FUTEX \
- (KMP_OS_LINUX && \
- (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64))
- #endif
- #if KMP_USE_FUTEX
- // ----------------------------------------------------------------------------
- // futex locks. futex locks are only available on Linux* OS.
- //
- // Like non-nested test and set lock, non-nested futex locks use the memory
- // allocated by the compiler for the lock, rather than a pointer to it.
- //
- // Information normally available to the tools, such as lock location, lock
- // usage (normal lock vs. critical section), etc. is not available with test and
- // set locks. With non-nested futex locks, the lock owner is not even available.
- // ----------------------------------------------------------------------------
- struct kmp_base_futex_lock {
- volatile kmp_int32 poll; // KMP_LOCK_FREE(futex) => unlocked
- // 2*(gtid+1) of owning thread, 0 if unlocked
- // locked: (gtid+1) of owning thread
- kmp_int32 depth_locked; // depth locked, for nested locks only
- };
- typedef struct kmp_base_futex_lock kmp_base_futex_lock_t;
- union kmp_futex_lock {
- kmp_base_futex_lock_t lk;
- kmp_lock_pool_t pool; // make certain struct is large enough
- double lk_align; // use worst case alignment
- // no cache line padding
- };
- typedef union kmp_futex_lock kmp_futex_lock_t;
- // Static initializer for futex lock variables. Usage:
- // kmp_futex_lock_t xlock = KMP_FUTEX_LOCK_INITIALIZER( xlock );
- #define KMP_FUTEX_LOCK_INITIALIZER(lock) \
- { \
- { KMP_LOCK_FREE(futex), 0 } \
- }
- extern int __kmp_acquire_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_futex_lock(kmp_futex_lock_t *lck);
- extern void __kmp_destroy_futex_lock(kmp_futex_lock_t *lck);
- extern int __kmp_acquire_nested_futex_lock(kmp_futex_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_test_nested_futex_lock(kmp_futex_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_nested_futex_lock(kmp_futex_lock_t *lck,
- kmp_int32 gtid);
- extern void __kmp_init_nested_futex_lock(kmp_futex_lock_t *lck);
- extern void __kmp_destroy_nested_futex_lock(kmp_futex_lock_t *lck);
- #endif // KMP_USE_FUTEX
- // ----------------------------------------------------------------------------
- // Ticket locks.
- #ifdef __cplusplus
- #ifdef _MSC_VER
- // MSVC won't allow use of std::atomic<> in a union since it has non-trivial
- // copy constructor.
- struct kmp_base_ticket_lock {
- // `initialized' must be the first entry in the lock data structure!
- std::atomic_bool initialized;
- volatile union kmp_ticket_lock *self; // points to the lock union
- ident_t const *location; // Source code location of omp_init_lock().
- std::atomic_uint
- next_ticket; // ticket number to give to next thread which acquires
- std::atomic_uint now_serving; // ticket number for thread which holds the lock
- std::atomic_int owner_id; // (gtid+1) of owning thread, 0 if unlocked
- std::atomic_int depth_locked; // depth locked, for nested locks only
- kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock
- };
- #else
- struct kmp_base_ticket_lock {
- // `initialized' must be the first entry in the lock data structure!
- std::atomic<bool> initialized;
- volatile union kmp_ticket_lock *self; // points to the lock union
- ident_t const *location; // Source code location of omp_init_lock().
- std::atomic<unsigned>
- next_ticket; // ticket number to give to next thread which acquires
- std::atomic<unsigned>
- now_serving; // ticket number for thread which holds the lock
- std::atomic<int> owner_id; // (gtid+1) of owning thread, 0 if unlocked
- std::atomic<int> depth_locked; // depth locked, for nested locks only
- kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock
- };
- #endif
- #else // __cplusplus
- struct kmp_base_ticket_lock;
- #endif // !__cplusplus
- typedef struct kmp_base_ticket_lock kmp_base_ticket_lock_t;
- union KMP_ALIGN_CACHE kmp_ticket_lock {
- kmp_base_ticket_lock_t
- lk; // This field must be first to allow static initializing.
- kmp_lock_pool_t pool;
- double lk_align; // use worst case alignment
- char lk_pad[KMP_PAD(kmp_base_ticket_lock_t, CACHE_LINE)];
- };
- typedef union kmp_ticket_lock kmp_ticket_lock_t;
- // Static initializer for simple ticket lock variables. Usage:
- // kmp_ticket_lock_t xlock = KMP_TICKET_LOCK_INITIALIZER( xlock );
- // Note the macro argument. It is important to make var properly initialized.
- #define KMP_TICKET_LOCK_INITIALIZER(lock) \
- { \
- { \
- ATOMIC_VAR_INIT(true) \
- , &(lock), NULL, ATOMIC_VAR_INIT(0U), ATOMIC_VAR_INIT(0U), \
- ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(-1) \
- } \
- }
- extern int __kmp_acquire_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_ticket_lock_with_cheks(kmp_ticket_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_release_ticket_lock(kmp_ticket_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_ticket_lock(kmp_ticket_lock_t *lck);
- extern void __kmp_destroy_ticket_lock(kmp_ticket_lock_t *lck);
- extern int __kmp_acquire_nested_ticket_lock(kmp_ticket_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_test_nested_ticket_lock(kmp_ticket_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_release_nested_ticket_lock(kmp_ticket_lock_t *lck,
- kmp_int32 gtid);
- extern void __kmp_init_nested_ticket_lock(kmp_ticket_lock_t *lck);
- extern void __kmp_destroy_nested_ticket_lock(kmp_ticket_lock_t *lck);
- // ----------------------------------------------------------------------------
- // Queuing locks.
- #if KMP_USE_ADAPTIVE_LOCKS
- struct kmp_adaptive_lock_info;
- typedef struct kmp_adaptive_lock_info kmp_adaptive_lock_info_t;
- #if KMP_DEBUG_ADAPTIVE_LOCKS
- struct kmp_adaptive_lock_statistics {
- /* So we can get stats from locks that haven't been destroyed. */
- kmp_adaptive_lock_info_t *next;
- kmp_adaptive_lock_info_t *prev;
- /* Other statistics */
- kmp_uint32 successfulSpeculations;
- kmp_uint32 hardFailedSpeculations;
- kmp_uint32 softFailedSpeculations;
- kmp_uint32 nonSpeculativeAcquires;
- kmp_uint32 nonSpeculativeAcquireAttempts;
- kmp_uint32 lemmingYields;
- };
- typedef struct kmp_adaptive_lock_statistics kmp_adaptive_lock_statistics_t;
- extern void __kmp_print_speculative_stats();
- extern void __kmp_init_speculative_stats();
- #endif // KMP_DEBUG_ADAPTIVE_LOCKS
- struct kmp_adaptive_lock_info {
- /* Values used for adaptivity.
- Although these are accessed from multiple threads we don't access them
- atomically, because if we miss updates it probably doesn't matter much. (It
- just affects our decision about whether to try speculation on the lock). */
- kmp_uint32 volatile badness;
- kmp_uint32 volatile acquire_attempts;
- /* Parameters of the lock. */
- kmp_uint32 max_badness;
- kmp_uint32 max_soft_retries;
- #if KMP_DEBUG_ADAPTIVE_LOCKS
- kmp_adaptive_lock_statistics_t volatile stats;
- #endif
- };
- #endif // KMP_USE_ADAPTIVE_LOCKS
- struct kmp_base_queuing_lock {
- // `initialized' must be the first entry in the lock data structure!
- volatile union kmp_queuing_lock
- *initialized; // Points to the lock union if in initialized state.
- ident_t const *location; // Source code location of omp_init_lock().
- KMP_ALIGN(8) // tail_id must be 8-byte aligned!
- volatile kmp_int32
- tail_id; // (gtid+1) of thread at tail of wait queue, 0 if empty
- // Must be no padding here since head/tail used in 8-byte CAS
- volatile kmp_int32
- head_id; // (gtid+1) of thread at head of wait queue, 0 if empty
- // Decl order assumes little endian
- // bakery-style lock
- volatile kmp_uint32
- next_ticket; // ticket number to give to next thread which acquires
- volatile kmp_uint32
- now_serving; // ticket number for thread which holds the lock
- volatile kmp_int32 owner_id; // (gtid+1) of owning thread, 0 if unlocked
- kmp_int32 depth_locked; // depth locked, for nested locks only
- kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock
- };
- typedef struct kmp_base_queuing_lock kmp_base_queuing_lock_t;
- KMP_BUILD_ASSERT(offsetof(kmp_base_queuing_lock_t, tail_id) % 8 == 0);
- union KMP_ALIGN_CACHE kmp_queuing_lock {
- kmp_base_queuing_lock_t
- lk; // This field must be first to allow static initializing.
- kmp_lock_pool_t pool;
- double lk_align; // use worst case alignment
- char lk_pad[KMP_PAD(kmp_base_queuing_lock_t, CACHE_LINE)];
- };
- typedef union kmp_queuing_lock kmp_queuing_lock_t;
- extern int __kmp_acquire_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_queuing_lock(kmp_queuing_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_queuing_lock(kmp_queuing_lock_t *lck);
- extern void __kmp_destroy_queuing_lock(kmp_queuing_lock_t *lck);
- extern int __kmp_acquire_nested_queuing_lock(kmp_queuing_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_test_nested_queuing_lock(kmp_queuing_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_release_nested_queuing_lock(kmp_queuing_lock_t *lck,
- kmp_int32 gtid);
- extern void __kmp_init_nested_queuing_lock(kmp_queuing_lock_t *lck);
- extern void __kmp_destroy_nested_queuing_lock(kmp_queuing_lock_t *lck);
- #if KMP_USE_ADAPTIVE_LOCKS
- // ----------------------------------------------------------------------------
- // Adaptive locks.
- struct kmp_base_adaptive_lock {
- kmp_base_queuing_lock qlk;
- KMP_ALIGN(CACHE_LINE)
- kmp_adaptive_lock_info_t
- adaptive; // Information for the speculative adaptive lock
- };
- typedef struct kmp_base_adaptive_lock kmp_base_adaptive_lock_t;
- union KMP_ALIGN_CACHE kmp_adaptive_lock {
- kmp_base_adaptive_lock_t lk;
- kmp_lock_pool_t pool;
- double lk_align;
- char lk_pad[KMP_PAD(kmp_base_adaptive_lock_t, CACHE_LINE)];
- };
- typedef union kmp_adaptive_lock kmp_adaptive_lock_t;
- #define GET_QLK_PTR(l) ((kmp_queuing_lock_t *)&(l)->lk.qlk)
- #endif // KMP_USE_ADAPTIVE_LOCKS
- // ----------------------------------------------------------------------------
- // DRDPA ticket locks.
- struct kmp_base_drdpa_lock {
- // All of the fields on the first cache line are only written when
- // initializing or reconfiguring the lock. These are relatively rare
- // operations, so data from the first cache line will usually stay resident in
- // the cache of each thread trying to acquire the lock.
- //
- // initialized must be the first entry in the lock data structure!
- KMP_ALIGN_CACHE
- volatile union kmp_drdpa_lock
- *initialized; // points to the lock union if in initialized state
- ident_t const *location; // Source code location of omp_init_lock().
- std::atomic<std::atomic<kmp_uint64> *> polls;
- std::atomic<kmp_uint64> mask; // is 2**num_polls-1 for mod op
- kmp_uint64 cleanup_ticket; // thread with cleanup ticket
- std::atomic<kmp_uint64> *old_polls; // will deallocate old_polls
- kmp_uint32 num_polls; // must be power of 2
- // next_ticket it needs to exist in a separate cache line, as it is
- // invalidated every time a thread takes a new ticket.
- KMP_ALIGN_CACHE
- std::atomic<kmp_uint64> next_ticket;
- // now_serving is used to store our ticket value while we hold the lock. It
- // has a slightly different meaning in the DRDPA ticket locks (where it is
- // written by the acquiring thread) than it does in the simple ticket locks
- // (where it is written by the releasing thread).
- //
- // Since now_serving is only read and written in the critical section,
- // it is non-volatile, but it needs to exist on a separate cache line,
- // as it is invalidated at every lock acquire.
- //
- // Likewise, the vars used for nested locks (owner_id and depth_locked) are
- // only written by the thread owning the lock, so they are put in this cache
- // line. owner_id is read by other threads, so it must be declared volatile.
- KMP_ALIGN_CACHE
- kmp_uint64 now_serving; // doesn't have to be volatile
- volatile kmp_uint32 owner_id; // (gtid+1) of owning thread, 0 if unlocked
- kmp_int32 depth_locked; // depth locked
- kmp_lock_flags_t flags; // lock specifics, e.g. critical section lock
- };
- typedef struct kmp_base_drdpa_lock kmp_base_drdpa_lock_t;
- union KMP_ALIGN_CACHE kmp_drdpa_lock {
- kmp_base_drdpa_lock_t
- lk; // This field must be first to allow static initializing. */
- kmp_lock_pool_t pool;
- double lk_align; // use worst case alignment
- char lk_pad[KMP_PAD(kmp_base_drdpa_lock_t, CACHE_LINE)];
- };
- typedef union kmp_drdpa_lock kmp_drdpa_lock_t;
- extern int __kmp_acquire_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_test_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid);
- extern void __kmp_init_drdpa_lock(kmp_drdpa_lock_t *lck);
- extern void __kmp_destroy_drdpa_lock(kmp_drdpa_lock_t *lck);
- extern int __kmp_acquire_nested_drdpa_lock(kmp_drdpa_lock_t *lck,
- kmp_int32 gtid);
- extern int __kmp_test_nested_drdpa_lock(kmp_drdpa_lock_t *lck, kmp_int32 gtid);
- extern int __kmp_release_nested_drdpa_lock(kmp_drdpa_lock_t *lck,
- kmp_int32 gtid);
- extern void __kmp_init_nested_drdpa_lock(kmp_drdpa_lock_t *lck);
- extern void __kmp_destroy_nested_drdpa_lock(kmp_drdpa_lock_t *lck);
- // ============================================================================
- // Lock purposes.
- // ============================================================================
- // Bootstrap locks.
- //
- // Bootstrap locks -- very few locks used at library initialization time.
- // Bootstrap locks are currently implemented as ticket locks.
- // They could also be implemented as test and set lock, but cannot be
- // implemented with other lock kinds as they require gtids which are not
- // available at initialization time.
- typedef kmp_ticket_lock_t kmp_bootstrap_lock_t;
- #define KMP_BOOTSTRAP_LOCK_INITIALIZER(lock) KMP_TICKET_LOCK_INITIALIZER((lock))
- #define KMP_BOOTSTRAP_LOCK_INIT(lock) \
- kmp_bootstrap_lock_t lock = KMP_TICKET_LOCK_INITIALIZER(lock)
- static inline int __kmp_acquire_bootstrap_lock(kmp_bootstrap_lock_t *lck) {
- return __kmp_acquire_ticket_lock(lck, KMP_GTID_DNE);
- }
- static inline int __kmp_test_bootstrap_lock(kmp_bootstrap_lock_t *lck) {
- return __kmp_test_ticket_lock(lck, KMP_GTID_DNE);
- }
- static inline void __kmp_release_bootstrap_lock(kmp_bootstrap_lock_t *lck) {
- __kmp_release_ticket_lock(lck, KMP_GTID_DNE);
- }
- static inline void __kmp_init_bootstrap_lock(kmp_bootstrap_lock_t *lck) {
- __kmp_init_ticket_lock(lck);
- }
- static inline void __kmp_destroy_bootstrap_lock(kmp_bootstrap_lock_t *lck) {
- __kmp_destroy_ticket_lock(lck);
- }
- // Internal RTL locks.
- //
- // Internal RTL locks are also implemented as ticket locks, for now.
- //
- // FIXME - We should go through and figure out which lock kind works best for
- // each internal lock, and use the type declaration and function calls for
- // that explicit lock kind (and get rid of this section).
- typedef kmp_ticket_lock_t kmp_lock_t;
- #define KMP_LOCK_INIT(lock) kmp_lock_t lock = KMP_TICKET_LOCK_INITIALIZER(lock)
- static inline int __kmp_acquire_lock(kmp_lock_t *lck, kmp_int32 gtid) {
- return __kmp_acquire_ticket_lock(lck, gtid);
- }
- static inline int __kmp_test_lock(kmp_lock_t *lck, kmp_int32 gtid) {
- return __kmp_test_ticket_lock(lck, gtid);
- }
- static inline void __kmp_release_lock(kmp_lock_t *lck, kmp_int32 gtid) {
- __kmp_release_ticket_lock(lck, gtid);
- }
- static inline void __kmp_init_lock(kmp_lock_t *lck) {
- __kmp_init_ticket_lock(lck);
- }
- static inline void __kmp_destroy_lock(kmp_lock_t *lck) {
- __kmp_destroy_ticket_lock(lck);
- }
- // User locks.
- //
- // Do not allocate objects of type union kmp_user_lock!!! This will waste space
- // unless __kmp_user_lock_kind == lk_drdpa. Instead, check the value of
- // __kmp_user_lock_kind and allocate objects of the type of the appropriate
- // union member, and cast their addresses to kmp_user_lock_p.
- enum kmp_lock_kind {
- lk_default = 0,
- lk_tas,
- #if KMP_USE_FUTEX
- lk_futex,
- #endif
- #if KMP_USE_DYNAMIC_LOCK && KMP_USE_TSX
- lk_hle,
- lk_rtm_queuing,
- lk_rtm_spin,
- #endif
- lk_ticket,
- lk_queuing,
- lk_drdpa,
- #if KMP_USE_ADAPTIVE_LOCKS
- lk_adaptive
- #endif // KMP_USE_ADAPTIVE_LOCKS
- };
- typedef enum kmp_lock_kind kmp_lock_kind_t;
- extern kmp_lock_kind_t __kmp_user_lock_kind;
- union kmp_user_lock {
- kmp_tas_lock_t tas;
- #if KMP_USE_FUTEX
- kmp_futex_lock_t futex;
- #endif
- kmp_ticket_lock_t ticket;
- kmp_queuing_lock_t queuing;
- kmp_drdpa_lock_t drdpa;
- #if KMP_USE_ADAPTIVE_LOCKS
- kmp_adaptive_lock_t adaptive;
- #endif // KMP_USE_ADAPTIVE_LOCKS
- kmp_lock_pool_t pool;
- };
- typedef union kmp_user_lock *kmp_user_lock_p;
- #if !KMP_USE_DYNAMIC_LOCK
- extern size_t __kmp_base_user_lock_size;
- extern size_t __kmp_user_lock_size;
- extern kmp_int32 (*__kmp_get_user_lock_owner_)(kmp_user_lock_p lck);
- static inline kmp_int32 __kmp_get_user_lock_owner(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_get_user_lock_owner_ != NULL);
- return (*__kmp_get_user_lock_owner_)(lck);
- }
- extern int (*__kmp_acquire_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- #if KMP_OS_LINUX && \
- (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64)
- #define __kmp_acquire_user_lock_with_checks(lck, gtid) \
- if (__kmp_user_lock_kind == lk_tas) { \
- if (__kmp_env_consistency_check) { \
- char const *const func = "omp_set_lock"; \
- if ((sizeof(kmp_tas_lock_t) <= OMP_LOCK_T_SIZE) && \
- lck->tas.lk.depth_locked != -1) { \
- KMP_FATAL(LockNestableUsedAsSimple, func); \
- } \
- if ((gtid >= 0) && (lck->tas.lk.poll - 1 == gtid)) { \
- KMP_FATAL(LockIsAlreadyOwned, func); \
- } \
- } \
- if (lck->tas.lk.poll != 0 || \
- !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)) { \
- kmp_uint32 spins; \
- kmp_uint64 time; \
- KMP_FSYNC_PREPARE(lck); \
- KMP_INIT_YIELD(spins); \
- KMP_INIT_BACKOFF(time); \
- do { \
- KMP_YIELD_OVERSUB_ELSE_SPIN(spins, time); \
- } while ( \
- lck->tas.lk.poll != 0 || \
- !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); \
- } \
- KMP_FSYNC_ACQUIRED(lck); \
- } else { \
- KMP_DEBUG_ASSERT(__kmp_acquire_user_lock_with_checks_ != NULL); \
- (*__kmp_acquire_user_lock_with_checks_)(lck, gtid); \
- }
- #else
- static inline int __kmp_acquire_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- KMP_DEBUG_ASSERT(__kmp_acquire_user_lock_with_checks_ != NULL);
- return (*__kmp_acquire_user_lock_with_checks_)(lck, gtid);
- }
- #endif
- extern int (*__kmp_test_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- #if KMP_OS_LINUX && \
- (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM || KMP_ARCH_AARCH64)
- #include "kmp_i18n.h" /* AC: KMP_FATAL definition */
- extern int __kmp_env_consistency_check; /* AC: copy from kmp.h here */
- static inline int __kmp_test_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- if (__kmp_user_lock_kind == lk_tas) {
- if (__kmp_env_consistency_check) {
- char const *const func = "omp_test_lock";
- if ((sizeof(kmp_tas_lock_t) <= OMP_LOCK_T_SIZE) &&
- lck->tas.lk.depth_locked != -1) {
- KMP_FATAL(LockNestableUsedAsSimple, func);
- }
- }
- return ((lck->tas.lk.poll == 0) &&
- __kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1));
- } else {
- KMP_DEBUG_ASSERT(__kmp_test_user_lock_with_checks_ != NULL);
- return (*__kmp_test_user_lock_with_checks_)(lck, gtid);
- }
- }
- #else
- static inline int __kmp_test_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- KMP_DEBUG_ASSERT(__kmp_test_user_lock_with_checks_ != NULL);
- return (*__kmp_test_user_lock_with_checks_)(lck, gtid);
- }
- #endif
- extern int (*__kmp_release_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- static inline void __kmp_release_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- KMP_DEBUG_ASSERT(__kmp_release_user_lock_with_checks_ != NULL);
- (*__kmp_release_user_lock_with_checks_)(lck, gtid);
- }
- extern void (*__kmp_init_user_lock_with_checks_)(kmp_user_lock_p lck);
- static inline void __kmp_init_user_lock_with_checks(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_init_user_lock_with_checks_ != NULL);
- (*__kmp_init_user_lock_with_checks_)(lck);
- }
- // We need a non-checking version of destroy lock for when the RTL is
- // doing the cleanup as it can't always tell if the lock is nested or not.
- extern void (*__kmp_destroy_user_lock_)(kmp_user_lock_p lck);
- static inline void __kmp_destroy_user_lock(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_destroy_user_lock_ != NULL);
- (*__kmp_destroy_user_lock_)(lck);
- }
- extern void (*__kmp_destroy_user_lock_with_checks_)(kmp_user_lock_p lck);
- static inline void __kmp_destroy_user_lock_with_checks(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_destroy_user_lock_with_checks_ != NULL);
- (*__kmp_destroy_user_lock_with_checks_)(lck);
- }
- extern int (*__kmp_acquire_nested_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- #if KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64)
- #define __kmp_acquire_nested_user_lock_with_checks(lck, gtid, depth) \
- if (__kmp_user_lock_kind == lk_tas) { \
- if (__kmp_env_consistency_check) { \
- char const *const func = "omp_set_nest_lock"; \
- if ((sizeof(kmp_tas_lock_t) <= OMP_NEST_LOCK_T_SIZE) && \
- lck->tas.lk.depth_locked == -1) { \
- KMP_FATAL(LockSimpleUsedAsNestable, func); \
- } \
- } \
- if (lck->tas.lk.poll - 1 == gtid) { \
- lck->tas.lk.depth_locked += 1; \
- *depth = KMP_LOCK_ACQUIRED_NEXT; \
- } else { \
- if ((lck->tas.lk.poll != 0) || \
- !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)) { \
- kmp_uint32 spins; \
- kmp_uint64 time; \
- KMP_FSYNC_PREPARE(lck); \
- KMP_INIT_YIELD(spins); \
- KMP_INIT_BACKOFF(time); \
- do { \
- KMP_YIELD_OVERSUB_ELSE_SPIN(spins, time); \
- } while ( \
- (lck->tas.lk.poll != 0) || \
- !__kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1)); \
- } \
- lck->tas.lk.depth_locked = 1; \
- *depth = KMP_LOCK_ACQUIRED_FIRST; \
- } \
- KMP_FSYNC_ACQUIRED(lck); \
- } else { \
- KMP_DEBUG_ASSERT(__kmp_acquire_nested_user_lock_with_checks_ != NULL); \
- *depth = (*__kmp_acquire_nested_user_lock_with_checks_)(lck, gtid); \
- }
- #else
- static inline void
- __kmp_acquire_nested_user_lock_with_checks(kmp_user_lock_p lck, kmp_int32 gtid,
- int *depth) {
- KMP_DEBUG_ASSERT(__kmp_acquire_nested_user_lock_with_checks_ != NULL);
- *depth = (*__kmp_acquire_nested_user_lock_with_checks_)(lck, gtid);
- }
- #endif
- extern int (*__kmp_test_nested_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- #if KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64)
- static inline int __kmp_test_nested_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- if (__kmp_user_lock_kind == lk_tas) {
- int retval;
- if (__kmp_env_consistency_check) {
- char const *const func = "omp_test_nest_lock";
- if ((sizeof(kmp_tas_lock_t) <= OMP_NEST_LOCK_T_SIZE) &&
- lck->tas.lk.depth_locked == -1) {
- KMP_FATAL(LockSimpleUsedAsNestable, func);
- }
- }
- KMP_DEBUG_ASSERT(gtid >= 0);
- if (lck->tas.lk.poll - 1 ==
- gtid) { /* __kmp_get_tas_lock_owner( lck ) == gtid */
- return ++lck->tas.lk.depth_locked; /* same owner, depth increased */
- }
- retval = ((lck->tas.lk.poll == 0) &&
- __kmp_atomic_compare_store_acq(&lck->tas.lk.poll, 0, gtid + 1));
- if (retval) {
- KMP_MB();
- lck->tas.lk.depth_locked = 1;
- }
- return retval;
- } else {
- KMP_DEBUG_ASSERT(__kmp_test_nested_user_lock_with_checks_ != NULL);
- return (*__kmp_test_nested_user_lock_with_checks_)(lck, gtid);
- }
- }
- #else
- static inline int __kmp_test_nested_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- KMP_DEBUG_ASSERT(__kmp_test_nested_user_lock_with_checks_ != NULL);
- return (*__kmp_test_nested_user_lock_with_checks_)(lck, gtid);
- }
- #endif
- extern int (*__kmp_release_nested_user_lock_with_checks_)(kmp_user_lock_p lck,
- kmp_int32 gtid);
- static inline int
- __kmp_release_nested_user_lock_with_checks(kmp_user_lock_p lck,
- kmp_int32 gtid) {
- KMP_DEBUG_ASSERT(__kmp_release_nested_user_lock_with_checks_ != NULL);
- return (*__kmp_release_nested_user_lock_with_checks_)(lck, gtid);
- }
- extern void (*__kmp_init_nested_user_lock_with_checks_)(kmp_user_lock_p lck);
- static inline void
- __kmp_init_nested_user_lock_with_checks(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_init_nested_user_lock_with_checks_ != NULL);
- (*__kmp_init_nested_user_lock_with_checks_)(lck);
- }
- extern void (*__kmp_destroy_nested_user_lock_with_checks_)(kmp_user_lock_p lck);
- static inline void
- __kmp_destroy_nested_user_lock_with_checks(kmp_user_lock_p lck) {
- KMP_DEBUG_ASSERT(__kmp_destroy_nested_user_lock_with_checks_ != NULL);
- (*__kmp_destroy_nested_user_lock_with_checks_)(lck);
- }
- // user lock functions which do not necessarily exist for all lock kinds.
- //
- // The "set" functions usually have wrapper routines that check for a NULL set
- // function pointer and call it if non-NULL.
- //
- // In some cases, it makes sense to have a "get" wrapper function check for a
- // NULL get function pointer and return NULL / invalid value / error code if
- // the function pointer is NULL.
- //
- // In other cases, the calling code really should differentiate between an
- // unimplemented function and one that is implemented but returning NULL /
- // invalid value. If this is the case, no get function wrapper exists.
- extern int (*__kmp_is_user_lock_initialized_)(kmp_user_lock_p lck);
- // no set function; fields set during local allocation
- extern const ident_t *(*__kmp_get_user_lock_location_)(kmp_user_lock_p lck);
- static inline const ident_t *__kmp_get_user_lock_location(kmp_user_lock_p lck) {
- if (__kmp_get_user_lock_location_ != NULL) {
- return (*__kmp_get_user_lock_location_)(lck);
- } else {
- return NULL;
- }
- }
- extern void (*__kmp_set_user_lock_location_)(kmp_user_lock_p lck,
- const ident_t *loc);
- static inline void __kmp_set_user_lock_location(kmp_user_lock_p lck,
- const ident_t *loc) {
- if (__kmp_set_user_lock_location_ != NULL) {
- (*__kmp_set_user_lock_location_)(lck, loc);
- }
- }
- extern kmp_lock_flags_t (*__kmp_get_user_lock_flags_)(kmp_user_lock_p lck);
- extern void (*__kmp_set_user_lock_flags_)(kmp_user_lock_p lck,
- kmp_lock_flags_t flags);
- static inline void __kmp_set_user_lock_flags(kmp_user_lock_p lck,
- kmp_lock_flags_t flags) {
- if (__kmp_set_user_lock_flags_ != NULL) {
- (*__kmp_set_user_lock_flags_)(lck, flags);
- }
- }
- // The function which sets up all of the vtbl pointers for kmp_user_lock_t.
- extern void __kmp_set_user_lock_vptrs(kmp_lock_kind_t user_lock_kind);
- // Macros for binding user lock functions.
- #define KMP_BIND_USER_LOCK_TEMPLATE(nest, kind, suffix) \
- { \
- __kmp_acquire##nest##user_lock_with_checks_ = (int (*)( \
- kmp_user_lock_p, kmp_int32))__kmp_acquire##nest##kind##_##suffix; \
- __kmp_release##nest##user_lock_with_checks_ = (int (*)( \
- kmp_user_lock_p, kmp_int32))__kmp_release##nest##kind##_##suffix; \
- __kmp_test##nest##user_lock_with_checks_ = (int (*)( \
- kmp_user_lock_p, kmp_int32))__kmp_test##nest##kind##_##suffix; \
- __kmp_init##nest##user_lock_with_checks_ = \
- (void (*)(kmp_user_lock_p))__kmp_init##nest##kind##_##suffix; \
- __kmp_destroy##nest##user_lock_with_checks_ = \
- (void (*)(kmp_user_lock_p))__kmp_destroy##nest##kind##_##suffix; \
- }
- #define KMP_BIND_USER_LOCK(kind) KMP_BIND_USER_LOCK_TEMPLATE(_, kind, lock)
- #define KMP_BIND_USER_LOCK_WITH_CHECKS(kind) \
- KMP_BIND_USER_LOCK_TEMPLATE(_, kind, lock_with_checks)
- #define KMP_BIND_NESTED_USER_LOCK(kind) \
- KMP_BIND_USER_LOCK_TEMPLATE(_nested_, kind, lock)
- #define KMP_BIND_NESTED_USER_LOCK_WITH_CHECKS(kind) \
- KMP_BIND_USER_LOCK_TEMPLATE(_nested_, kind, lock_with_checks)
- // User lock table & lock allocation
- /* On 64-bit Linux* OS (and OS X*) GNU compiler allocates only 4 bytems memory
- for lock variable, which is not enough to store a pointer, so we have to use
- lock indexes instead of pointers and maintain lock table to map indexes to
- pointers.
- Note: The first element of the table is not a pointer to lock! It is a
- pointer to previously allocated table (or NULL if it is the first table).
- Usage:
- if ( OMP_LOCK_T_SIZE < sizeof( <lock> ) ) { // or OMP_NEST_LOCK_T_SIZE
- Lock table is fully utilized. User locks are indexes, so table is used on
- user lock operation.
- Note: it may be the case (lin_32) that we don't need to use a lock
- table for regular locks, but do need the table for nested locks.
- }
- else {
- Lock table initialized but not actually used.
- }
- */
- struct kmp_lock_table {
- kmp_lock_index_t used; // Number of used elements
- kmp_lock_index_t allocated; // Number of allocated elements
- kmp_user_lock_p *table; // Lock table.
- };
- typedef struct kmp_lock_table kmp_lock_table_t;
- extern kmp_lock_table_t __kmp_user_lock_table;
- extern kmp_user_lock_p __kmp_lock_pool;
- struct kmp_block_of_locks {
- struct kmp_block_of_locks *next_block;
- void *locks;
- };
- typedef struct kmp_block_of_locks kmp_block_of_locks_t;
- extern kmp_block_of_locks_t *__kmp_lock_blocks;
- extern int __kmp_num_locks_in_block;
- extern kmp_user_lock_p __kmp_user_lock_allocate(void **user_lock,
- kmp_int32 gtid,
- kmp_lock_flags_t flags);
- extern void __kmp_user_lock_free(void **user_lock, kmp_int32 gtid,
- kmp_user_lock_p lck);
- extern kmp_user_lock_p __kmp_lookup_user_lock(void **user_lock,
- char const *func);
- extern void __kmp_cleanup_user_locks();
- #define KMP_CHECK_USER_LOCK_INIT() \
- { \
- if (!TCR_4(__kmp_init_user_locks)) { \
- __kmp_acquire_bootstrap_lock(&__kmp_initz_lock); \
- if (!TCR_4(__kmp_init_user_locks)) { \
- TCW_4(__kmp_init_user_locks, TRUE); \
- } \
- __kmp_release_bootstrap_lock(&__kmp_initz_lock); \
- } \
- }
- #endif // KMP_USE_DYNAMIC_LOCK
- #undef KMP_PAD
- #undef KMP_GTID_DNE
- #if KMP_USE_DYNAMIC_LOCK
- // KMP_USE_DYNAMIC_LOCK enables dynamic dispatch of lock functions without
- // breaking the current compatibility. Essential functionality of this new code
- // is dynamic dispatch, but it also implements (or enables implementation of)
- // hinted user lock and critical section which will be part of OMP 4.5 soon.
- //
- // Lock type can be decided at creation time (i.e., lock initialization), and
- // subsequent lock function call on the created lock object requires type
- // extraction and call through jump table using the extracted type. This type
- // information is stored in two different ways depending on the size of the lock
- // object, and we differentiate lock types by this size requirement - direct and
- // indirect locks.
- //
- // Direct locks:
- // A direct lock object fits into the space created by the compiler for an
- // omp_lock_t object, and TAS/Futex lock falls into this category. We use low
- // one byte of the lock object as the storage for the lock type, and appropriate
- // bit operation is required to access the data meaningful to the lock
- // algorithms. Also, to differentiate direct lock from indirect lock, 1 is
- // written to LSB of the lock object. The newly introduced "hle" lock is also a
- // direct lock.
- //
- // Indirect locks:
- // An indirect lock object requires more space than the compiler-generated
- // space, and it should be allocated from heap. Depending on the size of the
- // compiler-generated space for the lock (i.e., size of omp_lock_t), this
- // omp_lock_t object stores either the address of the heap-allocated indirect
- // lock (void * fits in the object) or an index to the indirect lock table entry
- // that holds the address. Ticket/Queuing/DRDPA/Adaptive lock falls into this
- // category, and the newly introduced "rtm" lock is also an indirect lock which
- // was implemented on top of the Queuing lock. When the omp_lock_t object holds
- // an index (not lock address), 0 is written to LSB to differentiate the lock
- // from a direct lock, and the remaining part is the actual index to the
- // indirect lock table.
- #include <stdint.h> // for uintptr_t
- // Shortcuts
- #define KMP_USE_INLINED_TAS \
- (KMP_OS_LINUX && (KMP_ARCH_X86 || KMP_ARCH_X86_64 || KMP_ARCH_ARM)) && 1
- #define KMP_USE_INLINED_FUTEX KMP_USE_FUTEX && 0
- // List of lock definitions; all nested locks are indirect locks.
- // hle lock is xchg lock prefixed with XACQUIRE/XRELEASE.
- // All nested locks are indirect lock types.
- #if KMP_USE_TSX
- #if KMP_USE_FUTEX
- #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(futex, a) m(hle, a) m(rtm_spin, a)
- #define KMP_FOREACH_I_LOCK(m, a) \
- m(ticket, a) m(queuing, a) m(adaptive, a) m(drdpa, a) m(rtm_queuing, a) \
- m(nested_tas, a) m(nested_futex, a) m(nested_ticket, a) \
- m(nested_queuing, a) m(nested_drdpa, a)
- #else
- #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(hle, a) m(rtm_spin, a)
- #define KMP_FOREACH_I_LOCK(m, a) \
- m(ticket, a) m(queuing, a) m(adaptive, a) m(drdpa, a) m(rtm_queuing, a) \
- m(nested_tas, a) m(nested_ticket, a) m(nested_queuing, a) \
- m(nested_drdpa, a)
- #endif // KMP_USE_FUTEX
- #define KMP_LAST_D_LOCK lockseq_rtm_spin
- #else
- #if KMP_USE_FUTEX
- #define KMP_FOREACH_D_LOCK(m, a) m(tas, a) m(futex, a)
- #define KMP_FOREACH_I_LOCK(m, a) \
- m(ticket, a) m(queuing, a) m(drdpa, a) m(nested_tas, a) m(nested_futex, a) \
- m(nested_ticket, a) m(nested_queuing, a) m(nested_drdpa, a)
- #define KMP_LAST_D_LOCK lockseq_futex
- #else
- #define KMP_FOREACH_D_LOCK(m, a) m(tas, a)
- #define KMP_FOREACH_I_LOCK(m, a) \
- m(ticket, a) m(queuing, a) m(drdpa, a) m(nested_tas, a) m(nested_ticket, a) \
- m(nested_queuing, a) m(nested_drdpa, a)
- #define KMP_LAST_D_LOCK lockseq_tas
- #endif // KMP_USE_FUTEX
- #endif // KMP_USE_TSX
- // Information used in dynamic dispatch
- #define KMP_LOCK_SHIFT \
- 8 // number of low bits to be used as tag for direct locks
- #define KMP_FIRST_D_LOCK lockseq_tas
- #define KMP_FIRST_I_LOCK lockseq_ticket
- #define KMP_LAST_I_LOCK lockseq_nested_drdpa
- #define KMP_NUM_I_LOCKS \
- (locktag_nested_drdpa + 1) // number of indirect lock types
- // Base type for dynamic locks.
- typedef kmp_uint32 kmp_dyna_lock_t;
- // Lock sequence that enumerates all lock kinds. Always make this enumeration
- // consistent with kmp_lockseq_t in the include directory.
- typedef enum {
- lockseq_indirect = 0,
- #define expand_seq(l, a) lockseq_##l,
- KMP_FOREACH_D_LOCK(expand_seq, 0) KMP_FOREACH_I_LOCK(expand_seq, 0)
- #undef expand_seq
- } kmp_dyna_lockseq_t;
- // Enumerates indirect lock tags.
- typedef enum {
- #define expand_tag(l, a) locktag_##l,
- KMP_FOREACH_I_LOCK(expand_tag, 0)
- #undef expand_tag
- } kmp_indirect_locktag_t;
- // Utility macros that extract information from lock sequences.
- #define KMP_IS_D_LOCK(seq) \
- ((seq) >= KMP_FIRST_D_LOCK && (seq) <= KMP_LAST_D_LOCK)
- #define KMP_IS_I_LOCK(seq) \
- ((seq) >= KMP_FIRST_I_LOCK && (seq) <= KMP_LAST_I_LOCK)
- #define KMP_GET_I_TAG(seq) (kmp_indirect_locktag_t)((seq)-KMP_FIRST_I_LOCK)
- #define KMP_GET_D_TAG(seq) ((seq) << 1 | 1)
- // Enumerates direct lock tags starting from indirect tag.
- typedef enum {
- #define expand_tag(l, a) locktag_##l = KMP_GET_D_TAG(lockseq_##l),
- KMP_FOREACH_D_LOCK(expand_tag, 0)
- #undef expand_tag
- } kmp_direct_locktag_t;
- // Indirect lock type
- typedef struct {
- kmp_user_lock_p lock;
- kmp_indirect_locktag_t type;
- } kmp_indirect_lock_t;
- // Function tables for direct locks. Set/unset/test differentiate functions
- // with/without consistency checking.
- extern void (*__kmp_direct_init[])(kmp_dyna_lock_t *, kmp_dyna_lockseq_t);
- extern void (**__kmp_direct_destroy)(kmp_dyna_lock_t *);
- extern int (**__kmp_direct_set)(kmp_dyna_lock_t *, kmp_int32);
- extern int (**__kmp_direct_unset)(kmp_dyna_lock_t *, kmp_int32);
- extern int (**__kmp_direct_test)(kmp_dyna_lock_t *, kmp_int32);
- // Function tables for indirect locks. Set/unset/test differentiate functions
- // with/without consistency checking.
- extern void (*__kmp_indirect_init[])(kmp_user_lock_p);
- extern void (**__kmp_indirect_destroy)(kmp_user_lock_p);
- extern int (**__kmp_indirect_set)(kmp_user_lock_p, kmp_int32);
- extern int (**__kmp_indirect_unset)(kmp_user_lock_p, kmp_int32);
- extern int (**__kmp_indirect_test)(kmp_user_lock_p, kmp_int32);
- // Extracts direct lock tag from a user lock pointer
- #define KMP_EXTRACT_D_TAG(l) \
- (*((kmp_dyna_lock_t *)(l)) & ((1 << KMP_LOCK_SHIFT) - 1) & \
- -(*((kmp_dyna_lock_t *)(l)) & 1))
- // Extracts indirect lock index from a user lock pointer
- #define KMP_EXTRACT_I_INDEX(l) (*(kmp_lock_index_t *)(l) >> 1)
- // Returns function pointer to the direct lock function with l (kmp_dyna_lock_t
- // *) and op (operation type).
- #define KMP_D_LOCK_FUNC(l, op) __kmp_direct_##op[KMP_EXTRACT_D_TAG(l)]
- // Returns function pointer to the indirect lock function with l
- // (kmp_indirect_lock_t *) and op (operation type).
- #define KMP_I_LOCK_FUNC(l, op) \
- __kmp_indirect_##op[((kmp_indirect_lock_t *)(l))->type]
- // Initializes a direct lock with the given lock pointer and lock sequence.
- #define KMP_INIT_D_LOCK(l, seq) \
- __kmp_direct_init[KMP_GET_D_TAG(seq)]((kmp_dyna_lock_t *)l, seq)
- // Initializes an indirect lock with the given lock pointer and lock sequence.
- #define KMP_INIT_I_LOCK(l, seq) \
- __kmp_direct_init[0]((kmp_dyna_lock_t *)(l), seq)
- // Returns "free" lock value for the given lock type.
- #define KMP_LOCK_FREE(type) (locktag_##type)
- // Returns "busy" lock value for the given lock teyp.
- #define KMP_LOCK_BUSY(v, type) ((v) << KMP_LOCK_SHIFT | locktag_##type)
- // Returns lock value after removing (shifting) lock tag.
- #define KMP_LOCK_STRIP(v) ((v) >> KMP_LOCK_SHIFT)
- // Initializes global states and data structures for managing dynamic user
- // locks.
- extern void __kmp_init_dynamic_user_locks();
- // Allocates and returns an indirect lock with the given indirect lock tag.
- extern kmp_indirect_lock_t *
- __kmp_allocate_indirect_lock(void **, kmp_int32, kmp_indirect_locktag_t);
- // Cleans up global states and data structures for managing dynamic user locks.
- extern void __kmp_cleanup_indirect_user_locks();
- // Default user lock sequence when not using hinted locks.
- extern kmp_dyna_lockseq_t __kmp_user_lock_seq;
- // Jump table for "set lock location", available only for indirect locks.
- extern void (*__kmp_indirect_set_location[KMP_NUM_I_LOCKS])(kmp_user_lock_p,
- const ident_t *);
- #define KMP_SET_I_LOCK_LOCATION(lck, loc) \
- { \
- if (__kmp_indirect_set_location[(lck)->type] != NULL) \
- __kmp_indirect_set_location[(lck)->type]((lck)->lock, loc); \
- }
- // Jump table for "set lock flags", available only for indirect locks.
- extern void (*__kmp_indirect_set_flags[KMP_NUM_I_LOCKS])(kmp_user_lock_p,
- kmp_lock_flags_t);
- #define KMP_SET_I_LOCK_FLAGS(lck, flag) \
- { \
- if (__kmp_indirect_set_flags[(lck)->type] != NULL) \
- __kmp_indirect_set_flags[(lck)->type]((lck)->lock, flag); \
- }
- // Jump table for "get lock location", available only for indirect locks.
- extern const ident_t *(*__kmp_indirect_get_location[KMP_NUM_I_LOCKS])(
- kmp_user_lock_p);
- #define KMP_GET_I_LOCK_LOCATION(lck) \
- (__kmp_indirect_get_location[(lck)->type] != NULL \
- ? __kmp_indirect_get_location[(lck)->type]((lck)->lock) \
- : NULL)
- // Jump table for "get lock flags", available only for indirect locks.
- extern kmp_lock_flags_t (*__kmp_indirect_get_flags[KMP_NUM_I_LOCKS])(
- kmp_user_lock_p);
- #define KMP_GET_I_LOCK_FLAGS(lck) \
- (__kmp_indirect_get_flags[(lck)->type] != NULL \
- ? __kmp_indirect_get_flags[(lck)->type]((lck)->lock) \
- : NULL)
- // number of kmp_indirect_lock_t objects to be allocated together
- #define KMP_I_LOCK_CHUNK 1024
- // Keep at a power of 2 since it is used in multiplication & division
- KMP_BUILD_ASSERT(KMP_I_LOCK_CHUNK % 2 == 0);
- // number of row entries in the initial lock table
- #define KMP_I_LOCK_TABLE_INIT_NROW_PTRS 8
- // Lock table for indirect locks.
- typedef struct kmp_indirect_lock_table {
- kmp_indirect_lock_t **table; // blocks of indirect locks allocated
- kmp_uint32 nrow_ptrs; // number *table pointer entries in table
- kmp_lock_index_t next; // index to the next lock to be allocated
- struct kmp_indirect_lock_table *next_table;
- } kmp_indirect_lock_table_t;
- extern kmp_indirect_lock_table_t __kmp_i_lock_table;
- // Returns the indirect lock associated with the given index.
- // Returns nullptr if no lock at given index
- static inline kmp_indirect_lock_t *__kmp_get_i_lock(kmp_lock_index_t idx) {
- kmp_indirect_lock_table_t *lock_table = &__kmp_i_lock_table;
- while (lock_table) {
- kmp_lock_index_t max_locks = lock_table->nrow_ptrs * KMP_I_LOCK_CHUNK;
- if (idx < max_locks) {
- kmp_lock_index_t row = idx / KMP_I_LOCK_CHUNK;
- kmp_lock_index_t col = idx % KMP_I_LOCK_CHUNK;
- if (!lock_table->table[row] || idx >= lock_table->next)
- break;
- return &lock_table->table[row][col];
- }
- idx -= max_locks;
- lock_table = lock_table->next_table;
- }
- return nullptr;
- }
- // Number of locks in a lock block, which is fixed to "1" now.
- // TODO: No lock block implementation now. If we do support, we need to manage
- // lock block data structure for each indirect lock type.
- extern int __kmp_num_locks_in_block;
- // Fast lock table lookup without consistency checking
- #define KMP_LOOKUP_I_LOCK(l) \
- ((OMP_LOCK_T_SIZE < sizeof(void *)) \
- ? __kmp_get_i_lock(KMP_EXTRACT_I_INDEX(l)) \
- : *((kmp_indirect_lock_t **)(l)))
- // Used once in kmp_error.cpp
- extern kmp_int32 __kmp_get_user_lock_owner(kmp_user_lock_p, kmp_uint32);
- #else // KMP_USE_DYNAMIC_LOCK
- #define KMP_LOCK_BUSY(v, type) (v)
- #define KMP_LOCK_FREE(type) 0
- #define KMP_LOCK_STRIP(v) (v)
- #endif // KMP_USE_DYNAMIC_LOCK
- // data structure for using backoff within spin locks.
- typedef struct {
- kmp_uint32 step; // current step
- kmp_uint32 max_backoff; // upper bound of outer delay loop
- kmp_uint32 min_tick; // size of inner delay loop in ticks (machine-dependent)
- } kmp_backoff_t;
- // Runtime's default backoff parameters
- extern kmp_backoff_t __kmp_spin_backoff_params;
- // Backoff function
- extern void __kmp_spin_backoff(kmp_backoff_t *);
- #ifdef __cplusplus
- } // extern "C"
- #endif // __cplusplus
- #endif /* KMP_LOCK_H */
|