Remove unused or obsolete classes and files

This commit is contained in:
Vinnie Falco
2016-04-11 05:32:30 -04:00
parent 735c341fae
commit 203739f7a4
425 changed files with 1968 additions and 5270 deletions

View File

@@ -27,9 +27,9 @@
#include <ripple/protocol/SystemParameters.h>
#include <ripple/net/HTTPClient.h>
#include <beast/http/URL.h>
#include <beast/module/core/text/LexicalCast.h>
#include <ripple/beast/core/LexicalCast.h>
#include <beast/streams/debug_ostream.h>
#include <beast/utility/ci_char_traits.h>
#include <beast/ci_char_traits.h>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <boost/regex.hpp>

View File

@@ -0,0 +1,259 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/core/DeadlineTimer.h>
#include <ripple/beast/core/Thread.h>
#include <algorithm>
#include <cassert>
#include <mutex>
namespace ripple {
class DeadlineTimer::Manager
: protected beast::Thread
{
private:
using Items = beast::List <DeadlineTimer>;
public:
Manager () : beast::Thread ("DeadlineTimer::Manager")
{
startThread ();
}
~Manager ()
{
signalThreadShouldExit ();
notify ();
waitForThreadToExit ();
assert (m_items.empty ());
}
static
Manager&
instance()
{
static Manager m;
return m;
}
// Okay to call on an active timer.
// However, an extra notification may still happen due to concurrency.
//
void activate (DeadlineTimer& timer,
double secondsRecurring, beast::RelativeTime const& when)
{
assert (secondsRecurring >= 0);
std::lock_guard <std::recursive_mutex> lock (m_mutex);
if (timer.m_isActive)
{
m_items.erase (m_items.iterator_to (timer));
timer.m_isActive = false;
}
timer.m_secondsRecurring = secondsRecurring;
timer.m_notificationTime = when;
insertSorted (timer);
timer.m_isActive = true;
notify ();
}
// Okay to call this on an inactive timer.
// This can happen naturally based on concurrency.
//
void deactivate (DeadlineTimer& timer)
{
std::lock_guard <std::recursive_mutex> lock (m_mutex);
if (timer.m_isActive)
{
m_items.erase (m_items.iterator_to (timer));
timer.m_isActive = false;
notify ();
}
}
void run ()
{
while (! threadShouldExit ())
{
beast::RelativeTime const currentTime (
beast::RelativeTime::fromStartup ());
double seconds (0);
DeadlineTimer* timer (nullptr);
{
std::lock_guard <std::recursive_mutex> lock (m_mutex);
// See if a timer expired
if (! m_items.empty ())
{
timer = &m_items.front ();
// Has this timer expired?
if (timer->m_notificationTime <= currentTime)
{
// Expired, remove it from the list.
assert (timer->m_isActive);
m_items.pop_front ();
// Is the timer recurring?
if (timer->m_secondsRecurring > 0)
{
// Yes so set the timer again.
timer->m_notificationTime =
currentTime + timer->m_secondsRecurring;
// Put it back into the list as active
insertSorted (*timer);
}
else
{
// Not a recurring timer, deactivate it.
timer->m_isActive = false;
}
timer->m_listener->onDeadlineTimer (*timer);
// re-loop
seconds = -1;
}
else
{
seconds = (
timer->m_notificationTime - currentTime).inSeconds ();
// Can't be zero and come into the else clause.
assert (seconds != 0);
// Don't call the listener
timer = nullptr;
}
}
}
// Note that we have released the lock here.
if (seconds > 0)
{
// Wait until interrupt or next timer.
//
int const milliSeconds (std::max (
static_cast <int> (seconds * 1000 + 0.5), 1));
assert (milliSeconds > 0);
wait (milliSeconds);
}
else if (seconds == 0)
{
// Wait until interrupt
//
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& timer)
{
if (! m_items.empty ())
{
Items::iterator before = m_items.begin ();
for (;;)
{
if (before->m_notificationTime >= timer.m_notificationTime)
{
m_items.insert (before, timer);
break;
}
++before;
if (before == m_items.end ())
{
m_items.push_back (timer);
break;
}
}
}
else
{
m_items.push_back (timer);
}
}
private:
std::recursive_mutex m_mutex;
Items m_items;
};
//------------------------------------------------------------------------------
DeadlineTimer::DeadlineTimer (Listener* listener)
: m_listener (listener)
, m_isActive (false)
{
}
DeadlineTimer::~DeadlineTimer ()
{
Manager::instance().deactivate (*this);
}
void DeadlineTimer::cancel ()
{
Manager::instance().deactivate (*this);
}
void DeadlineTimer::setExpiration (double secondsUntilDeadline)
{
assert (secondsUntilDeadline != 0);
beast::RelativeTime const when (
beast::RelativeTime::fromStartup() + secondsUntilDeadline);
Manager::instance().activate (*this, 0, when);
}
void DeadlineTimer::setRecurringExpiration (double secondsUntilDeadline)
{
assert (secondsUntilDeadline != 0);
beast::RelativeTime const when (
beast::RelativeTime::fromStartup() + secondsUntilDeadline);
Manager::instance().activate (*this, secondsUntilDeadline, when);
}
} // beast

View File

@@ -22,8 +22,7 @@
#include <ripple/core/JobTypes.h>
#include <ripple/core/JobTypeInfo.h>
#include <ripple/core/JobTypeData.h>
#include <beast/chrono/chrono_util.h>
#include <beast/module/core/thread/Workers.h>
#include <beast/clock/chrono_util.h>
#include <chrono>
#include <memory>
#include <mutex>

View File

@@ -23,7 +23,7 @@
#include <ripple/basics/random.h>
#include <ripple/core/impl/SNTPClock.h>
#include <beast/asio/placeholders.h>
#include <beast/threads/Thread.h>
#include <ripple/beast/core/Thread.h>
#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <cmath>

View File

@@ -20,8 +20,8 @@
#ifndef RIPPLE_NET_SNTPCLOCK_H_INCLUDED
#define RIPPLE_NET_SNTPCLOCK_H_INCLUDED
#include <beast/chrono/abstract_clock.h>
#include <beast/utility/Journal.h>
#include <beast/clock/abstract_clock.h>
#include <ripple/beast/utility/Journal.h>
#include <chrono>
#include <memory>
#include <string>

View File

@@ -0,0 +1,215 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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.
*/
//==============================================================================
#include <ripple/core/Stoppable.h>
#include <cassert>
namespace ripple {
Stoppable::Stoppable (char const* name, RootStoppable& root)
: m_name (name)
, m_root (root)
, m_child (this)
, m_started (false)
, m_stopped (false)
, m_childrenStopped (false)
{
}
Stoppable::Stoppable (char const* name, Stoppable& parent)
: m_name (name)
, m_root (parent.m_root)
, m_child (this)
, m_started (false)
, m_stopped (false)
, m_childrenStopped (false)
{
// Must not have stopping parent.
assert (! parent.isStopping());
parent.m_children.push_front (&m_child);
}
Stoppable::~Stoppable ()
{
// Children must be stopped.
assert (!m_started || m_childrenStopped);
}
bool Stoppable::isStopping() const
{
return m_root.isStopping();
}
bool Stoppable::isStopped () const
{
return m_stopped;
}
bool Stoppable::areChildrenStopped () const
{
return m_childrenStopped;
}
void Stoppable::stopped ()
{
m_stoppedEvent.signal();
}
void Stoppable::onPrepare ()
{
}
void Stoppable::onStart ()
{
}
void Stoppable::onStop ()
{
stopped();
}
void Stoppable::onChildrenStopped ()
{
}
//------------------------------------------------------------------------------
void Stoppable::prepareRecursive ()
{
for (Children::const_iterator iter (m_children.cbegin ());
iter != m_children.cend(); ++iter)
iter->stoppable->prepareRecursive ();
onPrepare ();
}
void Stoppable::startRecursive ()
{
onStart ();
for (Children::const_iterator iter (m_children.cbegin ());
iter != m_children.cend(); ++iter)
iter->stoppable->startRecursive ();
}
void Stoppable::stopAsyncRecursive (beast::Journal j)
{
using namespace std::chrono;
auto const start = high_resolution_clock::now();
onStop ();
auto const ms = duration_cast<milliseconds>(
high_resolution_clock::now() - start).count();
#ifdef NDEBUG
if (ms >= 10)
if (auto stream = j.fatal())
stream << m_name << "::onStop took " << ms << "ms";
#else
(void)ms;
#endif
for (Children::const_iterator iter (m_children.cbegin ());
iter != m_children.cend(); ++iter)
iter->stoppable->stopAsyncRecursive(j);
}
void Stoppable::stopRecursive (beast::Journal j)
{
// Block on each child from the bottom of the tree up.
//
for (Children::const_iterator iter (m_children.cbegin ());
iter != m_children.cend(); ++iter)
iter->stoppable->stopRecursive (j);
// if we get here then all children have stopped
//
m_childrenStopped = true;
onChildrenStopped ();
// Now block on this Stoppable.
//
bool const timedOut (! m_stoppedEvent.wait (1 * 1000)); // milliseconds
if (timedOut)
{
if (auto stream = j.error())
stream << "Waiting for '" << m_name << "' to stop";
m_stoppedEvent.wait ();
}
// once we get here, we know the stoppable has stopped.
m_stopped = true;
}
//------------------------------------------------------------------------------
RootStoppable::RootStoppable (char const* name)
: Stoppable (name, *this)
, m_prepared (false)
, m_calledStop (false)
, m_calledStopAsync (false)
{
}
bool RootStoppable::isStopping() const
{
return m_calledStopAsync;
}
void RootStoppable::prepare ()
{
if (m_prepared.exchange (true) == false)
prepareRecursive ();
}
void RootStoppable::start ()
{
// Courtesy call to prepare.
if (m_prepared.exchange (true) == false)
prepareRecursive ();
if (m_started.exchange (true) == false)
startRecursive ();
}
void RootStoppable::stop (beast::Journal j)
{
// Must have a prior call to start()
assert (m_started);
{
std::lock_guard<std::mutex> lock(m_);
if (m_calledStop)
{
if (auto stream = j.warn())
stream << "Stoppable::stop called again";
return;
}
m_calledStop = true;
c_.notify_all();
}
stopAsync (j);
stopRecursive (j);
}
void RootStoppable::stopAsync(beast::Journal j)
{
if (m_calledStopAsync.exchange (true) == false)
stopAsyncRecursive(j);
}
}

View File

@@ -0,0 +1,294 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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.
*/
//==============================================================================
#include <ripple/core/impl/Workers.h>
#include <beast/unit_test/suite.h>
#include <cassert>
namespace ripple {
Workers::Workers (
Callback& callback,
std::string const& threadNames,
int numberOfThreads)
: m_callback (callback)
, m_threadNames (threadNames)
, m_allPaused (true, true)
, m_semaphore (0)
, m_numberOfThreads (0)
, m_activeCount (0)
, m_pauseCount (0)
, m_runningTaskCount (0)
{
setNumberOfThreads (numberOfThreads);
}
Workers::~Workers ()
{
pauseAllThreadsAndWait ();
deleteWorkers (m_everyone);
}
int Workers::getNumberOfThreads () const noexcept
{
return m_numberOfThreads;
}
// VFALCO NOTE if this function is called quickly to reduce then
// increase the number of threads, it could result in
// more paused threads being created than expected.
//
void Workers::setNumberOfThreads (int numberOfThreads)
{
if (m_numberOfThreads != numberOfThreads)
{
if (numberOfThreads > m_numberOfThreads)
{
// Increasing the number of working threads
int const amount = numberOfThreads - m_numberOfThreads;
for (int i = 0; i < amount; ++i)
{
// See if we can reuse a paused worker
Worker* worker = m_paused.pop_front ();
if (worker != nullptr)
{
// If we got here then the worker thread is at [1]
// This will unblock their call to wait()
//
worker->notify ();
}
else
{
worker = new Worker (*this, m_threadNames);
}
m_everyone.push_front (worker);
}
}
else if (numberOfThreads < m_numberOfThreads)
{
// Decreasing the number of working threads
int const amount = m_numberOfThreads - numberOfThreads;
for (int i = 0; i < amount; ++i)
{
++m_pauseCount;
// Pausing a thread counts as one "internal task"
m_semaphore.signal ();
}
}
m_numberOfThreads = numberOfThreads;
}
}
void Workers::pauseAllThreadsAndWait ()
{
setNumberOfThreads (0);
m_allPaused.wait ();
assert (numberOfCurrentlyRunningTasks () == 0);
}
void Workers::addTask ()
{
m_semaphore.signal ();
}
int Workers::numberOfCurrentlyRunningTasks () const noexcept
{
return m_runningTaskCount.load ();
}
void Workers::deleteWorkers (beast::LockFreeStack <Worker>& stack)
{
for (;;)
{
Worker* const worker = stack.pop_front ();
if (worker != nullptr)
{
// This call blocks until the thread orderly exits
delete worker;
}
else
{
break;
}
}
}
//------------------------------------------------------------------------------
Workers::Worker::Worker (Workers& workers, std::string const& threadName)
: Thread (threadName)
, m_workers (workers)
{
startThread ();
}
Workers::Worker::~Worker ()
{
stopThread ();
}
void Workers::Worker::run ()
{
while (! threadShouldExit ())
{
// Increment the count of active workers, and if
// we are the first one then reset the "all paused" event
//
if (++m_workers.m_activeCount == 1)
m_workers.m_allPaused.reset ();
for (;;)
{
// Acquire a task or "internal task."
//
m_workers.m_semaphore.wait ();
// See if there's a pause request. This
// counts as an "internal task."
//
int pauseCount = m_workers.m_pauseCount.load ();
if (pauseCount > 0)
{
// Try to decrement
pauseCount = --m_workers.m_pauseCount;
if (pauseCount >= 0)
{
// We got paused
break;
}
else
{
// Undo our decrement
++m_workers.m_pauseCount;
}
}
// We couldn't pause so we must have gotten
// unblocked in order to process a task.
//
++m_workers.m_runningTaskCount;
m_workers.m_callback.processTask ();
--m_workers.m_runningTaskCount;
// Put the name back in case the callback changed it
Thread::setCurrentThreadName (Thread::getThreadName());
}
// Any worker that goes into the paused list must
// guarantee that it will eventually block on its
// event object.
//
m_workers.m_paused.push_front (this);
// Decrement the count of active workers, and if we
// are the last one then signal the "all paused" event.
//
if (--m_workers.m_activeCount == 0)
m_workers.m_allPaused.signal ();
Thread::setCurrentThreadName ("(" + getThreadName() + ")");
// [1] We will be here when the paused list is popped
//
// We block on our event object, a requirement of being
// put into the paused list.
//
// This will get signaled on either a reactivate or a stopThread()
//
wait ();
}
}
//------------------------------------------------------------------------------
class Workers_test : public beast::unit_test::suite
{
public:
struct TestCallback : Workers::Callback
{
explicit TestCallback (int count_)
: finished (false, count_ == 0)
, count (count_)
{
}
void processTask ()
{
if (--count == 0)
finished.signal ();
}
beast::WaitableEvent finished;
std::atomic <int> count;
};
void testThreads (int const threadCount)
{
testcase ("threadCount = " + std::to_string (threadCount));
TestCallback cb (threadCount);
Workers w (cb, "Test", 0);
expect (w.getNumberOfThreads () == 0);
w.setNumberOfThreads (threadCount);
expect (w.getNumberOfThreads () == threadCount);
for (int i = 0; i < threadCount; ++i)
w.addTask ();
// 10 seconds should be enough to finish on any system
//
bool signaled = cb.finished.wait (10 * 1000);
expect (signaled, "timed out");
w.pauseAllThreadsAndWait ();
// We had better finished all our work!
expect (cb.count.load () == 0, "Did not complete task!");
}
void run ()
{
testThreads (0);
testThreads (1);
testThreads (2);
testThreads (4);
testThreads (16);
testThreads (64);
}
};
BEAST_DEFINE_TESTSUITE(Workers,core,ripple);
} // beast

View File

@@ -0,0 +1,154 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_CORE_WORKERS_H_INCLUDED
#define RIPPLE_CORE_WORKERS_H_INCLUDED
#include <ripple/core/impl/semaphore.h>
#include <ripple/beast/core/Thread.h>
#include <ripple/beast/core/LockFreeStack.h>
#include <atomic>
#include <string>
#include <thread>
namespace ripple {
/** A group of threads that process tasks.
*/
class Workers
{
public:
/** Called to perform tasks as needed. */
struct Callback
{
/** Perform a task.
The call is made on a thread owned by Workers. It is important
that you only process one task from inside your callback. Each
call to addTask will result in exactly one call to processTask.
@see Workers::addTask
*/
virtual void processTask () = 0;
};
/** Create the object.
A number of initial threads may be optionally specified. The
default is to create one thread per CPU.
@param threadNames The name given to each created worker thread.
*/
explicit Workers (Callback& callback,
std::string const& threadNames = "Worker",
int numberOfThreads =
static_cast<int>(std::thread::hardware_concurrency()));
~Workers ();
/** Retrieve the desired number of threads.
This just returns the number of active threads that were requested. If
there was a recent call to setNumberOfThreads, the actual number of active
threads may be temporarily different from what was last requested.
@note This function is not thread-safe.
*/
int getNumberOfThreads () const noexcept;
/** Set the desired number of threads.
@note This function is not thread-safe.
*/
void setNumberOfThreads (int numberOfThreads);
/** Pause all threads and wait until they are paused.
If a thread is processing a task it will pause as soon as the task
completes. There may still be tasks signaled even after all threads
have paused.
@note This function is not thread-safe.
*/
void pauseAllThreadsAndWait ();
/** Add a task to be performed.
Every call to addTask will eventually result in a call to
Callback::processTask unless the Workers object is destroyed or
the number of threads is never set above zero.
@note This function is thread-safe.
*/
void addTask ();
/** Get the number of currently executing calls of Callback::processTask.
While this function is thread-safe, the value may not stay
accurate for very long. It's mainly for diagnostic purposes.
*/
int numberOfCurrentlyRunningTasks () const noexcept;
//--------------------------------------------------------------------------
private:
struct PausedTag { };
/* A Worker executes tasks on its provided thread.
These are the states:
Active: Running the task processing loop.
Idle: Active, but blocked on waiting for a task.
Pausd: Blocked waiting to exit or become active.
*/
class Worker
: public beast::LockFreeStack <Worker>::Node
, public beast::LockFreeStack <Worker, PausedTag>::Node
, public beast::Thread
{
public:
Worker (Workers& workers, std::string const& threadName);
~Worker ();
private:
void run ();
private:
Workers& m_workers;
};
private:
static void deleteWorkers (beast::LockFreeStack <Worker>& stack);
private:
Callback& m_callback;
std::string m_threadNames; // The name to give each thread
beast::WaitableEvent m_allPaused; // signaled when all threads paused
semaphore m_semaphore; // each pending task is 1 resource
int m_numberOfThreads; // how many we want active now
std::atomic <int> m_activeCount; // to know when all are paused
std::atomic <int> m_pauseCount; // how many threads need to pause now
std::atomic <int> m_runningTaskCount; // how many calls to processTask() active
beast::LockFreeStack <Worker> m_everyone; // holds all created workers
beast::LockFreeStack <Worker, PausedTag> m_paused; // holds just paused workers
};
} // beast
#endif

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_CORE_SEMAPHORE_H_INCLUDED
#define RIPPLE_CORE_SEMAPHORE_H_INCLUDED
#include <condition_variable>
#include <mutex>
namespace ripple {
template <class Mutex, class CondVar>
class basic_semaphore
{
private:
using scoped_lock = std::unique_lock <Mutex>;
Mutex m_mutex;
CondVar m_cond;
std::size_t m_count;
public:
using size_type = std::size_t;
/** Create the semaphore, with an optional initial count.
If unspecified, the initial count is zero.
*/
explicit basic_semaphore (size_type count = 0)
: m_count (count)
{
}
/** Increment the count and unblock one waiting thread. */
void notify ()
{
scoped_lock lock (m_mutex);
++m_count;
m_cond.notify_one ();
}
// Deprecated, for backward compatibility
void signal () { notify (); }
/** Block until notify is called. */
void wait ()
{
scoped_lock lock (m_mutex);
while (m_count == 0)
m_cond.wait (lock);
--m_count;
}
/** Perform a non-blocking wait.
@return `true` If the wait would be satisfied.
*/
bool try_wait ()
{
scoped_lock lock (m_mutex);
if (m_count == 0)
return false;
--m_count;
return true;
}
};
using semaphore = basic_semaphore <std::mutex, std::condition_variable>;
} // ripple
#endif