* Copyright (c) 2022 Huawei Technologies Co.,Ltd.
*
* CBB is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cm_spinlock.h
*
*
* IDENTIFICATION
* src/cm_concurrency/cm_spinlock.h
*
* -------------------------------------------------------------------------
*/
#ifndef __CM_SPINLOCK_H_
#define __CM_SPINLOCK_H_
#include "cm_defs.h"
#ifndef WIN32
#include <time.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef volatile uint32 spinlock_t;
typedef volatile uint32 ip_spinlock_t;
#if defined(__arm__) || defined(__aarch64__)
#define GS_INIT_SPIN_LOCK(lock) \
{ \
__atomic_store_n(&lock, 0, __ATOMIC_SEQ_CST); \
}
#else
#define GS_INIT_SPIN_LOCK(lock) \
{ \
(lock) = 0; \
}
#endif
#define GS_SPIN_COUNT 1000
#define SPIN_STAT_INC(stat, item) \
{ \
if ((stat) != NULL) { \
((stat)->item)++; \
} \
}
#define SPIN_STAT_ADD(stat, item, value) \
{ \
if ((stat) != NULL) { \
((stat)->item) += (value); \
} \
}
typedef struct st_spin_statis {
uint64 spins;
uint64 wait_usecs;
uint64 ss_wait_usecs;
uint64 fails;
} spin_statis_t;
typedef struct st_spin_statis_instance {
uint64 wait_times;
uint64 ss_wait_usecs;
} spin_statis_instance_t;
#if defined(__arm__) || defined(__aarch64__) || defined(__loongarch__)
#define fas_cpu_pause() \
{ \
__asm__ volatile("nop"); \
}
#else
#define fas_cpu_pause() \
{ \
__asm__ volatile("pause"); \
}
#endif
void cm_spin_sleep_and_stat(spin_statis_t *stat);
void cm_spin_sleep_and_stat2(uint32 ms);
uint64 cm_total_spin_usecs(void);
void cm_set_spin_sleep_time(uint32 sleep_time_nanosecs);
#ifdef WIN32
static forceinline uint32 cm_spin_set(spinlock_t *ptr, uint32 value)
{
return (uint32)InterlockedExchange(ptr, value);
}
static forceinline void cm_spin_sleep()
{
Sleep(1);
}
static forceinline void cm_spin_sleep_ex(uint32 tick)
{
Sleep(tick);
}
#else
#if defined(__arm__) || defined(__aarch64__)
static forceinline uint32 cm_spin_set(spinlock_t *ptr, uint32 value)
{
uint32 oldvalue = 0;
return !__atomic_compare_exchange_n(ptr, &oldvalue, value, CM_FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
}
static forceinline void cm_spin_unlock(spinlock_t *lock)
{
__atomic_store_n(lock, 0, __ATOMIC_SEQ_CST);
}
#else
static forceinline uint32 cm_spin_set(spinlock_t *ptr, uint32 value)
{
uint32 oldvalue = 0;
return (uint32)__sync_val_compare_and_swap(ptr, oldvalue, value);
}
#endif
static forceinline void cm_spin_sleep(void)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100;
(void)nanosleep(&ts, NULL);
}
static forceinline void cm_spin_sleep_ex(uint32 tick)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = tick;
(void)nanosleep(&ts, NULL);
}
#endif
static forceinline void cm_spin_lock_with_stat(spinlock_t *lock, spin_statis_t *stat,
spin_statis_instance_t *stat_instance)
{
uint32 spin_times = 0;
uint32 sleep_times = 0;
if (SECUREC_UNLIKELY(lock == NULL)) {
return;
}
for (;;) {
#if defined(__arm__) || defined(__aarch64__)
while (__atomic_load_n(lock, __ATOMIC_SEQ_CST) != 0) {
#else
while (*lock != 0) {
#endif
SPIN_STAT_INC(stat, spins);
spin_times++;
if (SECUREC_UNLIKELY(spin_times == GS_SPIN_COUNT)) {
cm_spin_sleep_and_stat(stat);
spin_times = 0;
SPIN_STAT_INC(stat_instance, wait_times);
}
}
if (SECUREC_LIKELY(cm_spin_set(lock, 1) == 0)) {
break;
}
SPIN_STAT_INC(stat, fails);
sleep_times++;
#ifndef WIN32
for (uint32 i = 0; i < sleep_times; i++) {
fas_cpu_pause();
}
#endif
}
}
#define cm_spin_lock(lock, stat) cm_spin_lock_with_stat(lock, stat, NULL)
static forceinline void cm_spin_lock_ex(spinlock_t *lock, spin_statis_t *stat, uint32 spin_count)
{
uint32 spin_times = 0;
uint32 sleep_times = 0;
if (SECUREC_UNLIKELY(lock == NULL)) {
return;
}
for (;;) {
#if defined(__arm__) || defined(__aarch64__)
while (__atomic_load_n(lock, __ATOMIC_SEQ_CST) != 0) {
#else
while (*lock != 0) {
#endif
SPIN_STAT_INC(stat, spins);
spin_times++;
#ifndef WIN32
fas_cpu_pause();
#endif
if (SECUREC_UNLIKELY(spin_times == spin_count)) {
cm_spin_sleep_and_stat(stat);
spin_times = 0;
}
}
if (cm_spin_set(lock, 1) != 0) {
SPIN_STAT_INC(stat, fails);
sleep_times++;
#ifndef WIN32
for (uint32 i = 0; i < sleep_times; i++) {
fas_cpu_pause();
}
#endif
continue;
}
break;
}
}
#if !defined(__arm__) && !defined(__aarch64__)
static forceinline void cm_spin_unlock(spinlock_t *lock)
{
if (SECUREC_UNLIKELY(lock == NULL)) {
return;
}
__atomic_store_n(lock, 0, __ATOMIC_RELEASE);
}
#endif
static forceinline bool32 cm_spin_try_lock(spinlock_t *lock)
{
#if defined(__arm__) || defined(__aarch64__)
if (__atomic_load_n(lock, __ATOMIC_SEQ_CST) != 0) {
#else
if (*lock != 0) {
#endif
return CM_FALSE;
}
return (cm_spin_set(lock, 1) == 0);
}
static forceinline bool32 cm_spin_timed_lock(spinlock_t *lock, uint32 timeout_ticks)
{
uint32 spin_times = 0, wait_ticks = 0;
uint32 sleep_times = 0;
for (;;) {
#if defined(__arm__) || defined(__aarch64__)
while (__atomic_load_n(lock, __ATOMIC_SEQ_CST) != 0) {
#else
while (*lock != 0) {
#endif
if (SECUREC_UNLIKELY(wait_ticks >= timeout_ticks)) {
return CM_FALSE;
}
#ifndef WIN32
fas_cpu_pause();
#endif
spin_times++;
if (SECUREC_UNLIKELY(spin_times == GS_SPIN_COUNT)) {
cm_spin_sleep();
spin_times = 0;
wait_ticks++;
}
}
if (cm_spin_set(lock, 1) != 0) {
sleep_times++;
#ifndef WIN32
for (uint32 i = 0; i < sleep_times; i++) {
fas_cpu_pause();
}
#endif
continue;
}
break;
}
return CM_TRUE;
}
static forceinline void cm_spin_lock_by_sid(uint32 sid, spinlock_t *lock, spin_statis_t *stat)
{
uint32 spin_times = 0;
uint32 sleep_times = 0;
if (SECUREC_UNLIKELY(lock == NULL)) {
return;
}
for (;;) {
#if defined(__arm__) || defined(__aarch64__)
while (__atomic_load_n(lock, __ATOMIC_SEQ_CST) != 0) {
#else
while (*lock != 0) {
#endif
SPIN_STAT_INC(stat, spins);
spin_times++;
if (SECUREC_UNLIKELY(spin_times == GS_SPIN_COUNT)) {
cm_spin_sleep_and_stat(stat);
spin_times = 0;
}
}
if (SECUREC_LIKELY(cm_spin_set(lock, sid) == 0)) {
break;
}
SPIN_STAT_INC(stat, fails);
sleep_times++;
#ifndef WIN32
for (uint32 i = 0; i < sleep_times; i++) {
fas_cpu_pause();
}
#endif
}
}
#ifdef __cplusplus
}
#endif
#endif