时间轮是一种很有趣的设计,可以用来实现一个简单高效的定时器,很多游戏系统都使用它。
附代码:
timer.h
#ifndef _TIMER_H_
#define _TIMER_H_
#include "typedef.h"
#include "mutex.h"
#include "event.h"
namespace Test
{
typedef struct {
C8 name[ STW_NAME_LENGTH ];
U32 magicTag;
U32 wheelSize;
U32 spokeIndex;
U32 ticks;
U32 granularity;
U32 hiwaterMark;
U32 active;
U32 cancelled;
U32 expired;
U32 starts;
typedef TNodeList<CEvent> SPOKE;
SPOKE spokes[WHEEL_SIZE];
} STW;
struct CEvent;
class CTimer
{
public:
CTimer(U32 wheel_size, U32 granularity, const char* name);
~CTimer(void);
bool TimerRunning(CEvent* tmr);
void TimerPrepare(CEvent* tmr);
void TimerStats(void);
RC_STW TimerStart(CEvent* tmr,U32 delay,U32 perioddelay);
RC_STW TimerStop(CEvent* tmr);
void TimerTick(void);
void Enqueue(CEvent* tmr, U32 delay);
private:
CTimer(void);
STW m_stw;
CMutex m_mutex;
};
}
#endif //_TIMER_H_
timer.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timer.h"
namespace Test
{
CTimer::CTimer(U32 wheelSize, U32 granularity, const char* name)
{
memset(m_stw.name, 0, sizeof(m_stw.name));
if( wheelSize<STW_MIN_WHEEL_SIZE
|| wheelSize>STW_MAX_WHEEL_SIZE )
{
return;
}
if( granularity<STW_MIN_GRANULARITY
|| granularity>STW_MAX_GRANULARITY )
{
return;
}
strcpy(m_stw.name, name);
m_stw.magicTag = MAGIC_TAG;
m_stw.ticks = 0;
m_stw.spokeIndex = 0;
m_stw.granularity = granularity;
m_stw.wheelSize = wheelSize;
m_stw.hiwaterMark = 0;
m_stw.active = 0;
m_stw.cancelled=0;
m_stw.expired=0;
m_stw.starts=0;
}
CTimer::~CTimer(void)
{
if( m_stw.magicTag!=MAGIC_TAG )
{
return;
}
m_stw.magicTag = 0;
}
void CTimer::Enqueue(CEvent* tmr, U32 delay)
{
U32 cursor = 0;
U32 ticks = 0;
U32 td = 0;
LOCK_INTERRUPTS();
if( delay<m_stw.granularity )
{
ticks = 1;
}
else
{
ticks = (delay / m_stw.granularity);
}
td = (ticks % m_stw.wheelSize);
tmr->rotCount = (ticks / m_stw.wheelSize);
cursor = ((m_stw.spokeIndex + td) % m_stw.wheelSize);
m_stw.spokes[cursor].PushNode(tmr);
UNLOCK_INTERRUPTS();
return;
}
void CTimer::TimerStats(void)
{
if( m_stw.magicTag!=MAGIC_TAG )
{
return;
}
printf("\n%s \n", m_stw.name);
printf(" Granularity=%u\n", m_stw.granularity);
printf(" Wheel Size=%u\n", WHEEL_SIZE);
printf(" Tick Count=%u\n", m_stw.ticks);
printf(" Spoke Index=%u\n", m_stw.spokeIndex);
printf(" Active timers=%u\n", m_stw.active);
printf(" Expired timers=%u\n", m_stw.expired);
printf(" Hiwater mark=%u\n", m_stw.hiwaterMark);
printf(" Started timers=%u\n", m_stw.starts);
printf(" Cancelled timers=%u\n", m_stw.cancelled);
return;
}
bool CTimer::TimerRunning(CEvent* tmr)
{
if( tmr==NULL )
{
return false;
}
if( tmr->Next()!=NULL )
{
return true;
}
return false;
}
void CTimer::TimerPrepare(CEvent* tmr)
{
if( tmr )
{
tmr->m_pNext = NULL;
tmr->m_pPrev = NULL;
}
}
RC_STW CTimer::TimerStart(CEvent* tmr, U32 delay, U32 perioddelay)
{
if( tmr==NULL )
{
return (RC_STW_NULL_TMR);
}
if( m_stw.magicTag!=MAGIC_TAG )
{
return (RC_STW_INVALID_WHEEL);
}
LOCK_INTERRUPTS();
if( tmr->Next() )
{
tmr->RemoveFromList();
m_stw.active--;
}
UNLOCK_INTERRUPTS();
tmr->delay = delay;
tmr->priDelay = perioddelay;
Enqueue(tmr, delay);
m_stw.starts++;
m_stw.active++;
if( m_stw.active>m_stw.hiwaterMark )
{
m_stw.hiwaterMark = m_stw.active;
}
return (RC_STW_OK);
}
RC_STW CTimer::TimerStop(CEvent* tmr)
{
if( tmr==NULL )
{
return (RC_STW_NULL_TMR);
}
if( m_stw.magicTag!=MAGIC_TAG )
{
return (RC_STW_INVALID_WHEEL);
}
LOCK_INTERRUPTS();
tmr->RemoveFromList();
TimerPrepare(tmr);
m_stw.active--;
m_stw.cancelled++;
UNLOCK_INTERRUPTS();
return (RC_STW_OK);
}
void CTimer::TimerTick(void)
{
if( m_stw.magicTag!=MAGIC_TAG )
{
return;
}
m_stw.ticks++;
m_stw.spokeIndex = (m_stw.spokeIndex+1) % m_stw.wheelSize;
CEvent* pcur = (CEvent*)m_stw.spokes[m_stw.spokeIndex].Head();
CEvent* pnext = NULL;
while( pcur )
{
if( pcur->rotCount!=0 )
{
pcur->rotCount--;
}
else
{
LOCK_INTERRUPTS();
pnext = (CEvent*)pcur->Next();
pcur->RemoveFromList();
m_stw.active--;
m_stw.expired++;
UNLOCK_INTERRUPTS();
pcur->OnTimerOut();
}
pcur = pnext;
}
return;
}
}