* 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_latch.h
*
*
* IDENTIFICATION
* src/cm_concurrency/cm_latch.h
*
* -------------------------------------------------------------------------
*/
#ifndef __CM_LATCH_H_
#define __CM_LATCH_H_
#include "cm_types.h"
#include "cm_spinlock.h"
#include "cm_log.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum en_latch_mode {
LATCH_MODE_S = 1,
LATCH_MODE_X = 2,
LATCH_MODE_FORCE_S = 3,
} latch_mode_t;
typedef enum en_latch_status {
LATCH_STATUS_IDLE = 0,
LATCH_STATUS_S = 1,
LATCH_STATUS_IX = 2,
LATCH_STATUS_X = 3,
} latch_stat_e;
typedef struct st_latch {
spinlock_t lock;
volatile uint16 shared_count;
volatile uint16 stat;
volatile uint16 sid;
uint16 unused;
} latch_t;
typedef struct st_latch_statis {
uint64 r_sleeps;
uint64 x_sleeps;
uint64 s_sleeps;
uint64 ix_sleeps;
spin_statis_t x_spin;
spin_statis_t s_spin;
spin_statis_t ix_spin;
uint64 hits;
uint64 misses;
uint64 spin_gets;
bool32 enable;
spin_statis_instance_t spin_stat;
} latch_statis_t;
static inline void cm_latch_init(latch_t *latch)
{
latch->sid = 0;
latch->lock = 0;
latch->stat = 0;
latch->unused = 0;
latch->shared_count = 0;
}
#define LATCH_NEED_STAT(stat) ((stat) != NULL && (stat)->enable)
static inline void cm_latch_stat_inc(latch_statis_t *stat, uint32 count)
{
if (LATCH_NEED_STAT(stat)) {
stat->hits++;
stat->spin_gets = (count == 0) ? 0 : stat->spin_gets + 1;
}
}
static inline void cm_latch_ix2x(latch_t *latch, uint32 sid, latch_statis_t *stat)
{
uint32 count = 0;
do {
if (LATCH_NEED_STAT(stat)) {
stat->misses++;
}
while (latch->shared_count > 0) {
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, ix_sleeps);
cm_spin_sleep();
count = 0;
}
}
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->ix_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
if (latch->shared_count == 0) {
latch->sid = (uint16)sid;
latch->stat = LATCH_STATUS_X;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return;
}
cm_spin_unlock(&latch->lock);
} while (1);
}
static inline bool32 cm_latch_timed_ix2x(latch_t *latch, uint32 sid, uint32 wait_ticks, latch_statis_t *stat)
{
uint32 count = 0;
uint32 ticks = 0;
do {
if (LATCH_NEED_STAT(stat)) {
stat->misses++;
}
while (latch->shared_count > 0) {
if (ticks >= wait_ticks) {
return CM_FALSE;
}
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, ix_sleeps);
cm_spin_sleep();
count = 0;
ticks++;
}
}
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->ix_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
if (latch->shared_count == 0) {
latch->sid = (uint16)sid;
latch->stat = LATCH_STATUS_X;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return CM_TRUE;
}
cm_spin_unlock(&latch->lock);
} while (1);
}
static inline void cm_latch_x(latch_t *latch, uint32 sid, latch_statis_t *stat)
{
uint32 count = 0;
do {
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->x_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
if (latch->stat == LATCH_STATUS_IDLE) {
latch->sid = (uint16)sid;
latch->stat = LATCH_STATUS_X;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return;
} else if (latch->stat == LATCH_STATUS_S) {
latch->stat = LATCH_STATUS_IX;
cm_spin_unlock(&latch->lock);
cm_latch_ix2x(latch, sid, stat);
return;
} else {
cm_spin_unlock(&latch->lock);
if (LATCH_NEED_STAT(stat)) {
stat->misses++;
}
while (latch->stat != LATCH_STATUS_IDLE && latch->stat != LATCH_STATUS_S) {
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, x_sleeps);
cm_spin_sleep();
count = 0;
}
}
}
} while (1);
}
static inline bool32 cm_latch_timed_x(latch_t *latch, uint32 sid, uint32 wait_ticks, latch_statis_t *stat)
{
uint32 count = 0;
uint32 ticks = 0;
do {
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->x_spin : NULL,
(LATCH_NEED_STAT(stat) ? &stat->spin_stat : NULL));
if (latch->stat == LATCH_STATUS_IDLE) {
latch->sid = (uint16)sid;
latch->stat = LATCH_STATUS_X;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return CM_TRUE;
} else if (latch->stat == LATCH_STATUS_S) {
latch->stat = LATCH_STATUS_IX;
cm_spin_unlock(&latch->lock);
if (!cm_latch_timed_ix2x(latch, sid, wait_ticks, stat)) {
cm_spin_lock_with_stat(&latch->lock, NULL, (LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
latch->stat = latch->shared_count > 0 ? LATCH_STATUS_S : LATCH_STATUS_IDLE;
cm_spin_unlock(&latch->lock);
return CM_FALSE;
}
return CM_TRUE;
} else {
cm_spin_unlock(&latch->lock);
if (LATCH_NEED_STAT(stat)) {
stat->misses++;
}
while (latch->stat != LATCH_STATUS_IDLE && latch->stat != LATCH_STATUS_S) {
if (ticks >= wait_ticks) {
return CM_FALSE;
}
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, x_sleeps);
cm_spin_sleep();
count = 0;
ticks++;
}
}
}
} while (1);
}
static inline void cm_latch_x2ix(latch_t *latch, uint32 sid, latch_statis_t *stat)
{
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->x_spin : NULL,
(LATCH_NEED_STAT(stat) ? &stat->spin_stat : NULL));
if (SECUREC_UNLIKELY(latch->sid != sid || latch->stat != LATCH_STATUS_X)) {
LOG_RUN_ERR("latch sid:%u != sid:%u or stat:%u not stat X", (uint32)latch->sid, sid, (uint32)latch->stat);
cm_spin_unlock(&latch->lock);
return;
}
latch->stat = LATCH_STATUS_IX;
cm_spin_unlock(&latch->lock);
}
static inline void cm_latch_degrade(latch_t *latch, uint32 sid, latch_statis_t *stat)
{
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->x_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
cm_panic_log(latch->sid == sid && latch->stat == LATCH_STATUS_X,
"latch sid:%u != sid:%u or stat:%u not stat X", (uint32)latch->sid, sid, (uint32)latch->stat);
latch->stat = LATCH_STATUS_S;
latch->shared_count = 1;
cm_spin_unlock(&latch->lock);
return;
}
static inline void cm_latch_s(latch_t *latch, uint32 sid, bool32 is_force, latch_statis_t *stat)
{
uint32 count = 0;
do {
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->s_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
if (latch->stat == LATCH_STATUS_IDLE) {
latch->stat = LATCH_STATUS_S;
latch->shared_count = 1;
latch->sid = (uint16)sid;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return;
} else if ((latch->stat == LATCH_STATUS_S) || (latch->stat == LATCH_STATUS_IX && is_force)) {
latch->shared_count++;
cm_spin_unlock(&latch->lock);
cm_latch_stat_inc(stat, count);
return;
} else {
cm_spin_unlock(&latch->lock);
if (LATCH_NEED_STAT(stat)) {
stat->misses++;
}
while (latch->stat != LATCH_STATUS_IDLE && latch->stat != LATCH_STATUS_S &&
!(latch->stat == LATCH_STATUS_IX && is_force)) {
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, s_sleeps);
cm_spin_sleep();
count = 0;
}
}
}
} while (1);
}
static inline bool32 cm_latch_timed_s(latch_t *latch, uint32 wait_ticks, bool32 is_force, latch_statis_t *stat)
{
uint32 count = 0;
uint32 ticks = 0;
do {
cm_spin_lock_with_stat(&latch->lock, (LATCH_NEED_STAT(stat)) ? &stat->s_spin : NULL,
(LATCH_NEED_STAT(stat)) ? &stat->spin_stat : NULL);
if (latch->stat == LATCH_STATUS_IDLE) {
latch->stat = LATCH_STATUS_S;
latch->shared_count = 1;
cm_spin_unlock(&latch->lock);
return CM_TRUE;
} else if ((latch->stat == LATCH_STATUS_S) || (latch->stat == LATCH_STATUS_IX && is_force)) {
latch->shared_count++;
cm_spin_unlock(&latch->lock);
return CM_TRUE;
} else {
cm_spin_unlock(&latch->lock);
while (latch->stat != LATCH_STATUS_IDLE && latch->stat != LATCH_STATUS_S &&
(latch->stat == LATCH_STATUS_IX && is_force)) {
if (ticks >= wait_ticks) {
return CM_FALSE;
}
count++;
if (count >= GS_SPIN_COUNT) {
SPIN_STAT_INC(stat, s_sleeps);
cm_spin_sleep();
count = 0;
ticks++;
}
}
}
} while (1);
}
static inline void cm_unlatch(latch_t *latch, latch_statis_t *stat)
{
spin_statis_t *stat_spin = NULL;
spin_statis_instance_t *stat_spin_ex = NULL;
if (LATCH_NEED_STAT(stat)) {
stat_spin = (latch->stat == LATCH_STATUS_S) ? &stat->s_spin : &stat->x_spin;
stat_spin_ex = &stat->spin_stat;
}
cm_spin_lock_with_stat(&latch->lock, stat_spin, stat_spin_ex);
if (latch->shared_count > 0) {
latch->shared_count--;
}
if ((latch->stat == LATCH_STATUS_S || latch->stat == LATCH_STATUS_X) && (latch->shared_count == 0)) {
latch->sid = 0;
latch->stat = LATCH_STATUS_IDLE;
}
cm_spin_unlock(&latch->lock);
}
static inline void cm_unlatch_x(latch_t *latch, latch_statis_t *stat)
{
spin_statis_t *stat_spin = NULL;
spin_statis_instance_t *stat_spin_ex = NULL;
if (LATCH_NEED_STAT(stat)) {
stat_spin = (latch->stat == LATCH_STATUS_S) ? &stat->s_spin : &stat->x_spin;
stat_spin_ex = &stat->spin_stat;
}
cm_spin_lock_with_stat(&latch->lock, stat_spin, stat_spin_ex);
if (latch->stat == LATCH_STATUS_X) {
latch->sid = 0;
latch->stat = LATCH_STATUS_IDLE;
}
cm_spin_unlock(&latch->lock);
}
static inline const char *cm_latch_stat(uint16 stat)
{
switch (stat) {
case LATCH_STATUS_IDLE:
return "idle";
case LATCH_STATUS_S:
return "s";
case LATCH_STATUS_IX:
return "ix";
case LATCH_STATUS_X:
return "x";
default:
return "invalid";
}
}
#ifdef __cplusplus
}
#endif
#endif