[Box Backup-commit] COMMIT r1141 - in box/chris/merge: lib/common test/common

boxbackup-dev@fluffy.co.uk boxbackup-dev@fluffy.co.uk
Mon, 06 Nov 2006 20:43:12 +0000


Author: chris
Date: 2006-11-06 20:43:12 +0000 (Mon, 06 Nov 2006)
New Revision: 1141

Added:
   box/chris/merge/lib/common/Timer.cpp
   box/chris/merge/lib/common/Timer.h
Modified:
   box/chris/merge/lib/common/BoxTime.h
   box/chris/merge/test/common/testcommon.cpp
Log:
Added generic timer support class


Modified: box/chris/merge/lib/common/BoxTime.h
===================================================================
--- box/chris/merge/lib/common/BoxTime.h	2006-11-06 20:40:46 UTC (rev 1140)
+++ box/chris/merge/lib/common/BoxTime.h	2006-11-06 20:43:12 UTC (rev 1141)
@@ -35,6 +35,9 @@
 {
 	return Time / MILLI_SEC_IN_NANO_SEC_LL;
 }
+inline uint64_t BoxTimeToMicroSeconds(box_time_t Time)
+{
+	return Time;
+}
 
 #endif // BOXTIME__H
-

Copied: box/chris/merge/lib/common/Timer.cpp (from rev 966, box/chris/merge/lib/common/WaitForEvent.cpp)
===================================================================
--- box/chris/merge/lib/common/Timer.cpp	                        (rev 0)
+++ box/chris/merge/lib/common/Timer.cpp	2006-11-06 20:43:12 UTC (rev 1141)
@@ -0,0 +1,252 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    Timer.cpp
+//		Purpose: Generic timers which execute arbitrary code when
+//			 they expire.
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <signal.h>
+
+#include "Timer.h"
+
+#include "MemLeakFindOn.h"
+
+std::vector<Timer*> Timers::sTimers;
+bool Timers::sInitialised = false;
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void TimerSigHandler(int)
+//		Purpose: Signal handler, notifies Timers class
+//		Created: 19/3/04
+//
+// --------------------------------------------------------------------------
+static void TimerSigHandler(int iUnused)
+{
+	Timers::Signal();	
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Init()
+//		Purpose: Initialise timers, prepare signal handler
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Init()
+{
+	ASSERT(!sInitialised);
+	
+	#ifdef PLATFORM_CYGWIN
+		ASSERT(::signal(SIGALRM, TimerSigHandler) == 0);
+	#elif defined WIN32
+		// no support for signals at all
+		InitTimer();
+		SetTimerHandler(TimerSigHandler);
+	#else
+		ASSERT(::signal(SIGALRM, TimerSigHandler) == 0);
+	#endif // PLATFORM_CYGWIN
+	
+	sInitialised = true;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Cleanup()
+//		Purpose: Clean up timers, stop signal handler
+//		Created: 6/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Cleanup()
+{
+	ASSERT(sInitialised);
+	
+	#ifdef PLATFORM_CYGWIN
+		ASSERT(::signal(SIGALRM, NULL) == TimerSigHandler);
+	#elif defined WIN32
+		// no support for signals at all
+		SetTimerHandler(NULL);
+		FiniTimer();
+	#else
+		ASSERT(::signal(SIGALRM, NULL) == TimerSigHandler);
+	#endif // PLATFORM_CYGWIN
+
+	sTimers.clear();
+	
+	sInitialised = false;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Add(Timer&)
+//		Purpose: Add a new timer to the set, and reschedule next wakeup
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Add(Timer& rTimer)
+{
+	ASSERT(sInitialised);
+	sTimers.push_back(&rTimer);
+	Reschedule();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Remove(Timer&)
+//		Purpose: Removes the timer from the set (preventing it from
+//			 being called) and reschedule next wakeup
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Remove(Timer& rTimer)
+{
+	ASSERT(sInitialised);
+
+	bool restart = true;
+	while (restart)
+	{
+		restart = false;
+		for (std::vector<Timer*>::iterator i = sTimers.begin();
+			i != sTimers.end(); i++)
+		{
+			if (&rTimer == *i)
+			{
+				sTimers.erase(i);
+				restart = true;
+				break;
+			}
+		}
+	}
+		
+	Reschedule();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Reschedule()
+//		Purpose: Recalculate when the next wakeup is due
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Reschedule()
+{
+	ASSERT(sInitialised);
+
+	box_time_t timeNow = GetCurrentBoxTime();
+	box_time_t timeToNextEvent = 0;
+	
+	for (std::vector<Timer*>::iterator i = sTimers.begin();
+		i != sTimers.end(); i++)
+	{
+		Timer& rTimer = **i;
+		ASSERT(!rTimer.HasExpired());
+		
+		box_time_t timeToExpiry = rTimer.GetExpiryTime() - timeNow;
+		
+		if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry)
+		{
+			timeToNextEvent = timeToExpiry;
+		}
+	}
+	
+	ASSERT(timeToNextEvent >= 0);
+	
+	struct itimerval timeout;
+	memset(&timeout, 0, sizeof(timeout));
+	
+	timeout.it_value.tv_sec  = BoxTimeToSeconds(timeToNextEvent);
+	timeout.it_value.tv_usec = (int)
+		(BoxTimeToMicroSeconds(timeToNextEvent) % MICRO_SEC_IN_SEC);
+
+#ifdef PLATFORM_CYGWIN
+	if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
+#else
+	if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
+#endif // PLATFORM_CYGWIN
+	{
+		TRACE0("WARNING: couldn't initialise timer\n");
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static void Timers::Signal()
+//		Purpose: Called by signal handler. Signals any timers which
+//			 are due or overdue, removes them from the set,
+//			 and reschedules next wakeup.
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Signal()
+{
+	ASSERT(sInitialised);
+
+	box_time_t timeNow = GetCurrentBoxTime();
+
+	std::vector<Timer*> timersCopy = sTimers;
+	
+	for (std::vector<Timer*>::iterator i = timersCopy.begin();
+		i != timersCopy.end(); i++)
+	{
+		Timer& rTimer = **i;
+		ASSERT(!rTimer.HasExpired());
+		
+		box_time_t timeToExpiry = rTimer.GetExpiryTime() - timeNow;
+		
+		if (timeToExpiry <= 0)
+		{
+			rTimer.OnExpire();
+		}
+	}		
+		
+	Reschedule();
+}
+
+Timer::Timer(size_t timeoutSecs)
+: mExpires(GetCurrentBoxTime() + SecondsToBoxTime(timeoutSecs)),
+  mExpired(false)
+{
+	Timers::Add(*this);
+}
+
+Timer::~Timer()
+{
+	Timers::Remove(*this);
+}
+
+Timer::Timer(const Timer& rToCopy)
+: mExpires(rToCopy.mExpires),
+  mExpired(rToCopy.mExpired)
+{
+	Timers::Add(*this);
+}
+
+Timer& Timer::operator=(const Timer& rToCopy)
+{
+	Timers::Remove(*this);
+	mExpires = rToCopy.mExpires;
+	mExpired = rToCopy.mExpired;
+	if (!mExpired)
+	{
+		Timers::Add(*this);
+	}
+}
+
+void Timer::OnExpire()
+{
+	mExpired = true;
+	Timers::Remove(*this);
+}

Copied: box/chris/merge/lib/common/Timer.h (from rev 966, box/chris/merge/lib/common/WaitForEvent.h)
===================================================================
--- box/chris/merge/lib/common/Timer.h	                        (rev 0)
+++ box/chris/merge/lib/common/Timer.h	2006-11-06 20:43:12 UTC (rev 1141)
@@ -0,0 +1,65 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    Timer.h
+//		Purpose: Generic timers which execute arbitrary code when
+//			 they expire.
+//		Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+
+#ifndef TIMER__H
+#define TIMER__H
+
+#include <sys/time.h>
+
+#include <vector>
+
+#include "MemLeakFindOn.h"
+#include "BoxTime.h"
+
+class Timer
+{
+public:
+	Timer(size_t timeoutSecs);
+	virtual ~Timer();
+	Timer(const Timer &);
+	Timer &operator=(const Timer &);
+
+public:
+	box_time_t   GetExpiryTime() { return mExpires; }
+	bool         HasExpired   () { return mExpired; }
+	virtual void OnExpire     ();
+	
+private:
+	box_time_t mExpires;
+	bool       mExpired;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    Timers
+//		Purpose: Static class to manage all timers and arrange 
+//			 efficient delivery of wakeup signals
+//		Created: 19/3/04
+//
+// --------------------------------------------------------------------------
+class Timers
+{
+	private:
+	static std::vector<Timer*> sTimers;
+	static bool sInitialised;
+	static void Reschedule();
+	
+	public:
+	static void Init();
+	static void Cleanup();
+	static void Add   (Timer& rTimer);
+	static void Remove(Timer& rTimer);
+	static void Signal();
+};
+
+#include "MemLeakFindOff.h"
+
+#endif // TIMER__H

Modified: box/chris/merge/test/common/testcommon.cpp
===================================================================
--- box/chris/merge/test/common/testcommon.cpp	2006-11-06 20:40:46 UTC (rev 1140)
+++ box/chris/merge/test/common/testcommon.cpp	2006-11-06 20:43:12 UTC (rev 1141)
@@ -27,6 +27,7 @@
 #include "autogen_ConversionException.h"
 #include "CollectInBufferStream.h"
 #include "Archive.h"
+#include "Timer.h"
 
 #include "MemLeakFindOn.h"
 
@@ -204,7 +205,57 @@
 	}
 #endif // BOX_MEMORY_LEAK_TESTING
 	
+	// Check that using timer methods without initialisation
+	// throws an exception
+	TEST_CHECK_THROWS(Timers::Add(*(Timer*)NULL), 
+		CommonException, AssertFailed);
+	TEST_CHECK_THROWS(Timers::Remove(*(Timer*)NULL), 
+		CommonException, AssertFailed);
+	TEST_CHECK_THROWS(Timers::Signal(), CommonException, AssertFailed);
+	TEST_CHECK_THROWS(Timers::Cleanup(), CommonException, AssertFailed);
+	
+	// Check that we can initialise the timers
+	Timers::Init();
+	
+	// Check that double initialisation throws an exception
+	TEST_CHECK_THROWS(Timers::Init(), CommonException, AssertFailed);
 
+	// Check that we can clean up the timers
+	Timers::Cleanup();
+	
+	// Check that double cleanup throws an exception
+	TEST_CHECK_THROWS(Timers::Cleanup(), CommonException, AssertFailed);
+
+	Timers::Init();
+
+	Timer t1(1);
+	Timer t2(2);
+	Timer t3(3);
+	
+	TEST_THAT(!t1.HasExpired());
+	TEST_THAT(!t2.HasExpired());
+	TEST_THAT(!t3.HasExpired());
+	
+	sleep(1);
+	TEST_THAT(t1.HasExpired());
+	TEST_THAT(!t2.HasExpired());
+	TEST_THAT(!t3.HasExpired());
+	
+	sleep(1);
+	TEST_THAT(t1.HasExpired());
+	TEST_THAT(t2.HasExpired());
+	TEST_THAT(!t3.HasExpired());
+	
+	t1 = Timer(1);
+	t2 = Timer(2);
+	TEST_THAT(!t1.HasExpired());
+	TEST_THAT(!t2.HasExpired());
+	
+	sleep(1);
+	TEST_THAT(t1.HasExpired());
+	TEST_THAT(!t2.HasExpired());
+	TEST_THAT(t3.HasExpired());
+
 	static char *testfilelines[] =
 	{
 		"First line",