diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
index 9cd94464ea..2b91845bd7 100644
--- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
+++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj
@@ -66,6 +66,7 @@
+
@@ -237,6 +238,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
index 292a02ca42..51db6d1515 100644
--- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
+++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters
@@ -608,6 +608,9 @@
beast_basics\threads
+
+ beast_basics\events
+
@@ -946,6 +949,9 @@
beast_core\time
+
+ beast_basics\events
+
diff --git a/Subtrees/beast/TODO.txt b/Subtrees/beast/TODO.txt
index dd7f0570ad..f69e94d55d 100644
--- a/Subtrees/beast/TODO.txt
+++ b/Subtrees/beast/TODO.txt
@@ -2,6 +2,14 @@
BEAST TODO
--------------------------------------------------------------------------------
+- Implement beast::Bimap?
+
+- Use Bimap for storage in the DeadlineTimer::Manager, to support
+ thousands of timers.
+
+- Think about adding a shouldStop bool to InterruptibleThread, along
+ with a shouldStop () function returning bool, and a stop() method.
+
- Make OwnedArray add routines return a pointer instead of reference
- Tidy up CacheLine, MemoryAlignment
diff --git a/Subtrees/beast/modules/beast_basics/beast_basics.cpp b/Subtrees/beast/modules/beast_basics/beast_basics.cpp
index 76fd7a7e85..05d51a7630 100644
--- a/Subtrees/beast/modules/beast_basics/beast_basics.cpp
+++ b/Subtrees/beast/modules/beast_basics/beast_basics.cpp
@@ -42,6 +42,7 @@ namespace beast
#include "diagnostic/beast_CatchAny.cpp"
+#include "events/beast_DeadlineTimer.cpp"
#include "events/beast_OncePerSecond.cpp"
#include "math/beast_MurmurHash.cpp"
diff --git a/Subtrees/beast/modules/beast_basics/beast_basics.h b/Subtrees/beast/modules/beast_basics/beast_basics.h
index bb533fd6d6..2dea753bb9 100644
--- a/Subtrees/beast/modules/beast_basics/beast_basics.h
+++ b/Subtrees/beast/modules/beast_basics/beast_basics.h
@@ -247,6 +247,7 @@ namespace beast
#include "functor/beast_Function.h"
#include "diagnostic/beast_CatchAny.h"
+#include "events/beast_DeadlineTimer.h"
#include "events/beast_OncePerSecond.h"
#include "math/beast_Math.h"
#include "math/beast_MurmurHash.h"
diff --git a/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.cpp b/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.cpp
new file mode 100644
index 0000000000..671d2ff0dc
--- /dev/null
+++ b/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.cpp
@@ -0,0 +1,247 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+class DeadlineTimer::Manager
+ : public SharedSingleton
+ , public InterruptibleThread::EntryPoint
+{
+private:
+ typedef CriticalSection LockType;
+ typedef List Items;
+
+public:
+ Manager ()
+ : SharedSingleton (SingletonLifetime::persistAfterCreation)
+ , m_shouldStop (false)
+ , m_thread ("DeadlineTimer::Manager")
+ {
+ m_thread.start (this);
+ }
+
+ ~Manager ()
+ {
+ m_shouldStop = true;
+
+ m_thread.interrupt ();
+
+ bassert (m_items.empty ());
+ }
+
+ void activate (DeadlineTimer* timer)
+ {
+ LockType::ScopedLockType lock (m_mutex);
+
+ bassert (! timer->m_isActive);
+
+ insertSorted (*timer);
+ timer->m_isActive = true;
+
+ m_thread.interrupt ();
+ }
+
+ // Okay to call this on an inactive timer.
+ // This can happen naturally based on concurrency.
+ //
+ void deactivate (DeadlineTimer* timer)
+ {
+ LockType::ScopedLockType lock (m_mutex);
+
+ if (timer->m_isActive)
+ {
+ m_items.erase (m_items.iterator_to (*timer));
+
+ timer->m_isActive = false;
+ }
+
+ m_thread.interrupt ();
+ }
+
+ void threadRun ()
+ {
+ while (! m_shouldStop)
+ {
+ Time const currentTime = Time::getCurrentTime ();
+ double seconds = 0;
+
+ {
+ LockType::ScopedLockType lock (m_mutex);
+
+ // Notify everyone whose timer has expired
+ //
+ if (! m_items.empty ())
+ {
+ for (;;)
+ {
+ Items::iterator const iter = m_items.begin ();
+
+ // Has this timer expired?
+ if (iter->m_notificationTime <= currentTime)
+ {
+ // Yes, so call the listener.
+ //
+ // Note that this happens while the lock is held.
+ //
+ iter->m_listener->onDeadlineTimer ();
+
+ // Remove it from the list.
+ m_items.erase (iter);
+
+ // Is the timer recurring?
+ if (iter->m_secondsRecurring > 0)
+ {
+ // Yes so set the timer again.
+ iter->m_notificationTime =
+ currentTime + RelativeTime (iter->m_secondsRecurring);
+
+ // Keep it active.
+ insertSorted (*iter);
+ }
+ else
+ {
+ // Not a recurring timer, deactivate it.
+ iter->m_isActive = false;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ // Figure out how long we need to wait.
+ // This has to be done while holding the lock.
+ //
+ if (! m_items.empty ())
+ {
+ seconds = (m_items.front ().m_notificationTime - currentTime).inSeconds ();
+ }
+ else
+ {
+ seconds = 0;
+ }
+ }
+
+ // Note that we have released the lock here.
+ //
+ if (seconds > 0)
+ {
+ // Wait until interrupt or next timer.
+ //
+ m_thread.wait (static_cast (seconds * 1000 + 0.5));
+ }
+ else if (seconds == 0)
+ {
+ // Wait until interrupt
+ //
+ m_thread.wait ();
+ }
+ else
+ {
+ // Do not wait. This can happen if the recurring timer duration
+ // is extremely short, or if a listener wastes too much time in
+ // their callback.
+ }
+ }
+ }
+
+ // Caller is responsible for locking
+ void insertSorted (DeadlineTimer& item)
+ {
+ if (! m_items.empty ())
+ {
+ Items::iterator before = m_items.begin ();
+
+ for (;;)
+ {
+ if (before->m_notificationTime >= item.m_notificationTime)
+ {
+ m_items.insert (before, item);
+ break;
+ }
+
+ ++before;
+
+ if (before == m_items.end ())
+ {
+ m_items.push_back (item);
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_items.push_back (item);
+ }
+ }
+
+ static Manager* createInstance ()
+ {
+ return new Manager;
+ }
+
+private:
+ CriticalSection m_mutex;
+ bool volatile m_shouldStop;
+ InterruptibleThread m_thread;
+ Items m_items;
+};
+
+//------------------------------------------------------------------------------
+
+DeadlineTimer::DeadlineTimer (Listener* listener)
+ : m_listener (listener)
+ , m_manager (Manager::getInstance ())
+ , m_isActive (false)
+{
+}
+
+DeadlineTimer::~DeadlineTimer ()
+{
+ m_manager->deactivate (this);
+}
+
+void DeadlineTimer::setExpiration (double secondsUntilDeadline)
+{
+ m_secondsRecurring = 0;
+ m_notificationTime = Time::getCurrentTime () + RelativeTime (secondsUntilDeadline);
+
+ m_manager->activate (this);
+}
+
+void DeadlineTimer::setRecurringExpiration (double secondsUntilDeadline)
+{
+ m_secondsRecurring = secondsUntilDeadline;
+ m_notificationTime = Time::getCurrentTime () + RelativeTime (secondsUntilDeadline);
+
+ m_manager->activate (this);
+}
+
+void DeadlineTimer::setExpirationTime (Time absoluteDeadline)
+{
+ m_secondsRecurring = 0;
+ m_notificationTime = absoluteDeadline;
+
+ m_manager->activate (this);
+}
+
+void DeadlineTimer::reset ()
+{
+ m_manager->deactivate (this);
+}
diff --git a/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.h b/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.h
new file mode 100644
index 0000000000..cc4d2d631f
--- /dev/null
+++ b/Subtrees/beast/modules/beast_basics/events/beast_DeadlineTimer.h
@@ -0,0 +1,77 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef BEAST_DEADLINETIMER_H_INCLUDED
+#define BEAST_DEADLINETIMER_H_INCLUDED
+
+/** Provides periodic or one time notifications at a specified time interval.
+*/
+class DeadlineTimer : public List ::Node
+{
+public:
+ /** Listener for a deadline timer.
+
+ The listener is called on an auxiliary thread. It is suggested
+ not to perform any time consuming operations during the call.
+ */
+ // VFALCO TODO Allow construction with a specific ThreadWithCallQueue&
+ // on which to notify the listener.
+ class Listener
+ {
+ public:
+ virtual void onDeadlineTimer () { }
+ };
+
+public:
+ /** Create a deadline timer with the specified listener attached.
+ */
+ explicit DeadlineTimer (Listener* listener);
+
+ ~DeadlineTimer ();
+
+ /** Set the timer to go off once in the future.
+ */
+ void setExpiration (double secondsUntilDeadline);
+
+ /** Set the timer to go off repeatedly with the specified frequency.
+ */
+ void setRecurringExpiration (double secondsUntilDeadline);
+
+ /** Set the timer to go off at a specific time.
+
+ @note If the time is in the past, the timer will go off
+ immediately.
+ */
+ void setExpirationTime (Time absoluteDeadline);
+
+ /** Reset the timer so that no more notifications are sent.
+ */
+ void reset ();
+
+private:
+ class Manager;
+
+ Listener* const m_listener;
+ ReferenceCountedObjectPtr m_manager;
+ bool m_isActive;
+ Time m_notificationTime;
+ double m_secondsRecurring; // non zero if recurring
+};
+
+#endif
diff --git a/Subtrees/beast/modules/beast_basics/events/beast_OncePerSecond.cpp b/Subtrees/beast/modules/beast_basics/events/beast_OncePerSecond.cpp
index 7f5bbda7e6..6b436ec51c 100644
--- a/Subtrees/beast/modules/beast_basics/events/beast_OncePerSecond.cpp
+++ b/Subtrees/beast/modules/beast_basics/events/beast_OncePerSecond.cpp
@@ -41,7 +41,7 @@ private:
{
for (;;)
{
- const bool interrupted = m_thread.wait (1000);
+ bool const interrupted = m_thread.wait (1000);
if (interrupted)
break;
diff --git a/Subtrees/beast/modules/beast_basics/threads/beast_ThreadWithCallQueue.h b/Subtrees/beast/modules/beast_basics/threads/beast_ThreadWithCallQueue.h
index ef6f153b6b..1db2021f76 100644
--- a/Subtrees/beast/modules/beast_basics/threads/beast_ThreadWithCallQueue.h
+++ b/Subtrees/beast/modules/beast_basics/threads/beast_ThreadWithCallQueue.h
@@ -114,18 +114,17 @@ public:
void stop (bool const wait);
- /**
- Determine if the thread needs interruption.
+ /** Determine if the thread needs interruption.
- Should be called periodically by the idle function. If interruptionPoint
- returns true or throws, it must not be called again until the idle function
- returns and is re-entered.
+ Should be called periodically by the idle function. If interruptionPoint
+ returns true or throws, it must not be called again until the idle function
+ returns and is re-entered.
- @invariant No previous calls to interruptionPoint() made after the idle
- function entry point returned `true`.
+ @invariant No previous calls to interruptionPoint() made after the idle
+ function entry point returned `true`.
- @return `false` if the idle function may continue, or `true` if the
- idle function must return as soon as possible.
+ @return `false` if the idle function may continue, or `true` if the
+ idle function must return as soon as possible.
*/
bool interruptionPoint ();