* kmp_lock.h -- lock header file
*/
#ifndef KMP_LOCK_H
#define KMP_LOCK_H
#include <limits.h>
#include <stddef.h>
#include "kmp_debug.h"
#include "kmp_os.h"
#ifdef __cplusplus
#include <atomic>
extern "C" {
#endif
#define KMP_PAD(type, sz) \
(sizeof(type) + (sz - ((sizeof(type) - 1) % (sz)) - 1))
#define KMP_GTID_DNE (-2)
struct ident;
typedef struct ident ident_t;
#if (KMP_OS_LINUX || KMP_OS_AIX) && 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
#define OMP_CRITICAL_SIZE sizeof(void *)
#define INTEL_CRITICAL_SIZE 32
typedef kmp_uint32 kmp_lock_flags_t;
#define kmp_lf_critical_section 1
typedef kmp_uint32 kmp_lock_index_t;
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);
struct kmp_base_tas_lock {
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && \
__LP64__
kmp_int32 depth_locked;
std::atomic<kmp_int32> poll;
#else
std::atomic<kmp_int32> poll;
kmp_int32 depth_locked;
#endif
};
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;
double lk_align;
};
typedef union kmp_tas_lock kmp_tas_lock_t;
#define KMP_TAS_LOCK_INITIALIZER(lock) \
{ \
{ 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
struct kmp_base_futex_lock {
volatile kmp_int32 poll;
kmp_int32 depth_locked;
};
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;
double lk_align;
};
typedef union kmp_futex_lock kmp_futex_lock_t;
#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
#ifdef __cplusplus
#ifdef _MSC_VER
struct kmp_base_ticket_lock {
std::atomic_bool initialized;
volatile union kmp_ticket_lock *self;
ident_t const *location;
std::atomic_uint
next_ticket;
std::atomic_uint now_serving;
std::atomic_int owner_id;
std::atomic_int depth_locked;
kmp_lock_flags_t flags;
};
#else
struct kmp_base_ticket_lock {
std::atomic<bool> initialized;
volatile union kmp_ticket_lock *self;
ident_t const *location;
std::atomic<unsigned>
next_ticket;
std::atomic<unsigned>
now_serving;
std::atomic<int> owner_id;
std::atomic<int> depth_locked;
kmp_lock_flags_t flags;
};
#endif
#else
struct kmp_base_ticket_lock;
#endif
typedef struct kmp_base_ticket_lock kmp_base_ticket_lock_t;
union KMP_ALIGN_CACHE kmp_ticket_lock {
kmp_base_ticket_lock_t
lk;
kmp_lock_pool_t pool;
double lk_align;
char lk_pad[KMP_PAD(kmp_base_ticket_lock_t, CACHE_LINE)];
};
typedef union kmp_ticket_lock kmp_ticket_lock_t;
#define KMP_TICKET_LOCK_INITIALIZER(lock) \
{ \
{ true, &(lock), NULL, 0U, 0U, 0, -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);
#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 {
kmp_adaptive_lock_info_t *next;
kmp_adaptive_lock_info_t *prev;
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
struct kmp_adaptive_lock_info {
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;
kmp_uint32 max_badness;
kmp_uint32 max_soft_retries;
#if KMP_DEBUG_ADAPTIVE_LOCKS
kmp_adaptive_lock_statistics_t volatile stats;
#endif
};
#endif
struct kmp_base_queuing_lock {
volatile union kmp_queuing_lock
*initialized;
ident_t const *location;
KMP_ALIGN(8)
volatile kmp_int32
tail_id;
volatile kmp_int32
head_id;
volatile kmp_uint32
next_ticket;
volatile kmp_uint32
now_serving;
volatile kmp_int32 owner_id;
kmp_int32 depth_locked;
kmp_lock_flags_t flags;
};
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;
kmp_lock_pool_t pool;
double lk_align;
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
struct kmp_base_adaptive_lock {
kmp_base_queuing_lock qlk;
KMP_ALIGN(CACHE_LINE)
kmp_adaptive_lock_info_t
adaptive;
};
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
struct kmp_base_drdpa_lock {
KMP_ALIGN_CACHE
volatile union kmp_drdpa_lock
*initialized;
ident_t const *location;
std::atomic<std::atomic<kmp_uint64> *> polls;
std::atomic<kmp_uint64> mask;
kmp_uint64 cleanup_ticket;
std::atomic<kmp_uint64> *old_polls;
kmp_uint32 num_polls;
KMP_ALIGN_CACHE
std::atomic<kmp_uint64> next_ticket;
KMP_ALIGN_CACHE
kmp_uint64 now_serving;
volatile kmp_uint32 owner_id;
kmp_int32 depth_locked;
kmp_lock_flags_t flags;
};
typedef struct kmp_base_drdpa_lock kmp_base_drdpa_lock_t;
union KMP_ALIGN_CACHE kmp_drdpa_lock {
kmp_base_drdpa_lock_t
lk;
kmp_lock_pool_t pool;
double lk_align;
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);
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);
}
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);
}
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
};
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_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"
extern int __kmp_env_consistency_check;
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);
}
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) {
return ++lck->tas.lk.depth_locked;
}
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);
}
extern int (*__kmp_is_user_lock_initialized_)(kmp_user_lock_p lck);
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);
}
}
extern void __kmp_set_user_lock_vptrs(kmp_lock_kind_t user_lock_kind);
#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)
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;
kmp_lock_index_t allocated;
kmp_user_lock_p *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
#undef KMP_PAD
#undef KMP_GTID_DNE
#if KMP_USE_DYNAMIC_LOCK
#include <stdint.h>
#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
#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
#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
#endif
#define KMP_LOCK_SHIFT \
8
#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)
typedef kmp_uint32 kmp_dyna_lock_t;
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;
typedef enum {
#define expand_tag(l, a) locktag_##l,
KMP_FOREACH_I_LOCK(expand_tag, 0)
#undef expand_tag
} kmp_indirect_locktag_t;
#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)
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;
typedef struct {
kmp_user_lock_p lock;
kmp_indirect_locktag_t type;
} kmp_indirect_lock_t;
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);
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);
#define KMP_EXTRACT_D_TAG(l) \
((kmp_dyna_lock_t)((kmp_base_tas_lock_t *)(l))->poll & \
((1 << KMP_LOCK_SHIFT) - 1) & \
-((kmp_dyna_lock_t)((kmp_tas_lock_t *)(l))->lk.poll & 1))
#define KMP_EXTRACT_I_INDEX(l) \
((kmp_lock_index_t)((kmp_base_tas_lock_t *)(l))->poll >> 1)
#define KMP_D_LOCK_FUNC(l, op) __kmp_direct_##op[KMP_EXTRACT_D_TAG(l)]
#define KMP_I_LOCK_FUNC(l, op) \
__kmp_indirect_##op[((kmp_indirect_lock_t *)(l))->type]
#define KMP_INIT_D_LOCK(l, seq) \
__kmp_direct_init[KMP_GET_D_TAG(seq)]((kmp_dyna_lock_t *)l, seq)
#define KMP_INIT_I_LOCK(l, seq) \
__kmp_direct_init[0]((kmp_dyna_lock_t *)(l), seq)
#define KMP_LOCK_FREE(type) (locktag_##type)
#define KMP_LOCK_BUSY(v, type) ((v) << KMP_LOCK_SHIFT | locktag_##type)
#define KMP_LOCK_STRIP(v) ((v) >> KMP_LOCK_SHIFT)
extern void __kmp_init_dynamic_user_locks();
extern kmp_indirect_lock_t *
__kmp_allocate_indirect_lock(void **, kmp_int32, kmp_indirect_locktag_t);
extern void __kmp_cleanup_indirect_user_locks();
extern kmp_dyna_lockseq_t __kmp_user_lock_seq;
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); \
}
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); \
}
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)
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)
#define KMP_I_LOCK_CHUNK 1024
KMP_BUILD_ASSERT(KMP_I_LOCK_CHUNK % 2 == 0);
#define KMP_I_LOCK_TABLE_INIT_NROW_PTRS 8
typedef struct kmp_indirect_lock_table {
kmp_indirect_lock_t **table;
kmp_uint32 nrow_ptrs;
kmp_lock_index_t next;
struct kmp_indirect_lock_table *next_table;
} kmp_indirect_lock_table_t;
extern kmp_indirect_lock_table_t __kmp_i_lock_table;
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;
}
extern int __kmp_num_locks_in_block;
#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)))
extern kmp_int32 __kmp_get_user_lock_owner(kmp_user_lock_p, kmp_uint32);
#else
#define KMP_LOCK_BUSY(v, type) (v)
#define KMP_LOCK_FREE(type) 0
#define KMP_LOCK_STRIP(v) (v)
#endif
typedef struct {
kmp_uint32 step;
kmp_uint32 max_backoff;
kmp_uint32 min_tick;
} kmp_backoff_t;
extern kmp_backoff_t __kmp_spin_backoff_params;
extern void __kmp_spin_backoff(kmp_backoff_t *);
#ifdef __cplusplus
}
#endif
#endif