514 lines
14 KiB
C
514 lines
14 KiB
C
/**
|
||
* 日期: 2025-03-08
|
||
* 作者: coffee
|
||
* 描述: 毫秒级定时器, 最小堆实现, 中断/多线程安全
|
||
*/
|
||
|
||
#include "HTimer.h"
|
||
#include "HDLog.h"
|
||
#include <string.h>
|
||
|
||
// 用于拷贝定时器文件使用可直接删除HDLog.h文件避免依赖
|
||
#ifndef LogD
|
||
#define LogD(...)
|
||
#endif
|
||
|
||
// ==================== 原子操作支持 ====================
|
||
#if defined(__GNUC__) || defined(__clang__)
|
||
// 支持 GCC 扩展
|
||
// 使用 __atomic_* 系列函数,提供明确的内存序语义
|
||
static inline int htimer_cas(volatile uint8_t *ptr, uint8_t oldv, uint8_t newv) {
|
||
return __atomic_compare_exchange_n(ptr, &oldv, newv, 0, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
|
||
}
|
||
static inline void htimer_store(volatile uint8_t *ptr, uint8_t val) {
|
||
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
|
||
}
|
||
#define HTIMER_CAS(ptr, oldv, newv) htimer_cas(ptr, oldv, newv)
|
||
#define HTIMER_STORE(ptr, val) htimer_store(ptr, val)
|
||
#define HTIMER_BARRIER() __atomic_thread_fence(__ATOMIC_SEQ_CST)
|
||
#define HTIMER_ATOMIC_OR(ptr, val) __atomic_fetch_or(ptr, val, __ATOMIC_RELEASE)
|
||
#define HTIMER_ATOMIC_XCHG(ptr, val) __atomic_exchange_n(ptr, val, __ATOMIC_ACQ_REL)
|
||
#define HTIMER_ATOMIC_LOAD(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
|
||
#define HTIMER_RELEASE_FENCE() __atomic_thread_fence(__ATOMIC_RELEASE)
|
||
#define HTIMER_ACQUIRE_FENCE() __atomic_thread_fence(__ATOMIC_ACQUIRE)
|
||
#elif defined(_MSC_VER)
|
||
#include <intrin.h>
|
||
#define HTIMER_CAS(ptr, oldv, newv) (_InterlockedCompareExchange8((volatile char*)(ptr), (char)(newv), (char)(oldv)) == (char)(oldv))
|
||
#define HTIMER_STORE(ptr, val) (*(ptr) = (val))
|
||
#define HTIMER_BARRIER() _ReadWriteBarrier()
|
||
#define HTIMER_ATOMIC_OR(ptr, val) _InterlockedOr8((volatile char*)(ptr), (char)(val))
|
||
#define HTIMER_ATOMIC_XCHG(ptr, val) _InterlockedExchange8((volatile char*)(ptr), (char)(val))
|
||
#define HTIMER_ATOMIC_LOAD(ptr) (*(volatile uint8_t*)(ptr))
|
||
#define HTIMER_RELEASE_FENCE() _ReadWriteBarrier()
|
||
#define HTIMER_ACQUIRE_FENCE() _ReadWriteBarrier()
|
||
#else
|
||
#pragma message("HTimer Not use atomic ops")
|
||
static inline int htimer_cas_simple(volatile uint8_t *ptr, uint8_t oldv, uint8_t newv) {
|
||
if (*ptr == oldv) { *ptr = newv; return 1; }
|
||
return 0;
|
||
}
|
||
static inline uint8_t htimer_xchg_simple(volatile uint8_t *ptr, uint8_t newv) {
|
||
uint8_t old = *ptr; *ptr = newv; return old;
|
||
}
|
||
#define HTIMER_CAS(ptr, oldv, newv) htimer_cas_simple(ptr, oldv, newv)
|
||
#define HTIMER_STORE(ptr, val) (*(ptr) = (val))
|
||
#define HTIMER_BARRIER() ((void)0)
|
||
#define HTIMER_ATOMIC_OR(ptr, val) (*(ptr) |= (val))
|
||
#define HTIMER_ATOMIC_XCHG(ptr, val) htimer_xchg_simple(ptr, val)
|
||
#define HTIMER_ATOMIC_LOAD(ptr) (*(ptr))
|
||
#define HTIMER_RELEASE_FENCE() ((void)0)
|
||
#define HTIMER_ACQUIRE_FENCE() ((void)0)
|
||
#endif
|
||
|
||
// 定时器内部状态
|
||
typedef enum {
|
||
kTimerUnused = 0, ///< 未使用
|
||
kTimerAdding, ///< 添加中 (单核中断安全: 初始化字段期间)
|
||
kTimerActive, ///< 活跃状态
|
||
kTimerDelete ///< 待删除
|
||
} eHTimerState;
|
||
|
||
static uint32_t (*GetCurrentMs)(void);
|
||
|
||
#define CHECK_VALUE (0x0FFFFFFF)
|
||
#define CREATE_INDEX(id, index) ((id << 8) | index)
|
||
#define GET_ID(index) ((index >> 8) & 0xFF)
|
||
#define GET_INDEX(index) (index & 0xFF)
|
||
#define HEAP_INVALID_INDEX (HTIMER_LEN_MAX)
|
||
|
||
struct __attribute__((packed)) TimerInfo {
|
||
TimeRegisterInfo *info;
|
||
uint8_t infoLen;
|
||
};
|
||
|
||
static struct TimerInfo sInfo;
|
||
|
||
// ==================== 最小堆操作 ====================
|
||
// 堆使用 timers[i].heapMem 存储堆位置 i 处的定时器索引
|
||
// 注意:堆操作在 HTimerRun 中单线程执行,无需加锁
|
||
|
||
#define HEAP_AT(reg, heapIdx) ((reg)->timers[heapIdx].heapMem)
|
||
#define HEAP_SET(reg, heapIdx, timerIdx) ((reg)->timers[heapIdx].heapMem = (timerIdx))
|
||
|
||
static inline HTimerInfo* getTimer(TimeRegisterInfo *reg, int heapIdx)
|
||
{
|
||
return ®->timers[HEAP_AT(reg, heapIdx)];
|
||
}
|
||
|
||
static void heapSwap(TimeRegisterInfo *reg, int a, int b)
|
||
{
|
||
HTimerLen_t ta = HEAP_AT(reg, a);
|
||
HTimerLen_t tb = HEAP_AT(reg, b);
|
||
|
||
HEAP_SET(reg, a, tb);
|
||
HEAP_SET(reg, b, ta);
|
||
|
||
reg->timers[ta].heapIndex = b;
|
||
reg->timers[tb].heapIndex = a;
|
||
}
|
||
|
||
static void heapUp(TimeRegisterInfo *reg, int i)
|
||
{
|
||
while (i > 0) {
|
||
int p = (i - 1) >> 1;
|
||
|
||
HTimerInfo *ti = getTimer(reg, i);
|
||
HTimerInfo *tp = getTimer(reg, p);
|
||
|
||
uint32_t iTime = ti->lastTime + ti->duration;
|
||
uint32_t pTime = tp->lastTime + tp->duration;
|
||
|
||
int32_t diff = (int32_t)(iTime - pTime);
|
||
if (diff > 0) {
|
||
break;
|
||
}
|
||
|
||
if (diff == 0 && HEAP_AT(reg, i) > HEAP_AT(reg, p)) {
|
||
break;
|
||
}
|
||
|
||
heapSwap(reg, i, p);
|
||
i = p;
|
||
}
|
||
}
|
||
|
||
static void heapDown(TimeRegisterInfo *reg, int i)
|
||
{
|
||
int size = reg->heapSize;
|
||
|
||
while (1) {
|
||
int l = i * 2 + 1;
|
||
int r = l + 1;
|
||
int s = i;
|
||
|
||
if (l < size) {
|
||
HTimerInfo *tl = getTimer(reg, l);
|
||
HTimerInfo *ts = getTimer(reg, s);
|
||
int32_t diff = (int32_t)((tl->lastTime + tl->duration) - (ts->lastTime + ts->duration));
|
||
if (diff < 0 || (diff == 0 && HEAP_AT(reg, l) < HEAP_AT(reg, s))) {
|
||
s = l;
|
||
}
|
||
}
|
||
|
||
if (r < size) {
|
||
HTimerInfo *tr = getTimer(reg, r);
|
||
HTimerInfo *ts = getTimer(reg, s);
|
||
int32_t diff = (int32_t)((tr->lastTime + tr->duration) - (ts->lastTime + ts->duration));
|
||
if (diff < 0 || (diff == 0 && HEAP_AT(reg, r) < HEAP_AT(reg, s))) {
|
||
s = r;
|
||
}
|
||
}
|
||
|
||
if (s == i) {
|
||
break;
|
||
}
|
||
|
||
heapSwap(reg, i, s);
|
||
i = s;
|
||
}
|
||
}
|
||
|
||
static void heapPush(TimeRegisterInfo *reg, HTimerLen_t timerIdx)
|
||
{
|
||
if (reg->heapSize >= reg->len) {
|
||
return;
|
||
}
|
||
|
||
HTimerLen_t i = reg->heapSize++;
|
||
HEAP_SET(reg, i, timerIdx);
|
||
reg->timers[timerIdx].heapIndex = i;
|
||
heapUp(reg, i);
|
||
}
|
||
|
||
static HTimerLen_t heapPop(TimeRegisterInfo *reg)
|
||
{
|
||
if (reg->heapSize == 0) {
|
||
return HEAP_INVALID_INDEX;
|
||
}
|
||
|
||
HTimerLen_t ret = HEAP_AT(reg, 0);
|
||
reg->timers[ret].heapIndex = HEAP_INVALID_INDEX;
|
||
|
||
if (--reg->heapSize > 0) {
|
||
HEAP_SET(reg, 0, HEAP_AT(reg, reg->heapSize));
|
||
reg->timers[HEAP_AT(reg, 0)].heapIndex = 0;
|
||
heapDown(reg, 0);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
// ==================== 定时器操作 ====================
|
||
|
||
static HTimer_t AddTimerData(uint8_t id, uint32_t duration, HTimerCallType call, eHTimerFlags flags)
|
||
{
|
||
if (!GetCurrentMs) {
|
||
LogD("GetCurrentMs not init");
|
||
return HTIMER_INVALID;
|
||
}
|
||
|
||
if (id >= sInfo.infoLen) {
|
||
LogD("error id[%d]", id);
|
||
return HTIMER_INVALID;
|
||
}
|
||
|
||
if ((duration & ~CHECK_VALUE) != 0) {
|
||
LogD("duration overflow, duration[%d]", duration);
|
||
return HTIMER_INVALID;
|
||
}
|
||
|
||
TimeRegisterInfo *reg = &sInfo.info[id];
|
||
uint32_t now = GetCurrentMs();
|
||
|
||
// 原子地查找并占用空闲槽位
|
||
for (HTimerLen_t i = 0; i < reg->len; ++i) {
|
||
HTimerInfo *t = ®->timers[i];
|
||
|
||
// 第一步:CAS 原子占用,kTimerUnused -> kTimerAdding
|
||
// 单核中断安全:即使中断发生,HTimerRun 也不会处理 kTimerAdding 状态
|
||
if (!HTIMER_CAS(&t->state, kTimerUnused, kTimerAdding)) {
|
||
continue;
|
||
}
|
||
|
||
// 成功占用槽位,设置其他字段
|
||
#if HTIMER_USE_USERDATA
|
||
t->userData = 0;
|
||
#endif
|
||
t->duration = duration;
|
||
t->lastTime = now;
|
||
t->call = call;
|
||
t->flags = flags;
|
||
t->heapIndex = HEAP_INVALID_INDEX;
|
||
// 确保上述写入在状态变更前完成
|
||
HTIMER_RELEASE_FENCE();
|
||
|
||
// 第二步:kTimerAdding -> kTimerActive,标记初始化完成
|
||
HTIMER_STORE(&t->state, kTimerActive);
|
||
|
||
// 标记需要调度
|
||
HTIMER_ATOMIC_OR(®->schedu, 1);
|
||
|
||
return CREATE_INDEX(id, i);
|
||
}
|
||
|
||
// 槽位满,尝试紧急回收已删除的定时器
|
||
for (HTimerLen_t i = 0; i < reg->len; ++i) {
|
||
HTimerInfo *t = ®->timers[i];
|
||
|
||
// 原子地将 kTimerDelete 转为 kTimerUnused
|
||
if (!HTIMER_CAS(&t->state, kTimerDelete, kTimerUnused)) {
|
||
continue;
|
||
}
|
||
|
||
// 回收成功,再次尝试占用
|
||
if (HTIMER_CAS(&t->state, kTimerUnused, kTimerAdding)) {
|
||
#if HTIMER_USE_USERDATA
|
||
t->userData = 0;
|
||
#endif
|
||
t->duration = duration;
|
||
t->lastTime = now;
|
||
t->call = call;
|
||
t->flags = flags;
|
||
t->heapIndex = HEAP_INVALID_INDEX;
|
||
HTIMER_RELEASE_FENCE();
|
||
HTIMER_STORE(&t->state, kTimerActive);
|
||
HTIMER_ATOMIC_OR(®->schedu, 1);
|
||
return CREATE_INDEX(id, i);
|
||
}
|
||
}
|
||
|
||
LogD("timers full, id[%d], duration[%d], flags[%d]", id, duration, flags);
|
||
return HTIMER_INVALID;
|
||
}
|
||
|
||
void HTimerInitMs(uint32_t (*func)(void))
|
||
{
|
||
GetCurrentMs = func;
|
||
}
|
||
|
||
uint32_t HTimerGetMs()
|
||
{
|
||
if (!GetCurrentMs) {
|
||
return 0;
|
||
}
|
||
return GetCurrentMs();
|
||
}
|
||
|
||
void HTimerInitRegister(TimeRegisterInfo *info, HTimerLen_t len)
|
||
{
|
||
memset(info, 0, sizeof(*info) * len);
|
||
sInfo.info = info;
|
||
sInfo.infoLen = len;
|
||
}
|
||
|
||
uint8_t HTimerRegisterTimerInfo(uint8_t id, HTimerInfo *info, uint16_t infoLen)
|
||
{
|
||
if (id >= sInfo.infoLen) {
|
||
LogD("error id[%d], max[%d]", id, sInfo.infoLen);
|
||
return 0;
|
||
}
|
||
|
||
if (infoLen > HTIMER_LEN_MAX) {
|
||
LogD("infoLen too large, max 255");
|
||
return 0;
|
||
}
|
||
|
||
memset(info, 0, sizeof(*info) * infoLen);
|
||
|
||
TimeRegisterInfo *reg = &sInfo.info[id];
|
||
reg->timers = info;
|
||
reg->len = (HTimerLen_t)infoLen;
|
||
reg->heapSize = 0;
|
||
|
||
for (HTimerLen_t i = 0; i < infoLen; ++i) {
|
||
info[i].heapIndex = HEAP_INVALID_INDEX;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
void HTimerRun(uint8_t id)
|
||
{
|
||
if (!GetCurrentMs || id >= sInfo.infoLen) {
|
||
return;
|
||
}
|
||
|
||
TimeRegisterInfo *reg = &sInfo.info[id];
|
||
if (reg->run) {
|
||
return ;
|
||
}
|
||
|
||
reg->run = 1;
|
||
uint32_t now = GetCurrentMs();
|
||
|
||
// schedu 阶段:只有 HTimerRun 修改堆,单线程安全
|
||
// 使用 acquire-release 语义确保看到其他线程的写入
|
||
while (HTIMER_ATOMIC_XCHG(®->schedu, 0)) {
|
||
// 确保看到 AddTimerData 中的写入
|
||
HTIMER_ACQUIRE_FENCE();
|
||
|
||
// 1. 清理堆中已删除的定时器
|
||
for (HTimerLen_t i = reg->heapSize; i > 0;) {
|
||
i--;
|
||
HTimerLen_t tIdx = HEAP_AT(reg, i);
|
||
HTimerInfo *t = ®->timers[tIdx];
|
||
if (HTIMER_ATOMIC_LOAD(&t->state) == kTimerDelete) {
|
||
HEAP_SET(reg, i, HEAP_AT(reg, --reg->heapSize));
|
||
t->heapIndex = HEAP_INVALID_INDEX;
|
||
t->state = kTimerUnused;
|
||
HTIMER_BARRIER();
|
||
if (i < reg->heapSize) {
|
||
heapUp(reg, i);
|
||
heapDown(reg, i);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. 清理不在堆中的已删除定时器 + 添加活跃定时器
|
||
for (HTimerLen_t i = 0; i < reg->len; i++) {
|
||
HTimerInfo *t = ®->timers[i];
|
||
uint8_t state = HTIMER_ATOMIC_LOAD(&t->state);
|
||
|
||
if (state == kTimerDelete) {
|
||
t->state = kTimerUnused;
|
||
continue;
|
||
}
|
||
|
||
if (state == kTimerActive && t->heapIndex == HEAP_INVALID_INDEX) {
|
||
heapPush(reg, i);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 执行到期的定时器
|
||
while (reg->heapSize > 0) {
|
||
HTimerLen_t tIdx = HEAP_AT(reg, 0);
|
||
HTimerInfo *t = ®->timers[tIdx];
|
||
|
||
if (HTIMER_ATOMIC_LOAD(&t->state) == kTimerDelete) {
|
||
heapPop(reg);
|
||
t->state = kTimerUnused;
|
||
t->heapIndex = HEAP_INVALID_INDEX;
|
||
continue;
|
||
}
|
||
|
||
uint32_t triggerTime = t->lastTime + t->duration;
|
||
if ((int32_t)(now - triggerTime) < 0) {
|
||
break;
|
||
}
|
||
|
||
heapPop(reg);
|
||
|
||
t->curr = 1;
|
||
HTIMER_BARRIER();
|
||
if (t->call) {
|
||
t->call();
|
||
}
|
||
t->curr = 0;
|
||
HTIMER_BARRIER();
|
||
|
||
if (HTIMER_ATOMIC_LOAD(&t->state) != kTimerActive) {
|
||
continue;
|
||
}
|
||
|
||
t->lastTime = now;
|
||
|
||
if (t->flags == kHTimerLoop) {
|
||
heapPush(reg, tIdx);
|
||
} else {
|
||
t->state = kTimerDelete;
|
||
HTIMER_ATOMIC_OR(®->schedu, 1);
|
||
}
|
||
}
|
||
|
||
reg->run = 0;
|
||
}
|
||
|
||
HTimer_t HTimerAdd(uint8_t id, uint32_t ms, HTimerCallType call, eHTimerFlags flags)
|
||
{
|
||
#ifdef HTIMER_CHECK_INVALID_LOOP
|
||
HTimer_t result = AddTimerData(id, ms, call, flags);
|
||
if (result == HTIMER_INVALID) { while (1); }
|
||
return result;
|
||
#else
|
||
return AddTimerData(id, ms, call, flags);
|
||
#endif
|
||
}
|
||
|
||
void HTimerRemove(HTimer_t index)
|
||
{
|
||
if (index == HTIMER_INVALID) {
|
||
return;
|
||
}
|
||
|
||
uint8_t id = GET_ID(index);
|
||
uint8_t i = GET_INDEX(index);
|
||
|
||
if (id >= sInfo.infoLen) {
|
||
return;
|
||
}
|
||
|
||
TimeRegisterInfo *reg = &sInfo.info[id];
|
||
if (i >= reg->len) {
|
||
return;
|
||
}
|
||
|
||
HTimerInfo *t = ®->timers[i];
|
||
|
||
// 原子设置状态
|
||
uint8_t oldState;
|
||
do {
|
||
oldState = t->state;
|
||
if (oldState == kTimerUnused) {
|
||
return; // 已经删除
|
||
}
|
||
} while (!HTIMER_CAS(&t->state, oldState, kTimerDelete));
|
||
|
||
// 标记需要调度
|
||
HTIMER_ATOMIC_OR(®->schedu, 1);
|
||
}
|
||
|
||
#if HTIMER_USE_USERDATA
|
||
|
||
void HTimerSetUserData(HTimer_t index, long data)
|
||
{
|
||
if (index == HTIMER_INVALID) {
|
||
return;
|
||
}
|
||
uint8_t id = GET_ID(index);
|
||
uint8_t i = GET_INDEX(index);
|
||
if (id >= sInfo.infoLen || i >= sInfo.info[id].len) {
|
||
return;
|
||
}
|
||
sInfo.info[id].timers[i].userData = data;
|
||
}
|
||
|
||
long HTimerGetUserData(HTimer_t index)
|
||
{
|
||
if (index == HTIMER_INVALID) {
|
||
return 0;
|
||
}
|
||
uint8_t id = GET_ID(index);
|
||
uint8_t i = GET_INDEX(index);
|
||
if (id >= sInfo.infoLen || i >= sInfo.info[id].len) {
|
||
return 0;
|
||
}
|
||
return sInfo.info[id].timers[i].userData;
|
||
}
|
||
|
||
long HTimerGetCurrCallUserData(uint8_t id)
|
||
{
|
||
if (id >= sInfo.infoLen) {
|
||
return 0;
|
||
}
|
||
TimeRegisterInfo *reg = &sInfo.info[id];
|
||
for (HTimerLen_t i = 0; i < reg->len; ++i) {
|
||
if (reg->timers[i].curr) {
|
||
return reg->timers[i].userData;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#endif
|