|
@@ -0,0 +1,305 @@
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+
|
|
|
+#include <time.h>
|
|
|
+#include <errno.h>
|
|
|
+#include "timer.h"
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+#ifdef _DEBUG
|
|
|
+#define _VALIDATE_TIMER_RET(tnum, ret) if(!ValidateTimer(tnum, __FUNCTION__)) return (ret)
|
|
|
+#define _VALIDATE_TIMER_VOID(tnum) if(!ValidateTimer(tnum, __FUNCTION__)) return
|
|
|
+#define TRACE printf
|
|
|
+#else // _DEBUG
|
|
|
+#define _VALIDATE_TIMER_RET(tnum, ret)
|
|
|
+#define _VALIDATE_TIMER_VOID(tnum)
|
|
|
+#define TRACE(...)
|
|
|
+#endif // _DEBUG
|
|
|
+
|
|
|
+#define _GFA_DEFAULT_TIMER_COUNT 32
|
|
|
+#define _GFA_MAX_TIMER_COUNT 256
|
|
|
+#define _COUNTOF(a) (sizeof(a) / sizeof(*a))
|
|
|
+#define _INVALID_THREAD_ID 0
|
|
|
+#define _NANOSECS_PER_SEC 1000000000
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+const int CGfaTimer::m_nPulseFreq[] = {250, 500, 750, 1000, 1500, 2000, 5000, 10000};
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+CGfaTimer::CGfaTimer(void) : m_pTimer(NULL), m_nTimerCount(0), m_nTimerIncrement(0), m_nClockCount(0), m_nClockPulse(0), m_threadID(_INVALID_THREAD_ID), m_bProcessTimers(false)
|
|
|
+{
|
|
|
+ memset(&m_timInterval, 0, sizeof(m_timInterval));
|
|
|
+
|
|
|
+ if(!m_mutex.Create())
|
|
|
+ {
|
|
|
+ TRACE("CGfaTimer::CGfaTimer: failed to create mutex!\n");
|
|
|
+ throw -1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+CGfaTimer::~CGfaTimer(void)
|
|
|
+{
|
|
|
+ Release();
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+int CGfaTimer::Initialize(int nTimerCount, GfaTimerResolution res)
|
|
|
+{
|
|
|
+ if(!m_pTimer)
|
|
|
+ {
|
|
|
+ if(nTimerCount == 0)
|
|
|
+ nTimerCount = _GFA_DEFAULT_TIMER_COUNT;
|
|
|
+ else if(nTimerCount < 0)
|
|
|
+ {
|
|
|
+ TRACE("CGfaTimer::Initialize: invalid timer count: %d!\n", nTimerCount);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else if(nTimerCount > _GFA_MAX_TIMER_COUNT)
|
|
|
+ nTimerCount = _GFA_MAX_TIMER_COUNT;
|
|
|
+
|
|
|
+ switch(res)
|
|
|
+ {
|
|
|
+ case GTR_10ms:
|
|
|
+ m_timInterval.tv_sec = 0;
|
|
|
+ m_timInterval.tv_nsec = 10000000;
|
|
|
+ m_nTimerIncrement = 10;
|
|
|
+ break;
|
|
|
+ case GTR_25ms:
|
|
|
+ m_timInterval.tv_sec = 0;
|
|
|
+ m_timInterval.tv_nsec = 25000000;
|
|
|
+ m_nTimerIncrement = 25;
|
|
|
+ break;
|
|
|
+ case GTR_50ms:
|
|
|
+ m_timInterval.tv_sec = 0;
|
|
|
+ m_timInterval.tv_nsec = 50000000;
|
|
|
+ m_nTimerIncrement = 50;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ TRACE("CGfaTimer::Initialize: invalid timer resolution: %d!\n", (int)res);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_pTimer = new GFA_TIMER[nTimerCount];
|
|
|
+ memset(m_pTimer, 0, sizeof(GFA_TIMER) * nTimerCount);
|
|
|
+ m_nTimerCount = nTimerCount;
|
|
|
+ m_bProcessTimers = true;
|
|
|
+
|
|
|
+ if(::pthread_create(&m_threadID, NULL, &CGfaTimer::LordOfTheTimers, reinterpret_cast<void*>(this)))
|
|
|
+ {
|
|
|
+ TRACE("CGfaTimer::Initialize: Failed to create timer thread!\n");
|
|
|
+ Release();
|
|
|
+ }
|
|
|
+
|
|
|
+ TRACE("CGfaTimer::Initialize: Success!\n");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ TRACE("CGfaTimer::Initialize: Already initialized!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return m_nTimerCount;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::Release(void)
|
|
|
+{
|
|
|
+ if(m_pTimer)
|
|
|
+ {
|
|
|
+ if(m_threadID != _INVALID_THREAD_ID)
|
|
|
+ {
|
|
|
+ m_bProcessTimers = false;
|
|
|
+ ::pthread_join(m_threadID, NULL);
|
|
|
+ m_threadID = _INVALID_THREAD_ID;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_nTimerCount = m_nTimerIncrement = m_nClockCount = m_nClockPulse = 0;
|
|
|
+ delete [] m_pTimer;
|
|
|
+ m_pTimer = NULL;
|
|
|
+ TRACE("CGfaTimer::Release: Timer released.\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+bool CGfaTimer::ValidateTimer(int tnum, const char *pszCaller)
|
|
|
+{
|
|
|
+ if(!m_pTimer)
|
|
|
+ {
|
|
|
+ TRACE("%s: Timer not initialized!\n", pszCaller);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((tnum < 0) || (tnum >= m_nTimerCount))
|
|
|
+ {
|
|
|
+ TRACE("%s: Invalid Timer number: %d\n", pszCaller, tnum);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+int CGfaTimer::tf_test(int tnum)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_RET(tnum, 0);
|
|
|
+ m_mutex.Lock();
|
|
|
+ int nRet = m_pTimer[tnum].tf;
|
|
|
+ m_mutex.Unlock();
|
|
|
+ return nRet;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::tf_set(int tnum)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_VOID(tnum);
|
|
|
+ m_mutex.Lock();
|
|
|
+ m_pTimer[tnum].tf = 1;
|
|
|
+ m_mutex.Unlock();
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::tf_clear(int tnum)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_VOID(tnum);
|
|
|
+ m_mutex.Lock();
|
|
|
+ m_pTimer[tnum].tf = 0;
|
|
|
+ m_mutex.Unlock();
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::tf_store(int tnum, int fset)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_VOID(tnum);
|
|
|
+ if(fset)
|
|
|
+ {
|
|
|
+ m_mutex.Lock();
|
|
|
+ m_pTimer[tnum].tf = 1;
|
|
|
+ m_mutex.Unlock();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ m_mutex.Lock();
|
|
|
+ m_pTimer[tnum].tf = 0;
|
|
|
+ m_mutex.Unlock();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+unsigned long CGfaTimer::tw_read(int tnum)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_RET(tnum, 0);
|
|
|
+ m_mutex.Lock();
|
|
|
+ unsigned long nRet = m_pTimer[tnum].tw;
|
|
|
+ m_mutex.Unlock();
|
|
|
+ return nRet;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::tw_set(int tnum, unsigned long val)
|
|
|
+{
|
|
|
+ _VALIDATE_TIMER_VOID(tnum);
|
|
|
+ m_mutex.Lock();
|
|
|
+ m_pTimer[tnum].tw = val;
|
|
|
+ m_mutex.Unlock();
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+int CGfaTimer::cp_test(GfaTimerClockPulse cp)
|
|
|
+{
|
|
|
+ if(cp >= GTCP_250ms && cp <= GTCP_10000ms)
|
|
|
+ {
|
|
|
+ return !!(m_nClockPulse & (0x00000001 << cp));
|
|
|
+ }
|
|
|
+
|
|
|
+ TRACE("CGfaTimer::cp_test: Invalid clock pulse: %d.\n", (int)cp);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void CGfaTimer::AddTimeSpecs(const struct timespec &ts1, const struct timespec &ts2, struct timespec &tsResult)
|
|
|
+{
|
|
|
+ long ns = ts1.tv_nsec + ts2.tv_nsec;
|
|
|
+ tsResult.tv_sec = ts1.tv_sec + ts2.tv_sec + (time_t)ns / _NANOSECS_PER_SEC;
|
|
|
+ tsResult.tv_nsec = ns % _NANOSECS_PER_SEC;
|
|
|
+}
|
|
|
+
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+/////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void* CGfaTimer::LordOfTheTimers(void *pParam)
|
|
|
+{
|
|
|
+ int nRet;
|
|
|
+ struct timespec tsDueTime;
|
|
|
+ CGfaTimer* pSelf = reinterpret_cast<CGfaTimer*>(pParam);
|
|
|
+ TRACE("CGfaTimer::LordOfTheTimers: thread enter.\n");
|
|
|
+
|
|
|
+ ::clock_gettime(CLOCK_MONOTONIC, &tsDueTime);
|
|
|
+
|
|
|
+ do
|
|
|
+ {
|
|
|
+ for(int i = 0; i < pSelf->m_nTimerCount; i++)
|
|
|
+ {
|
|
|
+ GFA_TIMER &rt = pSelf->m_pTimer[i];
|
|
|
+
|
|
|
+ pSelf->m_mutex.Lock();
|
|
|
+ if(rt.tf)
|
|
|
+ {
|
|
|
+ if(rt.tw >= pSelf->m_nTimerIncrement)
|
|
|
+ rt.tw -= pSelf->m_nTimerIncrement;
|
|
|
+ else if(rt.tw)
|
|
|
+ rt.tw = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ rt.tw = 0;
|
|
|
+ }
|
|
|
+ pSelf->m_mutex.Unlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ pSelf->m_nClockCount += pSelf->m_nTimerIncrement;
|
|
|
+
|
|
|
+ if(!(pSelf->m_nClockCount % CGfaTimer::m_nPulseFreq[0]))
|
|
|
+ {
|
|
|
+ unsigned long nPulseMask = 1;
|
|
|
+ pSelf->m_nClockPulse ^= nPulseMask;
|
|
|
+
|
|
|
+ for(size_t i = 1; i < _COUNTOF(CGfaTimer::m_nPulseFreq); i++)
|
|
|
+ {
|
|
|
+ nPulseMask <<= 1;
|
|
|
+
|
|
|
+ if(!(pSelf->m_nClockCount % CGfaTimer::m_nPulseFreq[i]))
|
|
|
+ {
|
|
|
+ pSelf->m_nClockPulse ^= nPulseMask;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ CGfaTimer::AddTimeSpecs(tsDueTime, pSelf->m_timInterval, tsDueTime);
|
|
|
+
|
|
|
+ if((nRet = ::clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tsDueTime, NULL)))
|
|
|
+ {
|
|
|
+ while(nRet && (errno == EINTR))
|
|
|
+ {
|
|
|
+ errno = 0;
|
|
|
+ nRet = ::clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tsDueTime, NULL);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ while(pSelf->m_bProcessTimers);
|
|
|
+
|
|
|
+ TRACE("CGfaTimer::LordOfTheTimers: thread exit.\n");
|
|
|
+ return NULL;
|
|
|
+}
|