summaryrefslogtreecommitdiff
path: root/tools/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/timer.c')
-rw-r--r--tools/timer.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/tools/timer.c b/tools/timer.c
new file mode 100644
index 00000000..1730edab
--- /dev/null
+++ b/tools/timer.c
@@ -0,0 +1,292 @@
+/*
+ * timer.c: Threaded timer class
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: timer.c,v 1.1 2006-06-03 10:04:28 phintuka Exp $
+ *
+ */
+
+#include <sys/time.h>
+
+#include <vdr/config.h>
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "timer.h"
+
+//#define XINELIBOUTPUT_DEBUG
+//#define XINELIBOUTPUT_DEBUG_STDOUT
+#ifdef XINELIBOUTPUT_DEBUG
+# include "logdefs.h"
+#else
+# define TRACE(x)
+# define TRACEF(x)
+#endif
+
+#if VDRVERSNUM>10317
+ #define time_ms() cTimeMs::Now()
+#endif
+
+// ---------------------------- cTimerThreadEvent ----------------------------
+
+class cTimerThreadEvent : public cListObject {
+ public:
+ cTimerThreadEvent(cTimerCallback *Handler, unsigned int TimeoutMs,
+ bool DeleteOnCancel = false) :
+ m_Handler(Handler),
+ m_DeleteOnCancel(DeleteOnCancel),
+ m_TimeoutMs(TimeoutMs)
+ {
+ m_NextEventTime = time_ms();
+ UpdateEventTime();
+ }
+
+ ~cTimerThreadEvent()
+ {
+ if(m_DeleteOnCancel && m_Handler)
+ delete m_Handler;
+ }
+
+ void UpdateEventTime()
+ {
+ m_NextEventTime += m_TimeoutMs;
+ }
+
+ int TimeToNextEvent(void)
+ {
+ return m_NextEventTime - time_ms();
+ }
+
+ virtual bool operator< (const cListObject &ListObject)
+ {
+ const cTimerThreadEvent *o = (cTimerThreadEvent *)&ListObject;
+ return m_NextEventTime<o->m_NextEventTime;
+ }
+
+ virtual int Compare(const cListObject &ListObject) const
+ {
+ const cTimerThreadEvent *o = (cTimerThreadEvent *)&ListObject;
+ if(m_NextEventTime<o->m_NextEventTime)
+ return -1;
+ else if(m_NextEventTime>o->m_NextEventTime)
+ return 1;
+ return 0;
+ }
+
+ cTimerCallback *m_Handler;
+
+ protected:
+ bool m_DeleteOnCancel;
+ unsigned int m_TimeoutMs;
+ int64_t m_NextEventTime;
+};
+
+// ------------------------------- cTimerThread ------------------------------
+
+class cTimerThread : public cThread {
+ private:
+ cTimerThread(cTimerThread&); // copy not allowed
+
+ static cMutex m_InstanceLock;
+ static cTimerThread *m_Instance; // singleton
+
+ cMutex m_Lock;
+ cCondVar m_Signal;
+ cList<cTimerThreadEvent> m_Events;
+ cTimerThreadEvent *m_RunningEvent;
+ bool m_Finished;
+ bool m_HandlerRunning;
+
+ cTimerThread() :
+ m_RunningEvent(NULL),
+ m_Finished(false),
+ m_HandlerRunning(false)
+ {
+ }
+
+ virtual ~cTimerThread()
+ {
+ m_Lock.Lock();
+ cTimerThreadEvent *ev;
+ while(NULL != (ev = m_Events.First())) {
+ m_Events.Del(ev,true);
+ }
+ m_Lock.Unlock();
+ m_Signal.Broadcast();
+ Cancel(1);
+ }
+
+ protected:
+
+ virtual void Action()
+ {
+ TRACEF("cTimerThread::Action");
+ m_Lock.Lock();
+ while(m_Events.First()) {
+ m_Signal.TimedWait(m_Lock,
+ max(1, m_Events.First()->TimeToNextEvent()));
+ TRACE("cTimerThread::Action waked up");
+ while(NULL != (m_RunningEvent = m_Events.First()) &&
+ m_RunningEvent->TimeToNextEvent() <= 0) {
+ TRACE("cTimerThread::Action calling handler");
+ m_HandlerRunning=true;
+// m_Lock.Unlock();
+// - can't unlock or running timer handler may be deleted while
+// executing (or thread may be killed by Delete)
+ bool result = m_RunningEvent->m_Handler->TimerEvent();
+// m_Lock.Lock();
+ m_HandlerRunning=false;
+ if(!result) {
+ if(m_RunningEvent) { // check if event was cancelled in handler...
+ TRACE("cTimerThread::Action handler cancelled timer");
+ m_Events.Del(m_RunningEvent, true);
+ }
+ } else {
+ if(m_RunningEvent) {
+ TRACE("cTimerThread::Action timer re-scheduled");
+ m_RunningEvent->UpdateEventTime();
+ m_Events.Sort();
+ }
+ }
+ m_RunningEvent = NULL;
+ }
+ }
+ m_Finished = true;
+ m_Lock.Unlock();
+ }
+
+ void Add(cTimerThreadEvent *Event)
+ {
+ TRACEF("cTimerThread::Add");
+ //m_Events.Del(Event, false);
+ Event->Unlink();
+ Del(Event->m_Handler);
+ m_Events.Add(Event);
+ m_Events.Sort();
+ }
+
+ bool Del(cTimerCallback *Handler, void *TargetId=NULL,
+ bool inDestructor=false)
+ {
+ TRACEF("cTimerThread::Del");
+ cTimerThreadEvent *ev = m_Events.First();
+ while(ev) {
+ if(ev->m_Handler == Handler ||
+ (TargetId && ev->m_Handler->TargetId() == TargetId) ||
+ (Handler && ev->m_Handler->is(Handler,Handler->size()))) {
+ cTimerThreadEvent *nev = m_Events.Next(ev);
+ if(inDestructor) ev->m_Handler=NULL;
+ m_Events.Del(ev, true);
+ ev = nev;
+ } else
+ ev = m_Events.Next(ev);
+ }
+ if(m_RunningEvent &&
+ (m_RunningEvent->m_Handler == Handler ||
+ m_RunningEvent->m_Handler->TargetId() == TargetId))
+ m_RunningEvent = NULL;
+ return !m_HandlerRunning && !m_RunningEvent && !m_Events.First();
+ }
+
+ public:
+
+ static void AddEvent(cTimerCallback *Handler, unsigned int TimeoutMs,
+ bool DeleteOnCancel=false)
+ {
+ TRACEF("cTimerThread::AddEvent");
+ m_InstanceLock.Lock();
+ if(m_Instance && m_Instance->m_Finished) {
+ delete m_Instance;
+ m_Instance = NULL;
+ }
+ if(!m_Instance) {
+ m_Instance = new cTimerThread;
+ m_Instance->m_Lock.Lock();
+ m_Instance->Start();
+ } else {
+ m_Instance->m_Lock.Lock();
+ m_Instance->m_Signal.Broadcast();
+ }
+ m_Instance->Add(new cTimerThreadEvent(Handler, max(1U,TimeoutMs),
+ DeleteOnCancel));
+ m_Instance->m_Lock.Unlock();
+ m_InstanceLock.Unlock();
+ }
+
+ static void CancelEvent(cTimerCallback *Handler, void *TargetId = NULL,
+ bool inDestructor=false)
+ {
+ TRACEF("cTimerThread::CancelEvent");
+ m_InstanceLock.Lock();
+ if(m_Instance && !m_Instance->m_Finished) {
+ m_Instance->m_Lock.Lock();
+ if(m_Instance->Del(Handler, TargetId, inDestructor) && !inDestructor) {
+ m_Instance->m_Lock.Unlock();
+ delete m_Instance;
+ m_Instance = NULL;
+ } else
+ m_Instance->m_Lock.Unlock();
+ }
+ m_InstanceLock.Unlock();
+ }
+
+};
+
+cMutex cTimerThread::m_InstanceLock;
+cTimerThread *cTimerThread::m_Instance = NULL;
+
+// ------------------------------ cTimerCallback -----------------------------
+
+cTimerCallback::~cTimerCallback()
+{
+ TRACEF("cTimerCallback::~cTimerCallback");
+ cTimerThread::CancelEvent(this, NULL, true);
+}
+
+void cTimerCallback::Set(cTimerCallback *handler, unsigned int TimeoutMs)
+{
+ TRACEF("cTimerCallback::Set");
+ cTimerThread::AddEvent(handler, TimeoutMs);
+}
+
+void cTimerCallback::Cancel(cTimerCallback *handler)
+{
+ TRACEF("cTimerCallback::Cancel");
+ cTimerThread::CancelEvent(handler);
+}
+
+// ------------------------------- cTimerEvent -------------------------------
+
+//cTimerEvent::cTimerEvent(unsigned int TimeoutMs)
+//{
+// TRACEF("cTimerEvent::cTimerEvent");
+//// cTimerThread::AddEvent(this, TimeoutMs, true);
+//}
+
+void cTimerEvent::AddEvent(unsigned int TimeoutMs)
+{
+ TRACEF("cTimerEvent::AddEvent");
+ cTimerThread::AddEvent(this, TimeoutMs, true);
+}
+
+void cTimerEvent::Cancel(cTimerEvent *&event)
+{
+ TRACEF("cTimerEvent::Cancel");
+ cTimerThread::CancelEvent(event);
+ event = NULL;
+}
+
+void cTimerEvent::CancelAll(void *Target)
+{
+ TRACEF("cTimerEvent::CancelAll");
+ cTimerThread::CancelEvent(NULL, Target);
+}
+
+
+
+
+
+
+