mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Add DeadlineTimer class
This commit is contained in:
@@ -66,6 +66,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\modules\beast_basics\beast_basics.h" />
|
<ClInclude Include="..\..\modules\beast_basics\beast_basics.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_basics\diagnostic\beast_CatchAny.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\events\beast_OncePerSecond.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_basics\functor\beast_Function.h" />
|
<ClInclude Include="..\..\modules\beast_basics\functor\beast_Function.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_basics\math\beast_Math.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|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_basics\events\beast_OncePerSecond.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -608,6 +608,9 @@
|
|||||||
<ClInclude Include="..\..\modules\beast_basics\threads\beast_SharedData.h">
|
<ClInclude Include="..\..\modules\beast_basics\threads\beast_SharedData.h">
|
||||||
<Filter>beast_basics\threads</Filter>
|
<Filter>beast_basics\threads</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.h">
|
||||||
|
<Filter>beast_basics\events</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\modules\beast_core\beast_core.cpp">
|
<ClCompile Include="..\..\modules\beast_core\beast_core.cpp">
|
||||||
@@ -946,6 +949,9 @@
|
|||||||
<ClCompile Include="..\..\modules\beast_core\time\beast_PerformedAtExit.cpp">
|
<ClCompile Include="..\..\modules\beast_core\time\beast_PerformedAtExit.cpp">
|
||||||
<Filter>beast_core\time</Filter>
|
<Filter>beast_core\time</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\modules\beast_basics\events\beast_DeadlineTimer.cpp">
|
||||||
|
<Filter>beast_basics\events</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Text Include="..\..\TODO.txt" />
|
<Text Include="..\..\TODO.txt" />
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
BEAST TODO
|
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
|
- Make OwnedArray add routines return a pointer instead of reference
|
||||||
|
|
||||||
- Tidy up CacheLine, MemoryAlignment
|
- Tidy up CacheLine, MemoryAlignment
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace beast
|
|||||||
|
|
||||||
#include "diagnostic/beast_CatchAny.cpp"
|
#include "diagnostic/beast_CatchAny.cpp"
|
||||||
|
|
||||||
|
#include "events/beast_DeadlineTimer.cpp"
|
||||||
#include "events/beast_OncePerSecond.cpp"
|
#include "events/beast_OncePerSecond.cpp"
|
||||||
|
|
||||||
#include "math/beast_MurmurHash.cpp"
|
#include "math/beast_MurmurHash.cpp"
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ namespace beast
|
|||||||
|
|
||||||
#include "functor/beast_Function.h"
|
#include "functor/beast_Function.h"
|
||||||
#include "diagnostic/beast_CatchAny.h"
|
#include "diagnostic/beast_CatchAny.h"
|
||||||
|
#include "events/beast_DeadlineTimer.h"
|
||||||
#include "events/beast_OncePerSecond.h"
|
#include "events/beast_OncePerSecond.h"
|
||||||
#include "math/beast_Math.h"
|
#include "math/beast_Math.h"
|
||||||
#include "math/beast_MurmurHash.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 (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
const bool interrupted = m_thread.wait (1000);
|
bool const interrupted = m_thread.wait (1000);
|
||||||
|
|
||||||
if (interrupted)
|
if (interrupted)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -114,18 +114,17 @@ public:
|
|||||||
|
|
||||||
void stop (bool const wait);
|
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
|
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 true or throws, it must not be called again until the idle function
|
||||||
returns and is re-entered.
|
returns and is re-entered.
|
||||||
|
|
||||||
@invariant No previous calls to interruptionPoint() made after the idle
|
@invariant No previous calls to interruptionPoint() made after the idle
|
||||||
function entry point returned `true`.
|
function entry point returned `true`.
|
||||||
|
|
||||||
@return `false` if the idle function may continue, or `true` if the
|
@return `false` if the idle function may continue, or `true` if the
|
||||||
idle function must return as soon as possible.
|
idle function must return as soon as possible.
|
||||||
*/
|
*/
|
||||||
bool interruptionPoint ();
|
bool interruptionPoint ();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user