mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
Add DeadlineTimer class
This commit is contained in:
@@ -66,6 +66,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\modules\beast_basics\beast_basics.h" />
|
||||
<ClInclude Include="..\..\modules\beast_basics\diagnostic\beast_CatchAny.h" />
|
||||
<ClInclude Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.h" />
|
||||
<ClInclude Include="..\..\modules\beast_basics\events\beast_OncePerSecond.h" />
|
||||
<ClInclude Include="..\..\modules\beast_basics\functor\beast_Function.h" />
|
||||
<ClInclude Include="..\..\modules\beast_basics\math\beast_Math.h" />
|
||||
@@ -237,6 +238,12 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\beast_basics\events\beast_OncePerSecond.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
|
||||
@@ -608,6 +608,9 @@
|
||||
<ClInclude Include="..\..\modules\beast_basics\threads\beast_SharedData.h">
|
||||
<Filter>beast_basics\threads</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.h">
|
||||
<Filter>beast_basics\events</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\modules\beast_core\beast_core.cpp">
|
||||
@@ -946,6 +949,9 @@
|
||||
<ClCompile Include="..\..\modules\beast_core\time\beast_PerformedAtExit.cpp">
|
||||
<Filter>beast_core\time</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.cpp">
|
||||
<Filter>beast_basics\events</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="..\..\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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
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 <DeadlineTimer::Manager>
|
||||
, public InterruptibleThread::EntryPoint
|
||||
{
|
||||
private:
|
||||
typedef CriticalSection LockType;
|
||||
typedef List <DeadlineTimer> Items;
|
||||
|
||||
public:
|
||||
Manager ()
|
||||
: SharedSingleton <Manager> (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 <int> (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);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
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 <DeadlineTimer>::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 <Manager> m_manager;
|
||||
bool m_isActive;
|
||||
Time m_notificationTime;
|
||||
double m_secondsRecurring; // non zero if recurring
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
const bool interrupted = m_thread.wait (1000);
|
||||
bool const interrupted = m_thread.wait (1000);
|
||||
|
||||
if (interrupted)
|
||||
break;
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user