563 lines
17 KiB
C++
563 lines
17 KiB
C++
#include <a8/a8.h>
|
|
|
|
#include <a8/xtimer.h>
|
|
|
|
enum TimerType_e
|
|
{
|
|
kTimeOutTimer = 0,
|
|
kIntervalTimer = 1
|
|
};
|
|
|
|
#define CONFIG_BASE_SMALL 0
|
|
#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
|
|
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
|
|
#define TVN_SIZE (1 << TVN_BITS)
|
|
#define TVR_SIZE (1 << TVR_BITS)
|
|
#define TVN_MASK (TVN_SIZE - 1)
|
|
#define TVR_MASK (TVR_SIZE - 1)
|
|
#define INDEX(N) ((base->timer_tick >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)
|
|
|
|
struct xtvec {
|
|
struct list_head vec[TVN_SIZE];
|
|
};
|
|
|
|
struct xtvec_root {
|
|
struct list_head vec[TVR_SIZE];
|
|
};
|
|
|
|
struct xtvec_base {
|
|
struct xtimer_list *running_timer;
|
|
long long timer_tick;
|
|
list_head free_timer;
|
|
int free_timer_num;
|
|
struct xtvec_root tv1;
|
|
struct xtvec tv2;
|
|
struct xtvec tv3;
|
|
struct xtvec tv4;
|
|
struct xtvec tv5;
|
|
};
|
|
|
|
struct xtimer_list {
|
|
struct list_head destory_handle_list;
|
|
struct list_head entry;
|
|
struct list_head attach_entry;
|
|
TimerType_e timer_type;
|
|
long long expires;
|
|
int expire_time;
|
|
struct xtvec_base *base;
|
|
|
|
a8::TimerCb cb;
|
|
};
|
|
|
|
static void InternalAddXTimer(struct xtvec_base *base, struct xtimer_list *timer)
|
|
{
|
|
long long expires = timer->expires;
|
|
long long idx = expires - base->timer_tick;
|
|
struct list_head *vec;
|
|
|
|
if (idx < 0) {
|
|
vec = base->tv1.vec + (base->timer_tick & TVR_MASK);
|
|
}else if (idx < TVR_SIZE) {
|
|
int i = expires & TVR_MASK;
|
|
vec = base->tv1.vec + i;
|
|
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
|
|
int i = (expires >> TVR_BITS) & TVN_MASK;
|
|
vec = base->tv2.vec + i;
|
|
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
|
|
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
|
|
vec = base->tv3.vec + i;
|
|
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
|
|
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
|
|
vec = base->tv4.vec + i;
|
|
} else {
|
|
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
|
|
vec = base->tv5.vec + i;
|
|
}
|
|
list_add_tail(&timer->entry, vec);
|
|
}
|
|
|
|
static int Cascade(struct xtvec_base *base, struct xtvec *tv, int index)
|
|
{
|
|
/* cascade all the timers from tv up one level */
|
|
struct xtimer_list *timer, *tmp;
|
|
struct list_head tv_list;
|
|
|
|
list_replace_init(tv->vec + index, &tv_list);
|
|
|
|
list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
|
|
InternalAddXTimer(base, timer);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
namespace a8
|
|
{
|
|
typedef struct list_head XTimerDestoryHandle;
|
|
|
|
struct XTimerPtr
|
|
{
|
|
xtimer_list* timer = nullptr;
|
|
};
|
|
|
|
struct XTimerDestoryHandleNode
|
|
{
|
|
struct list_head entry;
|
|
std::function<void(xtimer_list*)> cb;
|
|
};
|
|
|
|
class XTimerImpl
|
|
{
|
|
public:
|
|
|
|
XTimerImpl()
|
|
{
|
|
base_ = new xtvec_base();
|
|
base_->running_timer = nullptr;
|
|
INIT_LIST_HEAD(&base_->free_timer);
|
|
base_->free_timer_num = 0;
|
|
for (int j = 0; j < TVN_SIZE; j++) {
|
|
INIT_LIST_HEAD(base_->tv5.vec + j);
|
|
INIT_LIST_HEAD(base_->tv4.vec + j);
|
|
INIT_LIST_HEAD(base_->tv3.vec + j);
|
|
INIT_LIST_HEAD(base_->tv2.vec + j);
|
|
}
|
|
for (int j = 0; j < TVR_SIZE; j++) {
|
|
INIT_LIST_HEAD(base_->tv1.vec + j);
|
|
}
|
|
}
|
|
|
|
~XTimerImpl()
|
|
{
|
|
ClearTimer();
|
|
A8_SAFE_DELETE(base_);
|
|
}
|
|
|
|
void Init(XGetTickCountFunc func, void* context, int gc_time, int cache_timer_num)
|
|
{
|
|
get_tick_count_func_ = func;
|
|
context_ = context;
|
|
gc_time_ = gc_time;
|
|
cache_timer_num_ = cache_timer_num;
|
|
base_->timer_tick = get_tick_count_func_(context_);
|
|
InternalSetInterval
|
|
(gc_time_,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
int i = 0;
|
|
while (!list_empty(&base_->free_timer) &&
|
|
base_->free_timer_num > cache_timer_num_ && i < 1000) {
|
|
xtimer_list* timer = list_first_entry(&base_->free_timer, struct xtimer_list, entry);
|
|
list_del_init(&timer->entry);
|
|
delete timer;
|
|
|
|
base_->free_timer_num--;
|
|
++i;
|
|
}
|
|
}
|
|
},
|
|
nullptr,
|
|
nullptr);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (get_tick_count_func_(context_) >= base_->timer_tick) {
|
|
UpdateTimer();
|
|
}
|
|
}
|
|
|
|
void InternalSetTimeout(int expire_time, TimerCb cb, Attacher* attacher, XTimerWp* wp)
|
|
{
|
|
xtimer_list *timer = NewTimerList();
|
|
InitTimerList(timer,
|
|
kTimeOutTimer,
|
|
get_tick_count_func_(context_) + expire_time,
|
|
expire_time,
|
|
cb
|
|
);
|
|
InternalModifyTime(timer, expire_time);
|
|
if (wp) {
|
|
AddTimerWp(timer, *wp);
|
|
}
|
|
}
|
|
|
|
void InternalSetInterval(int expire_time, TimerCb cb, Attacher* attacher, XTimerWp* wp)
|
|
{
|
|
xtimer_list *timer = NewTimerList();
|
|
InitTimerList(timer,
|
|
kIntervalTimer,
|
|
get_tick_count_func_(context_) + expire_time,
|
|
expire_time,
|
|
cb);
|
|
InternalModifyTime(timer, expire_time);
|
|
if (wp) {
|
|
AddTimerWp(timer, *wp);
|
|
}
|
|
}
|
|
|
|
void InternalFireEvent(xtimer_list* timer, int event, a8::Args* args)
|
|
{
|
|
if (timer->cb) {
|
|
timer->cb(event, args);
|
|
}
|
|
}
|
|
|
|
void InternalDelete(xtimer_list* timer)
|
|
{
|
|
if (!timer) {
|
|
abort();
|
|
}
|
|
if (base_->running_timer == timer) {
|
|
base_->running_timer = nullptr;
|
|
}
|
|
if (timer->cb) {
|
|
timer->cb(TIMER_DELETE_EVENT, nullptr);
|
|
}
|
|
DetachTimer(timer);
|
|
if (!list_empty(&timer->attach_entry)) {
|
|
list_del_init(&timer->attach_entry);
|
|
}
|
|
while (!list_empty(&timer->destory_handle_list)) {
|
|
XTimerDestoryHandleNode* node = list_first_entry(&timer->destory_handle_list,
|
|
XTimerDestoryHandleNode,
|
|
entry);
|
|
list_del_init(&node->entry);
|
|
node->cb(timer);
|
|
delete node;
|
|
}
|
|
AddToFreeList(timer);
|
|
}
|
|
|
|
void InternalModifyTime(xtimer_list* timer, int expire_time)
|
|
{
|
|
DetachTimer(timer);
|
|
timer->expire_time = expire_time;
|
|
timer->expires = get_tick_count_func_(context_) + expire_time;
|
|
InternalAddXTimer(base_, timer);
|
|
}
|
|
|
|
long long InternalGetRemainTime(xtimer_list* timer)
|
|
{
|
|
long long remain_time = timer->expires - get_tick_count_func_(context_);
|
|
return std::max(remain_time, (long long)0);
|
|
}
|
|
|
|
bool IsRunning()
|
|
{
|
|
return base_->running_timer != nullptr;
|
|
}
|
|
|
|
void ClearAttacher(Attacher* attacher)
|
|
{
|
|
|
|
}
|
|
|
|
void DestoryAttacher(Attacher* attacher)
|
|
{
|
|
}
|
|
|
|
void UpdateTimer()
|
|
{
|
|
struct xtvec_base *base = base_;
|
|
while (get_tick_count_func_(context_) >= base->timer_tick) {
|
|
struct list_head work_list;
|
|
struct list_head *head = &work_list;
|
|
int index = base->timer_tick & TVR_MASK;
|
|
|
|
if (!index &&
|
|
(!Cascade(base, &base->tv2, INDEX(0))) &&
|
|
(!Cascade(base, &base->tv3, INDEX(1))) &&
|
|
!Cascade(base, &base->tv4, INDEX(2))) {
|
|
Cascade(base, &base->tv5, INDEX(3));
|
|
}
|
|
++base->timer_tick;
|
|
list_replace_init(base->tv1.vec + index, &work_list);
|
|
while (!list_empty(head)) {
|
|
struct xtimer_list *timer;
|
|
timer = list_first_entry(head, struct xtimer_list,entry);
|
|
base->running_timer = timer;
|
|
if (timer->cb) {
|
|
timer->cb(TIMER_EXEC_EVENT, nullptr);
|
|
}
|
|
if (base_->running_timer) {
|
|
switch (timer->timer_type) {
|
|
case kTimeOutTimer:
|
|
{
|
|
DetachTimer(timer);
|
|
if (!list_empty(&timer->attach_entry)) {
|
|
list_del_init(&timer->attach_entry);
|
|
}
|
|
while (!list_empty(&timer->destory_handle_list)) {
|
|
XTimerDestoryHandleNode* node = list_first_entry(&timer->destory_handle_list,
|
|
XTimerDestoryHandleNode,
|
|
entry);
|
|
list_del_init(&node->entry);
|
|
node->cb(timer);
|
|
delete node;
|
|
}
|
|
AddToFreeList(timer);
|
|
if (timer->cb) {
|
|
timer->cb(TIMER_DELETE_EVENT, nullptr);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
base->running_timer = nullptr;
|
|
}
|
|
|
|
void DeleteCurrentTimer()
|
|
{
|
|
InternalDelete(base_->running_timer);
|
|
}
|
|
|
|
private:
|
|
|
|
void AddToFreeList(xtimer_list* timer)
|
|
{
|
|
list_add_tail(&timer->entry, &base_->free_timer);
|
|
base_->free_timer_num++;
|
|
}
|
|
|
|
void AddTimerWp(xtimer_list* timer, XTimerWp& wp)
|
|
{
|
|
std::shared_ptr<XTimerPtr> timer_ptr = std::make_shared<XTimerPtr>();
|
|
timer_ptr->timer = timer;
|
|
wp = timer_ptr;
|
|
AddTimerDestoryHandle
|
|
(wp,
|
|
[timer_ptr] (xtimer_list* timer)
|
|
{
|
|
timer_ptr->timer = nullptr;
|
|
});
|
|
}
|
|
|
|
xtimer_list* NewTimerList()
|
|
{
|
|
if (!list_empty(&base_->free_timer)) {
|
|
xtimer_list* timer = list_first_entry(&base_->free_timer, struct xtimer_list,entry);
|
|
list_del_init(&timer->entry);
|
|
base_->free_timer_num--;
|
|
return timer;
|
|
} else {
|
|
return new xtimer_list();
|
|
}
|
|
}
|
|
|
|
void ClearTimer()
|
|
{
|
|
auto free_timers =
|
|
[this] (list_head* head)
|
|
{
|
|
while (!list_empty(head)) {
|
|
struct xtimer_list *timer;
|
|
timer = list_first_entry(head, struct xtimer_list,entry);
|
|
DetachTimer(timer);
|
|
if (!list_empty(&timer->attach_entry)) {
|
|
list_del_init(&timer->attach_entry);
|
|
}
|
|
while (!list_empty(&timer->destory_handle_list)) {
|
|
XTimerDestoryHandleNode* node = list_first_entry(&timer->destory_handle_list,
|
|
XTimerDestoryHandleNode,
|
|
entry);
|
|
list_del_init(&node->entry);
|
|
node->cb(timer);
|
|
delete node;
|
|
}
|
|
delete timer;
|
|
}
|
|
};
|
|
for (int j = 0; j < TVN_SIZE; j++) {
|
|
free_timers(base_->tv5.vec + j);
|
|
free_timers(base_->tv4.vec + j);
|
|
free_timers(base_->tv3.vec + j);
|
|
free_timers(base_->tv2.vec + j);
|
|
}
|
|
for (int j = 0; j < TVR_SIZE; j++) {
|
|
free_timers(base_->tv1.vec + j);
|
|
}
|
|
free_timers(&base_->free_timer);
|
|
}
|
|
|
|
XTimerDestoryHandle* AddTimerDestoryHandle(std::weak_ptr<XTimerPtr>& timer_ptr,
|
|
std::function<void(xtimer_list*)> cb)
|
|
{
|
|
XTimerDestoryHandleNode* node = new XTimerDestoryHandleNode;
|
|
node->cb = cb;
|
|
list_add_tail(&node->entry, &timer_ptr.lock()->timer->destory_handle_list);
|
|
return &node->entry;
|
|
}
|
|
|
|
void RemoveTimerDestoryHandle(XTimerDestoryHandle* handle)
|
|
{
|
|
if (!handle || list_empty(handle)) {
|
|
abort();
|
|
}
|
|
|
|
XTimerDestoryHandleNode* node = list_entry(handle, XTimerDestoryHandleNode, entry);
|
|
list_del_init(&node->entry);
|
|
delete node;
|
|
}
|
|
|
|
void InitTimerList(xtimer_list* timer,
|
|
TimerType_e timer_type,
|
|
long long expires,
|
|
int expire_time,
|
|
a8::TimerCb cb)
|
|
{
|
|
INIT_LIST_HEAD(&timer->destory_handle_list);
|
|
INIT_LIST_HEAD(&timer->entry);
|
|
INIT_LIST_HEAD(&timer->attach_entry);
|
|
timer->timer_type = timer_type;
|
|
timer->expires = expires;
|
|
timer->expire_time = expire_time;
|
|
timer->base = base_;
|
|
timer->cb = std::move(cb);
|
|
}
|
|
|
|
int DetachTimer(struct xtimer_list *timer)
|
|
{
|
|
if (!list_empty(&timer->entry)) {
|
|
list_del_init(&timer->entry);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
private:
|
|
xtvec_base* base_ = nullptr;
|
|
XGetTickCountFunc get_tick_count_func_ = nullptr;
|
|
void* context_ = nullptr;
|
|
int gc_time_ = 10;
|
|
int cache_timer_num_ = 100;
|
|
};
|
|
|
|
XTimer::XTimer()
|
|
{
|
|
impl_ = new XTimerImpl();
|
|
}
|
|
|
|
XTimer::~XTimer()
|
|
{
|
|
A8_SAFE_DELETE(impl_);
|
|
}
|
|
|
|
void XTimer::Init(XGetTickCountFunc func, void* context, int gc_time, int cache_timer_num)
|
|
{
|
|
impl_->Init(func, context, gc_time, cache_timer_num);
|
|
}
|
|
|
|
void XTimer::Update()
|
|
{
|
|
impl_->Update();
|
|
}
|
|
|
|
void XTimer::SetTimeout(int expire_time, TimerCb cb)
|
|
{
|
|
impl_->InternalSetTimeout(expire_time, cb, nullptr, nullptr);
|
|
}
|
|
|
|
void XTimer::SetTimeoutEx(int expire_time, TimerCb cb, Attacher* attacher)
|
|
{
|
|
|
|
impl_->InternalSetTimeout(expire_time, cb, attacher, nullptr);
|
|
}
|
|
|
|
XTimerWp XTimer::SetTimeoutWp(int expire_time, TimerCb cb)
|
|
{
|
|
XTimerWp result;
|
|
impl_->InternalSetTimeout(expire_time, cb, nullptr, &result);
|
|
return result;
|
|
}
|
|
|
|
XTimerWp XTimer::SetTimeoutWpEx(int expire_time, TimerCb cb, Attacher* attacher)
|
|
{
|
|
XTimerWp result;
|
|
impl_->InternalSetTimeout(expire_time, cb, attacher, &result);
|
|
return result;
|
|
}
|
|
|
|
void XTimer::SetInterval(int expire_time, a8::TimerCb cb)
|
|
{
|
|
impl_->InternalSetInterval(expire_time, cb, nullptr, nullptr);
|
|
}
|
|
|
|
void XTimer::SetIntervalEx(int expire_time, a8::TimerCb cb, Attacher* attacher)
|
|
{
|
|
impl_->InternalSetInterval(expire_time, cb, attacher, nullptr);
|
|
}
|
|
|
|
XTimerWp XTimer::SetIntervalWp(int expire_time, TimerCb cb)
|
|
{
|
|
XTimerWp result;
|
|
impl_->InternalSetInterval(expire_time, cb, nullptr, &result);
|
|
return result;
|
|
}
|
|
|
|
XTimerWp XTimer::SetIntervalWpEx(int expire_time, TimerCb cb, Attacher* attacher)
|
|
{
|
|
XTimerWp result;
|
|
impl_->InternalSetInterval(expire_time, cb, attacher, &result);
|
|
return result;
|
|
}
|
|
|
|
void XTimer::FireEvent(XTimerWp& timer_wp, int event, a8::Args* args)
|
|
{
|
|
if (timer_wp.expired()) {
|
|
abort();
|
|
}
|
|
impl_->InternalFireEvent(timer_wp.lock()->timer, event, args);
|
|
}
|
|
|
|
void XTimer::ModifyTime(XTimerWp& timer_wp, int expire_time)
|
|
{
|
|
if (timer_wp.expired()) {
|
|
abort();
|
|
}
|
|
impl_->InternalModifyTime(timer_wp.lock()->timer, expire_time);
|
|
}
|
|
|
|
void XTimer::Delete(XTimerWp& timer_wp)
|
|
{
|
|
if (timer_wp.expired()) {
|
|
abort();
|
|
}
|
|
impl_->InternalDelete(timer_wp.lock()->timer);
|
|
}
|
|
|
|
long long XTimer::GetRemainTime(XTimerWp& timer_wp)
|
|
{
|
|
if (timer_wp.expired()) {
|
|
abort();
|
|
}
|
|
return impl_->InternalGetRemainTime(timer_wp.lock()->timer);
|
|
}
|
|
|
|
bool XTimer::IsRunning()
|
|
{
|
|
return impl_->IsRunning();
|
|
}
|
|
|
|
void XTimer::ClearAttacher(Attacher* attacher)
|
|
{
|
|
impl_->ClearAttacher(attacher);
|
|
}
|
|
|
|
void XTimer::DestoryAttacher(Attacher* attacher)
|
|
{
|
|
impl_->DestoryAttacher(attacher);
|
|
}
|
|
|
|
void XTimer::DeleteCurrentTimer()
|
|
{
|
|
impl_->DeleteCurrentTimer();
|
|
}
|
|
|
|
}
|