mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move many Thread related classes
This commit is contained in:
@@ -137,7 +137,6 @@ namespace beast
|
||||
|
||||
#include "diagnostic/FatalError.cpp"
|
||||
#include "diagnostic/FPUFlags.cpp"
|
||||
#include "diagnostic/LeakChecked.cpp"
|
||||
#include "diagnostic/SemanticVersion.cpp"
|
||||
#include "diagnostic/UnitTest.cpp"
|
||||
#include "diagnostic/UnitTestUtilities.cpp"
|
||||
@@ -161,7 +160,6 @@ namespace beast
|
||||
#include "maths/Random.cpp"
|
||||
|
||||
#include "memory/MemoryBlock.cpp"
|
||||
#include "memory/StaticObject.cpp"
|
||||
|
||||
#include "misc/Main.cpp"
|
||||
#include "misc/Result.cpp"
|
||||
@@ -192,17 +190,12 @@ namespace beast
|
||||
|
||||
#include "thread/impl/TrackedMutex.cpp"
|
||||
#include "thread/DeadlineTimer.cpp"
|
||||
#include "thread/Stoppable.cpp"
|
||||
#include "thread/Semaphore.cpp"
|
||||
#include "thread/Workers.cpp"
|
||||
|
||||
#include "threads/ChildProcess.cpp"
|
||||
#include "threads/ReadWriteLock.cpp"
|
||||
#include "threads/ReadWriteMutex.cpp"
|
||||
#include "threads/SpinDelay.cpp"
|
||||
#include "threads/Thread.cpp"
|
||||
#include "threads/ThreadPool.cpp"
|
||||
#include "threads/TimeSliceThread.cpp"
|
||||
|
||||
#include "time/PerformanceCounter.cpp"
|
||||
#include "time/AtExitHook.cpp"
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
#include "../../beast/SafeBool.h"
|
||||
#include "../../beast/Strings.h"
|
||||
#include "../../beast/TypeTraits.h"
|
||||
#include "../../beast/Thread.h"
|
||||
#include "../../beast/Threads.h"
|
||||
#include "../../beast/Utility.h"
|
||||
#include "../../beast/Chrono.h"
|
||||
|
||||
@@ -81,10 +81,8 @@ class FileOutputStream;
|
||||
#include "memory/AtomicPointer.h"
|
||||
#include "memory/AtomicState.h"
|
||||
#include "threads/SpinDelay.h"
|
||||
#include "memory/StaticObject.h"
|
||||
|
||||
#include "time/AtExitHook.h"
|
||||
#include "diagnostic/LeakChecked.h"
|
||||
#include "time/Time.h"
|
||||
#include "threads/ScopedLock.h"
|
||||
#include "threads/CriticalSection.h"
|
||||
@@ -147,8 +145,6 @@ class FileOutputStream;
|
||||
|
||||
#include "memory/MemoryAlignment.h"
|
||||
#include "memory/CacheLine.h"
|
||||
#include "threads/ReadWriteMutex.h"
|
||||
#include "threads/Thread.h"
|
||||
#include "thread/MutexTraits.h"
|
||||
#include "thread/TrackedMutex.h"
|
||||
#include "diagnostic/FatalError.h"
|
||||
@@ -157,8 +153,6 @@ class FileOutputStream;
|
||||
#include "maths/uint24.h"
|
||||
#include "logging/Logger.h"
|
||||
#include "diagnostic/FPUFlags.h"
|
||||
#include "memory/SharedObject.h"
|
||||
#include "memory/SharedPtr.h"
|
||||
#include "memory/SharedFunction.h"
|
||||
#include "containers/AbstractFifo.h"
|
||||
#include "text/Identifier.h"
|
||||
@@ -223,8 +217,6 @@ class FileOutputStream;
|
||||
#include "threads/Process.h"
|
||||
#include "threads/ScopedReadLock.h"
|
||||
#include "threads/ScopedWriteLock.h"
|
||||
#include "threads/ThreadPool.h"
|
||||
#include "threads/TimeSliceThread.h"
|
||||
#include "diagnostic/UnitTest.h"
|
||||
#include "xml/XmlDocument.h"
|
||||
#include "xml/XmlElement.h"
|
||||
@@ -238,7 +230,6 @@ class FileOutputStream;
|
||||
#include "thread/DeadlineTimer.h"
|
||||
|
||||
#include "thread/Semaphore.h"
|
||||
#include "thread/Stoppable.h"
|
||||
#include "thread/Workers.h"
|
||||
|
||||
}
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class LeakCheckedBase::LeakCounterBase::Singleton
|
||||
{
|
||||
public:
|
||||
void push_back (LeakCounterBase* counter)
|
||||
{
|
||||
m_list.push_front (counter);
|
||||
}
|
||||
|
||||
void checkForLeaks ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
LeakCounterBase* const counter = m_list.pop_front ();
|
||||
|
||||
if (!counter)
|
||||
break;
|
||||
|
||||
counter->checkForLeaks ();
|
||||
}
|
||||
}
|
||||
|
||||
static Singleton& getInstance ()
|
||||
{
|
||||
static Singleton instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class LeakCheckedBase;
|
||||
|
||||
LockFreeStack <LeakCounterBase> m_list;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
LeakCheckedBase::LeakCounterBase::LeakCounterBase ()
|
||||
{
|
||||
Singleton::getInstance ().push_back (this);
|
||||
}
|
||||
|
||||
void LeakCheckedBase::LeakCounterBase::checkForLeaks ()
|
||||
{
|
||||
// If there's a runtime error from this line, it means there's
|
||||
// an order of destruction problem between different translation units!
|
||||
//
|
||||
this->checkPureVirtual ();
|
||||
|
||||
int const count = m_count.get ();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
/** If you hit this, then you've leaked one or more objects of the
|
||||
specified class; the name should have been printed by the line
|
||||
below.
|
||||
|
||||
If you're leaking, it's probably because you're using old-fashioned,
|
||||
non-RAII techniques for your object management. Tut, tut. Always,
|
||||
always use ScopedPointers, OwnedArrays, SharedObjects,
|
||||
etc, and avoid the 'delete' operator at all costs!
|
||||
*/
|
||||
DBG ("Leaked objects: " << count << " of " << getClassName ());
|
||||
|
||||
//bassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_DEBUG
|
||||
void LeakCheckedBase::reportDanglingPointer (char const* objectName)
|
||||
#else
|
||||
void LeakCheckedBase::reportDanglingPointer (char const*)
|
||||
#endif
|
||||
{
|
||||
/* If you hit this, then you've managed to delete more instances
|
||||
of this class than you've created. That indicates that you're
|
||||
deleting some dangling pointers.
|
||||
|
||||
Note that although this assertion will have been triggered
|
||||
during a destructor, it might not be this particular deletion
|
||||
that's at fault - the incorrect one may have happened at an
|
||||
earlier point in the program, and simply not been detected
|
||||
until now.
|
||||
|
||||
Most errors like this are caused by using old-fashioned,
|
||||
non-RAII techniques for your object management. Tut, tut.
|
||||
Always, always use ScopedPointers, OwnedArrays,
|
||||
SharedObjects, etc, and avoid the 'delete' operator
|
||||
at all costs!
|
||||
*/
|
||||
DBG ("Dangling pointer deletion: " << objectName);
|
||||
|
||||
bassertfalse;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LeakCheckedBase::checkForLeaks ()
|
||||
{
|
||||
LeakCounterBase::Singleton::getInstance ().checkForLeaks ();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_LEAKCHECKED_H_INCLUDED
|
||||
#define BEAST_LEAKCHECKED_H_INCLUDED
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class LeakCheckedBase
|
||||
{
|
||||
public:
|
||||
static void checkForLeaks ();
|
||||
|
||||
protected:
|
||||
class LeakCounterBase : public LockFreeStack <LeakCounterBase>::Node
|
||||
{
|
||||
public:
|
||||
LeakCounterBase ();
|
||||
|
||||
virtual ~LeakCounterBase ()
|
||||
{
|
||||
}
|
||||
|
||||
inline int increment ()
|
||||
{
|
||||
return ++m_count;
|
||||
}
|
||||
|
||||
inline int decrement ()
|
||||
{
|
||||
return --m_count;
|
||||
}
|
||||
|
||||
virtual char const* getClassName () const = 0;
|
||||
|
||||
private:
|
||||
void checkForLeaks ();
|
||||
virtual void checkPureVirtual () const = 0;
|
||||
|
||||
class Singleton;
|
||||
friend class LeakCheckedBase;
|
||||
|
||||
Atomic <int> m_count;
|
||||
};
|
||||
|
||||
static void reportDanglingPointer (char const* objectName);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Detects leaks at program exit.
|
||||
|
||||
To use this, derive your class from this template using CRTP (curiously
|
||||
recurring template pattern).
|
||||
*/
|
||||
template <class Object>
|
||||
class LeakChecked : private LeakCheckedBase
|
||||
{
|
||||
protected:
|
||||
LeakChecked () noexcept
|
||||
{
|
||||
getCounter ().increment ();
|
||||
}
|
||||
|
||||
LeakChecked (LeakChecked const&) noexcept
|
||||
{
|
||||
getCounter ().increment ();
|
||||
}
|
||||
|
||||
~LeakChecked ()
|
||||
{
|
||||
if (getCounter ().decrement () < 0)
|
||||
{
|
||||
reportDanglingPointer (getLeakCheckedName ());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Singleton that maintains the count of this object
|
||||
//
|
||||
class LeakCounter : public LeakCounterBase
|
||||
{
|
||||
public:
|
||||
LeakCounter () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
char const* getClassName () const
|
||||
{
|
||||
return getLeakCheckedName ();
|
||||
}
|
||||
|
||||
void checkPureVirtual () const { }
|
||||
};
|
||||
|
||||
private:
|
||||
/* Due to a bug in Visual Studio 10 and earlier, the string returned by
|
||||
typeid().name() will appear to leak on exit. Therefore, we should
|
||||
only call this function when there's an actual leak, or else there
|
||||
will be spurious leak notices at exit.
|
||||
*/
|
||||
static const char* getLeakCheckedName ()
|
||||
{
|
||||
return typeid (Object).name ();
|
||||
}
|
||||
|
||||
// Retrieve the singleton for this object
|
||||
//
|
||||
static LeakCounter& getCounter () noexcept
|
||||
{
|
||||
return StaticObject <LeakCounter>::get();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
namespace disabled
|
||||
{
|
||||
|
||||
class LeakCheckedBase
|
||||
{
|
||||
public:
|
||||
static void checkForLeaks ()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class Object>
|
||||
class LeakChecked : public LeakCheckedBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Lift the appropriate implementation into our namespace
|
||||
//
|
||||
#if BEAST_CHECK_MEMORY_LEAKS
|
||||
using detail::LeakChecked;
|
||||
using detail::LeakCheckedBase;
|
||||
#else
|
||||
using detail::disabled::LeakChecked;
|
||||
using detail::disabled::LeakCheckedBase;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -170,8 +170,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** The type of a list of tests.
|
||||
*/
|
||||
/** The type of a list of tests. */
|
||||
typedef Array <UnitTest*, CriticalSection> TestList;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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_SHAREDOBJECT_H_INCLUDED
|
||||
#define BEAST_SHAREDOBJECT_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Adds reference-counting to an object.
|
||||
|
||||
To add reference-counting to a class, derive it from this class, and
|
||||
use the SharedPtr class to point to it.
|
||||
|
||||
e.g. @code
|
||||
class MyClass : public SharedObject
|
||||
{
|
||||
void foo();
|
||||
|
||||
// This is a neat way of declaring a typedef for a pointer class,
|
||||
// rather than typing out the full templated name each time..
|
||||
typedef SharedPtr<MyClass> Ptr;
|
||||
};
|
||||
|
||||
MyClass::Ptr p = new MyClass();
|
||||
MyClass::Ptr p2 = p;
|
||||
p = nullptr;
|
||||
p2->foo();
|
||||
@endcode
|
||||
|
||||
Once a new SharedObject has been assigned to a pointer, be
|
||||
careful not to delete the object manually.
|
||||
|
||||
This class uses an Atomic<int> value to hold the reference count, so that it
|
||||
the pointers can be passed between threads safely. For a faster but non-thread-safe
|
||||
version, use SingleThreadedSharedObject instead.
|
||||
|
||||
@see SharedPtr, SharedObjectArray, SingleThreadedSharedObject
|
||||
*/
|
||||
class BEAST_API SharedObject : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
inline void incReferenceCount() const noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
|
||||
If doDelete is true the object will be deleted when the reference
|
||||
count drops to zero. The delete is performed using the regular
|
||||
operator and does NOT go through the ContainerDeletePolicy.
|
||||
|
||||
The return value indicates if the reference count dropped to zero,
|
||||
so callers who know the derived type can use the ContainerDeletePolicy.
|
||||
*/
|
||||
void decReferenceCount () const
|
||||
{
|
||||
bassert (getReferenceCount() > 0);
|
||||
if (--refCount == 0)
|
||||
destroy ();
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
inline int getReferenceCount() const noexcept
|
||||
{
|
||||
return refCount.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
SharedObject()
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SharedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
bassert (getReferenceCount() == 0);
|
||||
}
|
||||
|
||||
/** Destroy the object.
|
||||
Derived classes can override this for different behaviors.
|
||||
*/
|
||||
virtual void destroy () const
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
/** Resets the reference count to zero without deleting the object.
|
||||
You should probably never need to use this!
|
||||
*/
|
||||
void resetReferenceCount() noexcept
|
||||
{
|
||||
refCount = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Atomic <int> mutable refCount;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Adds reference-counting to an object.
|
||||
|
||||
This is effectively a version of the SharedObject class, but which
|
||||
uses a non-atomic counter, and so is not thread-safe (but which will be more
|
||||
efficient).
|
||||
For more details on how to use it, see the SharedObject class notes.
|
||||
|
||||
@see SharedObject, SharedPtr, SharedObjectArray
|
||||
*/
|
||||
class BEAST_API SingleThreadedSharedObject : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
inline void incReferenceCount() noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
|
||||
If doDelete is true the object will be deleted when the reference
|
||||
count drops to zero. The delete is performed using the regular
|
||||
operator and does NOT go through the ContainerDeletePolicy.
|
||||
|
||||
The return value indicates if the reference count dropped to zero,
|
||||
so callers who know the derived type can use the ContainerDeletePolicy.
|
||||
*/
|
||||
inline void decReferenceCount ()
|
||||
{
|
||||
bassert (getReferenceCount() > 0);
|
||||
if (--refCount == 0)
|
||||
destroy ();
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
inline int getReferenceCount() const noexcept { return refCount; }
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
SingleThreadedSharedObject() : refCount (0) {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SingleThreadedSharedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
bassert (getReferenceCount() == 0);
|
||||
}
|
||||
|
||||
virtual void destroy () const
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int refCount;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,320 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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_CORE_SHAREDPTR_H_INCLUDED
|
||||
#define BEAST_CORE_SHAREDPTR_H_INCLUDED
|
||||
|
||||
// Visual Studio doesn't seem to do very well when it comes
|
||||
// to templated constructors and assignments so we provide
|
||||
// non-templated versions when U and T are the same type.
|
||||
//
|
||||
#ifndef BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS
|
||||
#define BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS 1
|
||||
#endif
|
||||
|
||||
/** A smart-pointer container.
|
||||
|
||||
Requirement:
|
||||
T must support the SharedObject concept.
|
||||
|
||||
The template parameter specifies the class of the object you want to point
|
||||
to - the easiest way to make a class reference-countable is to simply make
|
||||
it inherit from SharedObject, but if you need to, you could roll your own
|
||||
reference-countable class by implementing a pair of methods called
|
||||
incReferenceCount() and decReferenceCount().
|
||||
|
||||
When using this class, you'll probably want to create a typedef to
|
||||
abbreviate the full templated name - e.g.
|
||||
|
||||
@code
|
||||
|
||||
typedef SharedPtr <MyClass> MyClassPtr;
|
||||
|
||||
@endcode
|
||||
|
||||
@see SharedObject, SharedObjectArray
|
||||
*/
|
||||
template <class T>
|
||||
class SharedPtr
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
/** The class being referenced by this container. */
|
||||
typedef T ReferencedType;
|
||||
|
||||
/** Construct a container pointing to nothing. */
|
||||
SharedPtr () noexcept
|
||||
: m_p (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a container holding an object.
|
||||
This will increment the object's reference-count if it is non-null.
|
||||
Requirement:
|
||||
U* must be convertible to T*
|
||||
*/
|
||||
/** @{ */
|
||||
SharedPtr (T* t) noexcept
|
||||
: m_p (acquire (t))
|
||||
{
|
||||
}
|
||||
|
||||
template <class U>
|
||||
SharedPtr (U* u) noexcept
|
||||
: m_p (acquire (u))
|
||||
{
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Construct a container holding an object from another container.
|
||||
This will increment the object's reference-count (if it is non-null).
|
||||
Requirement:
|
||||
U* must be convertible to T*
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS
|
||||
SharedPtr (SharedPtr const& sp) noexcept
|
||||
: m_p (acquire (sp.get ()))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class U>
|
||||
SharedPtr (SharedPtr <U> const& sp) noexcept
|
||||
: m_p (acquire (sp.get ()))
|
||||
{
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Assign a different object to the container.
|
||||
The previous object beind held, if any, loses a reference count and
|
||||
will be destroyed if it is the last reference.
|
||||
Requirement:
|
||||
U* must be convertible to T*
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS
|
||||
SharedPtr& operator= (T* t)
|
||||
{
|
||||
return assign (t);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class U>
|
||||
SharedPtr& operator= (U* u)
|
||||
{
|
||||
return assign (u);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Assign an object from another container to this one. */
|
||||
/** @{ */
|
||||
SharedPtr& operator= (SharedPtr const& sp)
|
||||
{
|
||||
return assign (sp.get ());
|
||||
}
|
||||
|
||||
/** Assign an object from another container to this one. */
|
||||
template <class U>
|
||||
SharedPtr& operator= (SharedPtr <U> const& sp)
|
||||
{
|
||||
return assign (sp.get ());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
/** Construct a container with an object transferred from another container.
|
||||
The originating container loses its reference to the object.
|
||||
Requires:
|
||||
U* must be convertible to T*
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS
|
||||
SharedPtr (SharedPtr && sp) noexcept
|
||||
: m_p (sp.swap <T> (nullptr))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class U>
|
||||
SharedPtr (SharedPtr <U>&& sp) noexcept
|
||||
: m_p (sp.swap <U> (nullptr))
|
||||
{
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Transfer ownership of another object to this container.
|
||||
The originating container loses its reference to the object.
|
||||
Requires:
|
||||
U* must be convertible to T*
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_SHAREDPTR_PROVIDE_COMPILER_WORKAROUNDS
|
||||
SharedPtr& operator= (SharedPtr && sp)
|
||||
{
|
||||
return assign (sp.swap <T> (nullptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class U>
|
||||
SharedPtr& operator= (SharedPtr <U>&& sp)
|
||||
{
|
||||
return assign (sp.swap <U> (nullptr));
|
||||
}
|
||||
/** @} */
|
||||
#endif
|
||||
|
||||
/** Destroy the container and release the held reference, if any.
|
||||
*/
|
||||
~SharedPtr ()
|
||||
{
|
||||
release (m_p);
|
||||
}
|
||||
|
||||
/** Returns `true` if the container is not pointing to an object. */
|
||||
bool empty () const noexcept
|
||||
{
|
||||
return m_p == nullptr;
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references if any, else nullptr. */
|
||||
operator T* () const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references if any, else nullptr. */
|
||||
T* operator-> () const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references if any, else nullptr. */
|
||||
T* get () const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
private:
|
||||
// Acquire a reference to u for the caller.
|
||||
//
|
||||
template <class U>
|
||||
static T* acquire (U* u) noexcept
|
||||
{
|
||||
if (u != nullptr)
|
||||
u->incReferenceCount ();
|
||||
return u;
|
||||
}
|
||||
|
||||
static void release (T* t)
|
||||
{
|
||||
if (t != nullptr)
|
||||
t->decReferenceCount ();
|
||||
}
|
||||
|
||||
// Swap ownership of the currently referenced object.
|
||||
// The caller receives a pointer to the original object,
|
||||
// and this container is left with the passed object. No
|
||||
// reference counts are changed.
|
||||
//
|
||||
template <class U>
|
||||
T* swap (U* u)
|
||||
{
|
||||
T* const t (m_p);
|
||||
m_p = u;
|
||||
return t;
|
||||
}
|
||||
|
||||
// Acquire ownership of u.
|
||||
// Any previous reference is released.
|
||||
//
|
||||
template <class U>
|
||||
SharedPtr& assign (U* u)
|
||||
{
|
||||
if (m_p != u)
|
||||
release (this->swap (acquire (u)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* m_p;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// bind() helpers for pointer to member function
|
||||
|
||||
template <class T>
|
||||
const T* get_pointer (SharedPtr<T> const& ptr)
|
||||
{
|
||||
return ptr.get();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* get_pointer (SharedPtr<T>& ptr)
|
||||
{
|
||||
return ptr.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** SharedPtr comparisons. */
|
||||
/** @{ */
|
||||
template <class T, class U>
|
||||
bool operator== (SharedPtr <T> const& lhs, U* const rhs) noexcept
|
||||
{
|
||||
return lhs.get() == rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator== (SharedPtr <T> const& lhs, SharedPtr <U> const& rhs) noexcept
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator== (T const* lhs, SharedPtr <U> const& rhs) noexcept
|
||||
{
|
||||
return lhs == rhs.get();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!= (SharedPtr <T> const& lhs, U const* rhs) noexcept
|
||||
{
|
||||
return lhs.get() != rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!= (SharedPtr <T> const& lhs, SharedPtr <U> const& rhs) noexcept
|
||||
{
|
||||
return lhs.get() != rhs.get();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!= (T const* lhs, SharedPtr <U> const& rhs) noexcept
|
||||
{
|
||||
return lhs != rhs.get();
|
||||
}
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// This is here so we don't need the Thread class declaration
|
||||
void staticObjectWait (std::size_t n)
|
||||
{
|
||||
// Wait for initialization
|
||||
Thread::yield ();
|
||||
if (n > 10)
|
||||
Thread::sleep (1);
|
||||
else if (n > 100)
|
||||
Thread::sleep (10);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_STATICOBJECT_H_INCLUDED
|
||||
#define BEAST_STATICOBJECT_H_INCLUDED
|
||||
|
||||
// Spec: N2914=09-0104
|
||||
//
|
||||
// [3.6.2] Initialization of non-local objects
|
||||
//
|
||||
// Objects with static storage duration (3.7.1) or thread storage
|
||||
// duration (3.7.2) shall be zero-initialized (8.5) before any
|
||||
// other initialization takes place.
|
||||
//
|
||||
|
||||
namespace detail {
|
||||
|
||||
extern void staticObjectWait (std::size_t n);
|
||||
|
||||
}
|
||||
|
||||
/** Wrapper to produce an object with static storage duration.
|
||||
|
||||
The object is constructed in a thread-safe fashion when the get function
|
||||
is first called. Note that the destructor for Object is never called. To
|
||||
invoke the destructor, use the AtExitHook facility (with caution).
|
||||
|
||||
The Tag parameter allows multiple instances of the same Object type, by
|
||||
using different tags.
|
||||
|
||||
Object must meet these requirements:
|
||||
DefaultConstructible
|
||||
|
||||
@see AtExitHook
|
||||
*/
|
||||
template <class Object, typename Tag = void>
|
||||
class StaticObject
|
||||
{
|
||||
public:
|
||||
static Object& get ()
|
||||
{
|
||||
StaticData& staticData (StaticData::get());
|
||||
|
||||
if (staticData.state.get() != initialized)
|
||||
{
|
||||
if (staticData.state.compareAndSetBool (initializing, uninitialized))
|
||||
{
|
||||
// Initialize the object.
|
||||
new (&staticData.object) Object;
|
||||
staticData.state = initialized;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::size_t n = 0; staticData.state.get() != initialized; ++n)
|
||||
{
|
||||
detail::staticObjectWait (n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return staticData.object;
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
uninitialized = 0, // must be zero to function properly
|
||||
initializing,
|
||||
initialized
|
||||
};
|
||||
|
||||
// This structure gets zero-filled at static initialization time.
|
||||
// No constructors are called.
|
||||
//
|
||||
class StaticData : public Uncopyable
|
||||
{
|
||||
public:
|
||||
Atomic <int> state;
|
||||
Object object;
|
||||
|
||||
static StaticData& get ()
|
||||
{
|
||||
static uint8 storage [sizeof (StaticData)];
|
||||
return *(reinterpret_cast <StaticData*> (&storage [0]));
|
||||
}
|
||||
|
||||
private:
|
||||
StaticData();
|
||||
~StaticData();
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -39,13 +39,6 @@ bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylock (
|
||||
void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&mutex); }
|
||||
|
||||
//==============================================================================
|
||||
void BEAST_CALLTYPE Thread::sleep (int millisecs)
|
||||
{
|
||||
struct timespec time;
|
||||
time.tv_sec = millisecs / 1000;
|
||||
time.tv_nsec = (millisecs % 1000) * 1000000;
|
||||
nanosleep (&time, nullptr);
|
||||
}
|
||||
|
||||
void Process::terminate()
|
||||
{
|
||||
@@ -913,146 +906,7 @@ void InterProcessLock::exit()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void BEAST_API beast_threadEntryPoint (void*);
|
||||
|
||||
extern "C" void* threadEntryProc (void*);
|
||||
extern "C" void* threadEntryProc (void* userData)
|
||||
{
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
#if BEAST_ANDROID
|
||||
struct AndroidThreadScope
|
||||
{
|
||||
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
|
||||
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
|
||||
};
|
||||
|
||||
const AndroidThreadScope androidEnv;
|
||||
#endif
|
||||
|
||||
beast_threadEntryPoint (userData);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Thread::launchThread()
|
||||
{
|
||||
threadHandle = 0;
|
||||
pthread_t handle = 0;
|
||||
|
||||
if (pthread_create (&handle, 0, threadEntryProc, this) == 0)
|
||||
{
|
||||
pthread_detach (handle);
|
||||
threadHandle = (void*) handle;
|
||||
threadId = (ThreadID) threadHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
threadId = 0;
|
||||
threadHandle = 0;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle != 0)
|
||||
{
|
||||
#if BEAST_ANDROID
|
||||
bassertfalse; // pthread_cancel not available!
|
||||
#else
|
||||
pthread_cancel ((pthread_t) threadHandle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::setCurrentThreadName (const String& name)
|
||||
{
|
||||
#if BEAST_IOS || (BEAST_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSThread currentThread] setName: beastStringToNS (name)];
|
||||
}
|
||||
#elif BEAST_LINUX
|
||||
#if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
|
||||
pthread_setname_np (pthread_self(), name.toRawUTF8());
|
||||
#else
|
||||
prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Thread::setThreadPriority (void* handle, int priority)
|
||||
{
|
||||
struct sched_param param;
|
||||
int policy;
|
||||
priority = blimit (0, 10, priority);
|
||||
|
||||
if (handle == nullptr)
|
||||
handle = (void*) pthread_self();
|
||||
|
||||
if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0)
|
||||
return false;
|
||||
|
||||
policy = priority == 0 ? SCHED_OTHER : SCHED_RR;
|
||||
|
||||
const int minPriority = sched_get_priority_min (policy);
|
||||
const int maxPriority = sched_get_priority_max (policy);
|
||||
|
||||
param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
|
||||
return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0;
|
||||
}
|
||||
|
||||
Thread::ThreadID Thread::getCurrentThreadId()
|
||||
{
|
||||
return (ThreadID) pthread_self();
|
||||
}
|
||||
|
||||
void Thread::yield()
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/* Remove this macro if you're having problems compiling the cpu affinity
|
||||
calls (the API for these has changed about quite a bit in various Linux
|
||||
versions, and a lot of distros seem to ship with obsolete versions)
|
||||
*/
|
||||
#if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES)
|
||||
#define SUPPORT_AFFINITIES 1
|
||||
#endif
|
||||
|
||||
void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
||||
{
|
||||
#if SUPPORT_AFFINITIES
|
||||
cpu_set_t affinity;
|
||||
CPU_ZERO (&affinity);
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
if ((affinityMask & (1 << i)) != 0)
|
||||
CPU_SET (i, &affinity);
|
||||
|
||||
/*
|
||||
N.B. If this line causes a compile error, then you've probably not got the latest
|
||||
version of glibc installed.
|
||||
|
||||
If you don't want to update your copy of glibc and don't care about cpu affinities,
|
||||
then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0.
|
||||
*/
|
||||
sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
|
||||
sched_yield();
|
||||
|
||||
#else
|
||||
/* affinities aren't supported because either the appropriate header files weren't found,
|
||||
or the SUPPORT_AFFINITIES macro was turned off
|
||||
*/
|
||||
bassertfalse;
|
||||
(void) affinityMask;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DynamicLibrary::open (const String& name)
|
||||
{
|
||||
close();
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
HWND beast_messageWindowHandle = 0; // (this is used by other parts of the codebase)
|
||||
|
||||
void* getUser32Function (const char* functionName)
|
||||
{
|
||||
HMODULE module = GetModuleHandleA ("user32.dll");
|
||||
@@ -72,143 +70,6 @@ void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_S
|
||||
bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) section) != FALSE; }
|
||||
void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) section); }
|
||||
|
||||
//==============================================================================
|
||||
void BEAST_API beast_threadEntryPoint (void*);
|
||||
|
||||
static unsigned int __stdcall threadEntryProc (void* userData)
|
||||
{
|
||||
if (beast_messageWindowHandle != 0)
|
||||
AttachThreadInput (GetWindowThreadProcessId (beast_messageWindowHandle, 0),
|
||||
GetCurrentThreadId(), TRUE);
|
||||
|
||||
beast_threadEntryPoint (userData);
|
||||
|
||||
_endthreadex (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Thread::launchThread()
|
||||
{
|
||||
unsigned int newThreadId;
|
||||
threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId);
|
||||
threadId = (ThreadID) newThreadId;
|
||||
}
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
CloseHandle ((HANDLE) threadHandle);
|
||||
threadId = 0;
|
||||
threadHandle = 0;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle != 0)
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
||||
#endif
|
||||
TerminateThread (threadHandle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::setCurrentThreadName (const String& name)
|
||||
{
|
||||
#if BEAST_DEBUG && BEAST_MSVC
|
||||
struct
|
||||
{
|
||||
DWORD dwType;
|
||||
LPCSTR szName;
|
||||
DWORD dwThreadID;
|
||||
DWORD dwFlags;
|
||||
} info;
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name.toUTF8();
|
||||
info.dwThreadID = GetCurrentThreadId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info);
|
||||
}
|
||||
__except (EXCEPTION_CONTINUE_EXECUTION)
|
||||
{}
|
||||
#else
|
||||
(void) name;
|
||||
#endif
|
||||
}
|
||||
|
||||
Thread::ThreadID Thread::getCurrentThreadId()
|
||||
{
|
||||
return (ThreadID) (pointer_sized_int) GetCurrentThreadId();
|
||||
}
|
||||
|
||||
bool Thread::setThreadPriority (void* handle, int priority)
|
||||
{
|
||||
int pri = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
|
||||
if (priority < 1) pri = THREAD_PRIORITY_IDLE;
|
||||
else if (priority < 2) pri = THREAD_PRIORITY_LOWEST;
|
||||
else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
else if (priority < 7) pri = THREAD_PRIORITY_NORMAL;
|
||||
else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST;
|
||||
|
||||
if (handle == 0)
|
||||
handle = GetCurrentThread();
|
||||
|
||||
return SetThreadPriority (handle, pri) != FALSE;
|
||||
}
|
||||
|
||||
void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
||||
{
|
||||
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SleepEvent
|
||||
{
|
||||
SleepEvent() noexcept
|
||||
: handle (CreateEvent (nullptr, FALSE, FALSE,
|
||||
#if BEAST_DEBUG
|
||||
_T("BEAST Sleep Event")))
|
||||
#else
|
||||
nullptr))
|
||||
#endif
|
||||
{}
|
||||
|
||||
~SleepEvent() noexcept
|
||||
{
|
||||
CloseHandle (handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
static SleepEvent sleepEvent;
|
||||
|
||||
void BEAST_CALLTYPE Thread::sleep (const int millisecs)
|
||||
{
|
||||
if (millisecs >= 10 || sleepEvent.handle == 0)
|
||||
{
|
||||
Sleep ((DWORD) millisecs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlike Sleep() this is guaranteed to return to the current thread after
|
||||
// the time expires, so we'll use this for short waits, which are more likely
|
||||
// to need to be accurate
|
||||
WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs);
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::yield()
|
||||
{
|
||||
Sleep (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int lastProcessPriority = -1;
|
||||
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Stoppable::Stoppable (char const* name, RootStoppable& root)
|
||||
: m_name (name)
|
||||
, m_root (root)
|
||||
, m_child (this)
|
||||
, 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_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
// Must not have stopping parent.
|
||||
bassert (! parent.isStopping());
|
||||
|
||||
parent.m_children.push_front (&m_child);
|
||||
}
|
||||
|
||||
Stoppable::~Stoppable ()
|
||||
{
|
||||
// Children must be stopped.
|
||||
bassert (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 ()
|
||||
{
|
||||
onStop ();
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->stopAsyncRecursive ();
|
||||
}
|
||||
|
||||
void Stoppable::stopRecursive (Journal journal)
|
||||
{
|
||||
// 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 (journal);
|
||||
|
||||
// if we get here then all children have stopped
|
||||
//
|
||||
memoryBarrier ();
|
||||
m_childrenStopped = true;
|
||||
onChildrenStopped ();
|
||||
|
||||
// Now block on this Stoppable.
|
||||
//
|
||||
bool const timedOut (! m_stoppedEvent.wait (1 * 1000)); // milliseconds
|
||||
if (timedOut)
|
||||
{
|
||||
journal.warning << "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)
|
||||
{
|
||||
}
|
||||
|
||||
RootStoppable::~RootStoppable ()
|
||||
{
|
||||
}
|
||||
|
||||
bool RootStoppable::isStopping() const
|
||||
{
|
||||
return m_calledStopAsync.get() != 0;
|
||||
}
|
||||
|
||||
void RootStoppable::prepare ()
|
||||
{
|
||||
if (! m_prepared.compareAndSetBool (1, 0))
|
||||
return;
|
||||
|
||||
prepareRecursive ();
|
||||
}
|
||||
|
||||
void RootStoppable::start ()
|
||||
{
|
||||
// Courtesy call to prepare.
|
||||
if (m_prepared.compareAndSetBool (1, 0))
|
||||
prepareRecursive ();
|
||||
|
||||
if (! m_started.compareAndSetBool (1, 0))
|
||||
return;
|
||||
|
||||
startRecursive ();
|
||||
}
|
||||
|
||||
void RootStoppable::stop (Journal journal)
|
||||
{
|
||||
if (! m_calledStop.compareAndSetBool (1, 0))
|
||||
{
|
||||
journal.warning << "Stoppable::stop called again";
|
||||
return;
|
||||
}
|
||||
|
||||
stopAsync ();
|
||||
stopRecursive (journal);
|
||||
}
|
||||
|
||||
void RootStoppable::stopAsync ()
|
||||
{
|
||||
if (! m_calledStopAsync.compareAndSetBool (1, 0))
|
||||
return;
|
||||
|
||||
stopAsyncRecursive ();
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CORE_STOPPABLE_H_INCLUDED
|
||||
#define BEAST_CORE_STOPPABLE_H_INCLUDED
|
||||
|
||||
class RootStoppable;
|
||||
|
||||
/** Provides an interface for starting and stopping.
|
||||
|
||||
A common method of structuring server or peer to peer code is to isolate
|
||||
conceptual portions of functionality into individual classes, aggregated
|
||||
into some larger "application" or "core" object which holds all the parts.
|
||||
Frequently, these components are dependent on each other in unavoidably
|
||||
complex ways. They also often use threads and perform asynchronous i/o
|
||||
operations involving sockets or other operating system objects. The process
|
||||
of starting and stopping such a system can be complex. This interface
|
||||
provides a set of behaviors for ensuring that the start and stop of a
|
||||
composite application-style object is well defined.
|
||||
|
||||
Upon the initialization of the composite object these steps are peformed:
|
||||
|
||||
1. Construct sub-components.
|
||||
|
||||
These are all typically derived from Stoppable. There can be a deep
|
||||
hierarchy: Stoppable objects may themselves have Stoppable child
|
||||
objects. This captures the relationship of dependencies.
|
||||
|
||||
2. prepare()
|
||||
|
||||
Because some components may depend on others, preparatory steps require
|
||||
that all objects be first constructed. The prepare step calls all
|
||||
Stoppable objects in the tree starting from the leaves and working up
|
||||
to the root. In this stage we are guaranteed that all objects have been
|
||||
constructed and are in a well-defined state.
|
||||
|
||||
3. onPrepare()
|
||||
|
||||
This override is called for all Stoppable objects in the hierarchy
|
||||
during the prepare stage. Objects are called from the bottom up.
|
||||
It is guaranteed that all child Stoppable objects have already been
|
||||
prepared when this is called.
|
||||
|
||||
4. start()
|
||||
|
||||
At this point all sub-components have been constructed and prepared,
|
||||
so it should be safe for them to be started. While some Stoppable
|
||||
objects may do nothing in their start function, others will start
|
||||
threads or call asynchronous i/o initiating functions like timers or
|
||||
sockets.
|
||||
|
||||
5. onStart()
|
||||
|
||||
This override is called for all Stoppable objects in the hierarchy
|
||||
during the start stage. Objects are called from the bottom up.
|
||||
It is guaranteed that all child Stoppable objects have already been
|
||||
started when this is called.
|
||||
|
||||
This is the sequence of events involved in stopping:
|
||||
|
||||
6. stopAsync() [optional]
|
||||
|
||||
This notifies the root Stoppable and all its children that a stop is
|
||||
requested.
|
||||
|
||||
7. stop()
|
||||
|
||||
This first calls stopAsync(), and then blocks on each child Stoppable in
|
||||
the in the tree from the bottom up, until the Stoppable indicates it has
|
||||
stopped. This will usually be called from the main thread of execution
|
||||
when some external signal indicates that the process should stop. For
|
||||
example, an RPC 'stop' command, or a SIGINT POSIX signal.
|
||||
|
||||
8. onStop()
|
||||
|
||||
This override is called for the root Stoppable and all its children when
|
||||
stopAsync() is called. Derived classes should cancel pending I/O and
|
||||
timers, signal that threads should exit, queue cleanup jobs, and perform
|
||||
any other necessary final actions in preparation for exit.
|
||||
|
||||
9. onChildrenStopped()
|
||||
|
||||
This override is called when all the children have stopped. This informs
|
||||
the Stoppable that there should not be any more dependents making calls
|
||||
into its member functions. A Stoppable that has no children will still
|
||||
have this function called.
|
||||
|
||||
10. stopped()
|
||||
|
||||
The derived class calls this function to inform the Stoppable API that
|
||||
it has completed the stop. This unblocks the caller of stop().
|
||||
|
||||
For stoppables which are only considered stopped when all of their
|
||||
children have stopped, and their own internal logic indicates a stop, it
|
||||
will be necessary to perform special actions in onChildrenStopped(). The
|
||||
funtion areChildrenStopped() can be used after children have stopped,
|
||||
but before the Stoppable logic itself has stopped, to determine if the
|
||||
stoppable's logic is a true stop.
|
||||
|
||||
Pseudo code for this process is as follows:
|
||||
|
||||
@code
|
||||
|
||||
// Returns `true` if derived logic has stopped.
|
||||
//
|
||||
// When the logic stops, logicProcessingStop() is no longer called.
|
||||
// If children are still active we need to wait until we get a
|
||||
// notification that the children have stopped.
|
||||
//
|
||||
bool logicHasStopped ();
|
||||
|
||||
// Called when children have stopped
|
||||
void onChildrenStopped ()
|
||||
{
|
||||
// We have stopped when the derived logic stops and children stop.
|
||||
if (logicHasStopped)
|
||||
stopped();
|
||||
}
|
||||
|
||||
// derived-specific logic that executes periodically
|
||||
void logicProcessingStep ()
|
||||
{
|
||||
// process
|
||||
// ...
|
||||
|
||||
// now see if we've stopped
|
||||
if (logicHasStopped() && areChildrenStopped())
|
||||
stopped();
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
Derived class that manage one or more threads should typically notify
|
||||
those threads in onStop that they should exit. In the thread function,
|
||||
when the last thread is about to exit it would call stopped().
|
||||
|
||||
@note A Stoppable may not be restarted.
|
||||
*/
|
||||
/** @{ */
|
||||
class Stoppable
|
||||
{
|
||||
protected:
|
||||
Stoppable (char const* name, RootStoppable& root);
|
||||
|
||||
public:
|
||||
/** Create the Stoppable. */
|
||||
Stoppable (char const* name, Stoppable& parent);
|
||||
|
||||
/** Destroy the Stoppable. */
|
||||
virtual ~Stoppable ();
|
||||
|
||||
/** Returns `true` if the stoppable should stop. */
|
||||
bool isStopping () const;
|
||||
|
||||
/** Returns `true` if the requested stop has completed. */
|
||||
bool isStopped () const;
|
||||
|
||||
/** Returns `true` if all children have stopped. */
|
||||
bool areChildrenStopped () const;
|
||||
|
||||
/** Called by derived classes to indicate that the stoppable has stopped. */
|
||||
void stopped ();
|
||||
|
||||
/** Override called during preparation.
|
||||
Since all other Stoppable objects in the tree have already been
|
||||
constructed, this provides an opportunity to perform initialization which
|
||||
depends on calling into other Stoppable objects.
|
||||
This call is made on the same thread that called prepare().
|
||||
The default implementation does nothing.
|
||||
Guaranteed to only be called once.
|
||||
*/
|
||||
virtual void onPrepare ();
|
||||
|
||||
/** Override called during start. */
|
||||
virtual void onStart ();
|
||||
|
||||
/** Override called when the stop notification is issued.
|
||||
|
||||
The call is made on an unspecified, implementation-specific thread.
|
||||
onStop and onChildrenStopped will never be called concurrently, across
|
||||
all Stoppable objects descended from the same root, inclusive of the
|
||||
root.
|
||||
|
||||
It is safe to call isStopping, isStopped, and areChildrenStopped from
|
||||
within this function; The values returned will always be valid and never
|
||||
change during the callback.
|
||||
|
||||
The default implementation simply calls stopped(). This is applicable
|
||||
when the Stoppable has a trivial stop operation (or no stop operation),
|
||||
and we are merely using the Stoppable API to position it as a dependency
|
||||
of some parent service.
|
||||
|
||||
Thread safety:
|
||||
May not block for long periods.
|
||||
Guaranteed only to be called once.
|
||||
Must be safe to call from any thread at any time.
|
||||
*/
|
||||
virtual void onStop ();
|
||||
|
||||
/** Override called when all children have stopped.
|
||||
|
||||
The call is made on an unspecified, implementation-specific thread.
|
||||
onStop and onChildrenStopped will never be called concurrently, across
|
||||
all Stoppable objects descended from the same root, inclusive of the
|
||||
root.
|
||||
|
||||
It is safe to call isStopping, isStopped, and areChildrenStopped from
|
||||
within this function; The values returned will always be valid and never
|
||||
change during the callback.
|
||||
|
||||
The default implementation does nothing.
|
||||
|
||||
Thread safety:
|
||||
May not block for long periods.
|
||||
Guaranteed only to be called once.
|
||||
Must be safe to call from any thread at any time.
|
||||
*/
|
||||
virtual void onChildrenStopped ();
|
||||
|
||||
private:
|
||||
friend class RootStoppable;
|
||||
|
||||
struct Child;
|
||||
typedef LockFreeStack <Child> Children;
|
||||
|
||||
struct Child : Children::Node
|
||||
{
|
||||
Child (Stoppable* stoppable_) : stoppable (stoppable_)
|
||||
{
|
||||
}
|
||||
|
||||
Stoppable* stoppable;
|
||||
};
|
||||
|
||||
void prepareRecursive ();
|
||||
void startRecursive ();
|
||||
void stopAsyncRecursive ();
|
||||
void stopRecursive (Journal journal);
|
||||
|
||||
protected:
|
||||
char const* m_name;
|
||||
RootStoppable& m_root;
|
||||
Child m_child;
|
||||
bool volatile m_stopped;
|
||||
bool volatile m_childrenStopped;
|
||||
Children m_children;
|
||||
WaitableEvent m_stoppedEvent;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class RootStoppable : public Stoppable
|
||||
{
|
||||
public:
|
||||
explicit RootStoppable (char const* name);
|
||||
|
||||
~RootStoppable ();
|
||||
|
||||
bool isStopping() const;
|
||||
|
||||
/** Prepare all contained Stoppable objects.
|
||||
This calls onPrepare for all Stoppable objects in the tree.
|
||||
Calls made after the first have no effect.
|
||||
Thread safety:
|
||||
May be called from any thread.
|
||||
*/
|
||||
void prepare ();
|
||||
|
||||
/** Start all contained Stoppable objects.
|
||||
The default implementation does nothing.
|
||||
Calls made after the first have no effect.
|
||||
Thread safety:
|
||||
May be called from any thread.
|
||||
*/
|
||||
void start ();
|
||||
|
||||
/** Notify a root stoppable and children to stop, and block until stopped.
|
||||
Has no effect if the stoppable was already notified.
|
||||
This blocks until the stoppable and all of its children have stopped.
|
||||
Thread safety:
|
||||
Safe to call from any thread not associated with a Stoppable.
|
||||
*/
|
||||
void stop (Journal journal = Journal());
|
||||
|
||||
/** Notify a root stoppable and children to stop, without waiting.
|
||||
Has no effect if the stoppable was already notified.
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
void stopAsync ();
|
||||
|
||||
private:
|
||||
Atomic <int> m_prepared;
|
||||
Atomic <int> m_started;
|
||||
Atomic <int> m_calledStop;
|
||||
Atomic <int> m_calledStopAsync;
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ReadWriteMutex::ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
ReadWriteMutex::~ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterRead () const noexcept
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// attempt the lock optimistically
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
m_readers->addref ();
|
||||
|
||||
// is there a writer?
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
if (m_writes->isSignaled ())
|
||||
{
|
||||
// a writer exists, give up the read lock
|
||||
m_readers->release ();
|
||||
|
||||
// block until the writer is done
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
}
|
||||
|
||||
// now try the loop again
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitRead () const noexcept
|
||||
{
|
||||
m_readers->release ();
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterWrite () const noexcept
|
||||
{
|
||||
// Optimistically acquire the write lock.
|
||||
m_writes->addref ();
|
||||
|
||||
// Go for the mutex.
|
||||
// Another writer might block us here.
|
||||
m_mutex.enter ();
|
||||
|
||||
// Only one competing writer will get here,
|
||||
// but we don't know who, so we have to drain
|
||||
// readers no matter what. New readers will be
|
||||
// blocked by the mutex.
|
||||
//
|
||||
if (m_readers->isSignaled ())
|
||||
{
|
||||
SpinDelay delay;
|
||||
|
||||
do
|
||||
{
|
||||
delay.pause ();
|
||||
}
|
||||
while (m_readers->isSignaled ());
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitWrite () const noexcept
|
||||
{
|
||||
// Releasing the mutex first and then decrementing the
|
||||
// writer count allows another waiting writer to atomically
|
||||
// acquire the lock, thus starving readers. This fulfills
|
||||
// the write-preferencing requirement.
|
||||
|
||||
m_mutex.exit ();
|
||||
|
||||
m_writes->release ();
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_READWRITEMUTEX_H_INCLUDED
|
||||
#define BEAST_READWRITEMUTEX_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Multiple consumer, single producer (MCSP) synchronization.
|
||||
|
||||
This is an optimized lock for the multiple reader, single writer
|
||||
scenario. It provides only a subset of features of the more general
|
||||
traditional read/write lock. Specifically, these rules apply:
|
||||
|
||||
- A caller cannot hold a read lock while acquiring a write lock.
|
||||
|
||||
- Write locks are only recursive with respect to write locks.
|
||||
|
||||
- Read locks are only recursive with respect to read locks.
|
||||
|
||||
- A write lock cannot be downgraded.
|
||||
|
||||
- Writes are preferenced over reads.
|
||||
|
||||
For real-time applications, these restrictions are often not an issue.
|
||||
|
||||
The implementation is wait-free in the fast path: acquiring read access
|
||||
for a lock without contention - just one interlocked increment!
|
||||
|
||||
@class ReadWriteMutex
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped read lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedReadLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedReadLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterRead ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedReadLock () noexcept
|
||||
{
|
||||
m_lock.exitRead ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped write lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedWriteLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedWriteLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterWrite ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedWriteLock () noexcept
|
||||
{
|
||||
m_lock.exitWrite ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class BEAST_API ReadWriteMutex
|
||||
{
|
||||
public:
|
||||
/** Provides the type of scoped read lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedReadLock <ReadWriteMutex> ScopedReadLockType;
|
||||
|
||||
/** Provides the type of scoped write lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedWriteLock <ReadWriteMutex> ScopedWriteLockType;
|
||||
|
||||
/** Create a ReadWriteMutex */
|
||||
ReadWriteMutex () noexcept;
|
||||
|
||||
/** Destroy a ReadWriteMutex
|
||||
|
||||
If the object is destroyed while a lock is held, the result is
|
||||
undefined behavior.
|
||||
*/
|
||||
~ReadWriteMutex () noexcept;
|
||||
|
||||
/** Acquire a read lock.
|
||||
|
||||
This is recursive with respect to other read locks. Calling this while
|
||||
holding a write lock is undefined.
|
||||
*/
|
||||
void enterRead () const noexcept;
|
||||
|
||||
/** Release a previously acquired read lock */
|
||||
void exitRead () const noexcept;
|
||||
|
||||
/** Acquire a write lock.
|
||||
|
||||
This is recursive with respect to other write locks. Calling this while
|
||||
holding a read lock is undefined.
|
||||
*/
|
||||
void enterWrite () const noexcept;
|
||||
|
||||
/** Release a previously acquired write lock */
|
||||
void exitWrite () const noexcept;
|
||||
|
||||
private:
|
||||
CriticalSection m_mutex;
|
||||
|
||||
mutable CacheLine::Padded <AtomicCounter> m_writes;
|
||||
mutable CacheLine::Padded <AtomicCounter> m_readers;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,376 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Thread::Thread (const String& threadName_)
|
||||
: threadName (threadName_),
|
||||
threadHandle (nullptr),
|
||||
threadId (0),
|
||||
threadPriority (5),
|
||||
affinityMask (0),
|
||||
shouldExit (false)
|
||||
{
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
/* If your thread class's destructor has been called without first stopping the thread, that
|
||||
means that this partially destructed object is still performing some work - and that's
|
||||
probably a Bad Thing!
|
||||
|
||||
To avoid this type of nastiness, always make sure you call stopThread() before or during
|
||||
your subclass's destructor.
|
||||
*/
|
||||
check_precondition (! isThreadRunning());
|
||||
|
||||
stopThread ();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Use a ref-counted object to hold this shared data, so that it can outlive its static
|
||||
// shared pointer when threads are still running during static shutdown.
|
||||
struct CurrentThreadHolder : public SharedObject
|
||||
{
|
||||
CurrentThreadHolder() noexcept {}
|
||||
|
||||
typedef SharedPtr <CurrentThreadHolder> Ptr;
|
||||
ThreadLocalValue<Thread*> value;
|
||||
};
|
||||
|
||||
static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros).
|
||||
|
||||
static SpinLock* castToSpinLockWithoutAliasingWarning (void* s)
|
||||
{
|
||||
return static_cast<SpinLock*> (s);
|
||||
}
|
||||
|
||||
static CurrentThreadHolder::Ptr getCurrentThreadHolder()
|
||||
{
|
||||
static CurrentThreadHolder::Ptr currentThreadHolder;
|
||||
SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock));
|
||||
|
||||
if (currentThreadHolder == nullptr)
|
||||
currentThreadHolder = new CurrentThreadHolder();
|
||||
|
||||
return currentThreadHolder;
|
||||
}
|
||||
|
||||
void Thread::threadEntryPoint()
|
||||
{
|
||||
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
||||
currentThreadHolder->value = this;
|
||||
|
||||
if (threadName.isNotEmpty())
|
||||
setCurrentThreadName (threadName);
|
||||
|
||||
if (startSuspensionEvent.wait (10000))
|
||||
{
|
||||
bassert (getCurrentThreadId() == threadId);
|
||||
|
||||
if (affinityMask != 0)
|
||||
setCurrentThreadAffinityMask (affinityMask);
|
||||
|
||||
run();
|
||||
}
|
||||
|
||||
currentThreadHolder->value.releaseCurrentThreadStorage();
|
||||
closeThreadHandle();
|
||||
}
|
||||
|
||||
// used to wrap the incoming call from the platform-specific code
|
||||
void BEAST_API beast_threadEntryPoint (void* userData)
|
||||
{
|
||||
static_cast <Thread*> (userData)->threadEntryPoint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::startThread()
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
launchThread();
|
||||
setThreadPriority (threadHandle, threadPriority);
|
||||
startSuspensionEvent.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::startThread (const int priority)
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
threadPriority = priority;
|
||||
startThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPriority (priority);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::isThreadRunning() const
|
||||
{
|
||||
return threadHandle != nullptr;
|
||||
}
|
||||
|
||||
Thread* Thread::getCurrentThread()
|
||||
{
|
||||
return getCurrentThreadHolder()->value.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::signalThreadShouldExit()
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
|
||||
{
|
||||
// Doh! So how exactly do you expect this thread to wait for itself to stop??
|
||||
bassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0);
|
||||
|
||||
const uint32 timeoutEnd = Time::getMillisecondCounter() + (uint32) timeOutMilliseconds;
|
||||
|
||||
while (isThreadRunning())
|
||||
{
|
||||
if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd)
|
||||
return false;
|
||||
|
||||
sleep (2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thread::stopThread (const int timeOutMilliseconds)
|
||||
{
|
||||
bool cleanExit = true;
|
||||
|
||||
// agh! You can't stop the thread that's calling this method! How on earth
|
||||
// would that work??
|
||||
bassert (getCurrentThreadId() != getThreadId());
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
notify();
|
||||
|
||||
if (timeOutMilliseconds != 0)
|
||||
{
|
||||
cleanExit = waitForThreadToExit (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
bassert (! cleanExit);
|
||||
|
||||
// very bad karma if this point is reached, as there are bound to be
|
||||
// locks and events left in silly states when a thread is killed by force..
|
||||
killThread();
|
||||
|
||||
threadHandle = nullptr;
|
||||
threadId = 0;
|
||||
|
||||
cleanExit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanExit = true;
|
||||
}
|
||||
}
|
||||
|
||||
return cleanExit;
|
||||
}
|
||||
|
||||
void Thread::stopThreadAsync ()
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::setPriority (const int newPriority)
|
||||
{
|
||||
// NB: deadlock possible if you try to set the thread prio from the thread itself,
|
||||
// so using setCurrentThreadPriority instead in that case.
|
||||
if (getCurrentThreadId() == getThreadId())
|
||||
return setCurrentThreadPriority (newPriority);
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (setThreadPriority (threadHandle, newPriority))
|
||||
{
|
||||
threadPriority = newPriority;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thread::setCurrentThreadPriority (const int newPriority)
|
||||
{
|
||||
return setThreadPriority (0, newPriority);
|
||||
}
|
||||
|
||||
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||
{
|
||||
affinityMask = newAffinityMask;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::wait (const int timeOutMilliseconds) const
|
||||
{
|
||||
return defaultEvent.wait (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
void Thread::notify() const
|
||||
{
|
||||
defaultEvent.signal();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SpinLock::enter() const noexcept
|
||||
{
|
||||
if (! tryEnter())
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
if (tryEnter())
|
||||
return;
|
||||
|
||||
while (! tryEnter())
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class AtomicTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AtomicTests() : UnitTest ("Atomic", "beast") {}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("Misc");
|
||||
|
||||
char a1[7];
|
||||
expect (numElementsInArray(a1) == 7);
|
||||
int a2[3];
|
||||
expect (numElementsInArray(a2) == 3);
|
||||
|
||||
expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
|
||||
expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
|
||||
expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211));
|
||||
|
||||
beginTestCase ("int");
|
||||
AtomicTester <int>::testInteger (*this);
|
||||
beginTestCase ("unsigned int");
|
||||
AtomicTester <unsigned int>::testInteger (*this);
|
||||
beginTestCase ("int32");
|
||||
AtomicTester <int32>::testInteger (*this);
|
||||
beginTestCase ("uint32");
|
||||
AtomicTester <uint32>::testInteger (*this);
|
||||
beginTestCase ("long");
|
||||
AtomicTester <long>::testInteger (*this);
|
||||
beginTestCase ("void*");
|
||||
AtomicTester <void*>::testInteger (*this);
|
||||
beginTestCase ("int*");
|
||||
AtomicTester <int*>::testInteger (*this);
|
||||
beginTestCase ("float");
|
||||
AtomicTester <float>::testFloat (*this);
|
||||
#if ! BEAST_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms
|
||||
beginTestCase ("int64");
|
||||
AtomicTester <int64>::testInteger (*this);
|
||||
beginTestCase ("uint64");
|
||||
AtomicTester <uint64>::testInteger (*this);
|
||||
beginTestCase ("double");
|
||||
AtomicTester <double>::testFloat (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
class AtomicTester
|
||||
{
|
||||
public:
|
||||
AtomicTester() {}
|
||||
|
||||
static void testInteger (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a.set ((Type) 10);
|
||||
test.expect (a.value == (Type) 10);
|
||||
test.expect (a.get() == (Type) 10);
|
||||
a += (Type) 15;
|
||||
test.expect (a.get() == (Type) 25);
|
||||
memoryBarrier();
|
||||
a -= (Type) 5;
|
||||
test.expect (a.get() == (Type) 20);
|
||||
test.expect (++a == (Type) 21);
|
||||
++a;
|
||||
test.expect (--a == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
memoryBarrier();
|
||||
|
||||
testFloat (test);
|
||||
}
|
||||
|
||||
static void testFloat (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a = (Type) 21;
|
||||
memoryBarrier();
|
||||
|
||||
/* These are some simple test cases to check the atomics - let me know
|
||||
if any of these assertions fail on your system!
|
||||
*/
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (a.compareAndSetBool ((Type) 200, a.get()));
|
||||
test.expect (a.get() == (Type) 200);
|
||||
|
||||
test.expect (a.exchange ((Type) 300) == (Type) 200);
|
||||
test.expect (a.get() == (Type) 300);
|
||||
|
||||
b = a;
|
||||
test.expect (b.get() == a.get());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static AtomicTests atomicTests;
|
||||
@@ -1,285 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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_JUCE_THREAD_H_INCLUDED
|
||||
#define BEAST_JUCE_THREAD_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates a thread.
|
||||
|
||||
Subclasses derive from Thread and implement the run() method, in which they
|
||||
do their business. The thread can then be started with the startThread() method
|
||||
and controlled with various other methods.
|
||||
|
||||
This class also contains some thread-related static methods, such
|
||||
as sleep(), yield(), getCurrentThreadId() etc.
|
||||
|
||||
@see CriticalSection, WaitableEvent, Process, ThreadWithProgressWindow,
|
||||
MessageManagerLock
|
||||
*/
|
||||
class BEAST_API Thread : LeakChecked <Thread>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a thread.
|
||||
|
||||
When first created, the thread is not running. Use the startThread()
|
||||
method to start it.
|
||||
*/
|
||||
explicit Thread (const String& threadName);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If the thread has not been stopped first, this will generate a fatal error.
|
||||
*/
|
||||
virtual ~Thread();
|
||||
|
||||
//==============================================================================
|
||||
/** Must be implemented to perform the thread's actual code.
|
||||
|
||||
Remember that the thread must regularly check the threadShouldExit()
|
||||
method whilst running, and if this returns true it should return from
|
||||
the run() method as soon as possible to avoid being forcibly killed.
|
||||
|
||||
@see threadShouldExit, startThread
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
//==============================================================================
|
||||
// Thread control functions..
|
||||
|
||||
/** Starts the thread running.
|
||||
|
||||
This will start the thread's run() method.
|
||||
(if it's already started, startThread() won't do anything).
|
||||
|
||||
@see stopThread
|
||||
*/
|
||||
void startThread();
|
||||
|
||||
/** Starts the thread with a given priority.
|
||||
|
||||
Launches the thread with a given priority, where 0 = lowest, 10 = highest.
|
||||
If the thread is already running, its priority will be changed.
|
||||
|
||||
@see startThread, setPriority
|
||||
*/
|
||||
void startThread (int priority);
|
||||
|
||||
/** Attempts to stop the thread running.
|
||||
|
||||
This method will cause the threadShouldExit() method to return true
|
||||
and call notify() in case the thread is currently waiting.
|
||||
|
||||
Hopefully the thread will then respond to this by exiting cleanly, and
|
||||
the stopThread method will wait for a given time-period for this to
|
||||
happen.
|
||||
|
||||
If the thread is stuck and fails to respond after the time-out, it gets
|
||||
forcibly killed, which is a very bad thing to happen, as it could still
|
||||
be holding locks, etc. which are needed by other parts of your program.
|
||||
|
||||
@param timeOutMilliseconds The number of milliseconds to wait for the
|
||||
thread to finish before killing it by force. A negative
|
||||
value in here will wait forever.
|
||||
@see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning
|
||||
|
||||
@returns true if the thread exits, or false if the timeout expires first.
|
||||
*/
|
||||
bool stopThread (int timeOutMilliseconds = -1);
|
||||
|
||||
/** Stop the thread without blocking.
|
||||
This calls signalThreadShouldExit followed by notify.
|
||||
*/
|
||||
void stopThreadAsync ();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the thread is currently active */
|
||||
bool isThreadRunning() const;
|
||||
|
||||
/** Sets a flag to tell the thread it should stop.
|
||||
|
||||
Calling this means that the threadShouldExit() method will then return true.
|
||||
The thread should be regularly checking this to see whether it should exit.
|
||||
|
||||
If your thread makes use of wait(), you might want to call notify() after calling
|
||||
this method, to interrupt any waits that might be in progress, and allow it
|
||||
to reach a point where it can exit.
|
||||
|
||||
@see threadShouldExit
|
||||
@see waitForThreadToExit
|
||||
*/
|
||||
void signalThreadShouldExit();
|
||||
|
||||
/** Checks whether the thread has been told to stop running.
|
||||
|
||||
Threads need to check this regularly, and if it returns true, they should
|
||||
return from their run() method at the first possible opportunity.
|
||||
|
||||
@see signalThreadShouldExit
|
||||
*/
|
||||
inline bool threadShouldExit() const { return shouldExit; }
|
||||
|
||||
/** Waits for the thread to stop.
|
||||
|
||||
This will waits until isThreadRunning() is false or until a timeout expires.
|
||||
|
||||
@param timeOutMilliseconds the time to wait, in milliseconds. If this value
|
||||
is less than zero, it will wait forever.
|
||||
@returns true if the thread exits, or false if the timeout expires first.
|
||||
*/
|
||||
bool waitForThreadToExit (int timeOutMilliseconds = -1) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the thread's priority.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
|
||||
@param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority
|
||||
of 5 is normal.
|
||||
*/
|
||||
bool setPriority (int priority);
|
||||
|
||||
/** Changes the priority of the caller thread.
|
||||
|
||||
Similar to setPriority(), but this static method acts on the caller thread.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
|
||||
@see setPriority
|
||||
*/
|
||||
static bool setCurrentThreadPriority (int priority);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the affinity mask for the thread.
|
||||
|
||||
This will only have an effect next time the thread is started - i.e. if the
|
||||
thread is already running when called, it'll have no effect.
|
||||
|
||||
@see setCurrentThreadAffinityMask
|
||||
*/
|
||||
void setAffinityMask (uint32 affinityMask);
|
||||
|
||||
/** Changes the affinity mask for the caller thread.
|
||||
|
||||
This will change the affinity mask for the thread that calls this static method.
|
||||
|
||||
@see setAffinityMask
|
||||
*/
|
||||
static void setCurrentThreadAffinityMask (uint32 affinityMask);
|
||||
|
||||
//==============================================================================
|
||||
// this can be called from any thread that needs to pause..
|
||||
static void BEAST_CALLTYPE sleep (int milliseconds);
|
||||
|
||||
/** Yields the calling thread's current time-slot. */
|
||||
static void BEAST_CALLTYPE yield();
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the thread wait for a notification.
|
||||
|
||||
This puts the thread to sleep until either the timeout period expires, or
|
||||
another thread calls the notify() method to wake it up.
|
||||
|
||||
A negative time-out value means that the method will wait indefinitely.
|
||||
|
||||
@returns true if the event has been signalled, false if the timeout expires.
|
||||
*/
|
||||
bool wait (int timeOutMilliseconds = -1) const;
|
||||
|
||||
/** Wakes up the thread.
|
||||
|
||||
If the thread has called the wait() method, this will wake it up.
|
||||
|
||||
@see wait
|
||||
*/
|
||||
void notify() const;
|
||||
|
||||
//==============================================================================
|
||||
/** A value type used for thread IDs.
|
||||
@see getCurrentThreadId(), getThreadId()
|
||||
*/
|
||||
typedef void* ThreadID;
|
||||
|
||||
/** Returns an id that identifies the caller thread.
|
||||
|
||||
To find the ID of a particular thread object, use getThreadId().
|
||||
|
||||
@returns a unique identifier that identifies the calling thread.
|
||||
@see getThreadId
|
||||
*/
|
||||
static ThreadID getCurrentThreadId();
|
||||
|
||||
/** Finds the thread object that is currently running.
|
||||
|
||||
Note that the main UI thread (or other non-Beast threads) don't have a Thread
|
||||
object associated with them, so this will return 0.
|
||||
*/
|
||||
static Thread* getCurrentThread();
|
||||
|
||||
/** Returns the ID of this thread.
|
||||
|
||||
That means the ID of this thread object - not of the thread that's calling the method.
|
||||
|
||||
This can change when the thread is started and stopped, and will be invalid if the
|
||||
thread's not actually running.
|
||||
|
||||
@see getCurrentThreadId
|
||||
*/
|
||||
ThreadID getThreadId() const noexcept { return threadId; }
|
||||
|
||||
/** Returns the name of the thread.
|
||||
|
||||
This is the name that gets set in the constructor.
|
||||
*/
|
||||
const String& getThreadName() const { return threadName; }
|
||||
|
||||
/** Changes the name of the caller thread.
|
||||
Different OSes may place different length or content limits on this name.
|
||||
*/
|
||||
static void setCurrentThreadName (const String& newThreadName);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const String threadName;
|
||||
void* volatile threadHandle;
|
||||
ThreadID threadId;
|
||||
CriticalSection startStopLock;
|
||||
WaitableEvent startSuspensionEvent, defaultEvent;
|
||||
int threadPriority;
|
||||
uint32 affinityMask;
|
||||
bool volatile shouldExit;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
friend void BEAST_API beast_threadEntryPoint (void*);
|
||||
#endif
|
||||
|
||||
void launchThread();
|
||||
void closeThreadHandle();
|
||||
void killThread();
|
||||
void threadEntryPoint();
|
||||
static bool setThreadPriority (void*, int);
|
||||
};
|
||||
|
||||
#endif // BEAST_THREAD_H_INCLUDED
|
||||
@@ -1,371 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ThreadPoolJob::ThreadPoolJob (const String& name)
|
||||
: jobName (name),
|
||||
pool (nullptr),
|
||||
shouldStop (false),
|
||||
isActive (false),
|
||||
shouldBeDeleted (false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadPoolJob::~ThreadPoolJob()
|
||||
{
|
||||
// you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
|
||||
// to remove it first!
|
||||
bassert (pool == nullptr || ! pool->contains (this));
|
||||
}
|
||||
|
||||
String ThreadPoolJob::getJobName() const
|
||||
{
|
||||
return jobName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::setJobName (const String& newName)
|
||||
{
|
||||
jobName = newName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::signalJobShouldExit()
|
||||
{
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ThreadPool::ThreadPoolThread
|
||||
: public Thread
|
||||
, LeakChecked <ThreadPoolThread>
|
||||
{
|
||||
public:
|
||||
ThreadPoolThread (ThreadPool& pool_)
|
||||
: Thread ("Pool"),
|
||||
pool (pool_)
|
||||
{
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (! pool.runNextJob())
|
||||
wait (500);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadPool& pool;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool::ThreadPool (const int numThreads)
|
||||
{
|
||||
bassert (numThreads > 0); // not much point having a pool without any threads!
|
||||
|
||||
createThreads (numThreads);
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool()
|
||||
{
|
||||
createThreads (SystemStats::getNumCpus());
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
removeAllJobs (true, 5000);
|
||||
stopThreads();
|
||||
}
|
||||
|
||||
void ThreadPool::createThreads (int numThreads)
|
||||
{
|
||||
for (int i = bmax (1, numThreads); --i >= 0;)
|
||||
threads.add (new ThreadPoolThread (*this));
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->startThread();
|
||||
}
|
||||
|
||||
void ThreadPool::stopThreads()
|
||||
{
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->signalThreadShouldExit();
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->stopThread (500);
|
||||
}
|
||||
|
||||
void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinished)
|
||||
{
|
||||
bassert (job != nullptr);
|
||||
bassert (job->pool == nullptr);
|
||||
|
||||
if (job->pool == nullptr)
|
||||
{
|
||||
job->pool = this;
|
||||
job->shouldStop = false;
|
||||
job->isActive = false;
|
||||
job->shouldBeDeleted = deleteJobWhenFinished;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
jobs.add (job);
|
||||
}
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->notify();
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadPool::getNumJobs() const
|
||||
{
|
||||
return jobs.size();
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::getJob (const int index) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs [index];
|
||||
}
|
||||
|
||||
bool ThreadPool::contains (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job));
|
||||
}
|
||||
|
||||
bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job)) && job->isActive;
|
||||
}
|
||||
|
||||
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
|
||||
const int timeOutMs) const
|
||||
{
|
||||
if (job != nullptr)
|
||||
{
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
while (contains (job))
|
||||
{
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (2);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThreadPool::removeJob (ThreadPoolJob* const job,
|
||||
const bool interruptIfRunning,
|
||||
const int timeOutMs)
|
||||
{
|
||||
bool dontWait = true;
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
if (job != nullptr)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
if (interruptIfRunning)
|
||||
job->signalJobShouldExit();
|
||||
|
||||
dontWait = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dontWait || waitForJobToFinish (job, timeOutMs);
|
||||
}
|
||||
|
||||
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs,
|
||||
ThreadPool::JobSelector* selectedJobsToRemove)
|
||||
{
|
||||
Array <ThreadPoolJob*> jobsToWaitFor;
|
||||
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = jobs.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
|
||||
if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
jobsToWaitFor.add (job);
|
||||
|
||||
if (interruptRunningJobs)
|
||||
job->signalJobShouldExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (int i = jobsToWaitFor.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobsToWaitFor.getUnchecked (i);
|
||||
|
||||
if (! isJobRunning (job))
|
||||
jobsToWaitFor.remove (i);
|
||||
}
|
||||
|
||||
if (jobsToWaitFor.size() == 0)
|
||||
break;
|
||||
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const
|
||||
{
|
||||
StringArray s;
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
const ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
if (job->isActive || ! onlyReturnActiveJobs)
|
||||
s.add (job->getJobName());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ThreadPool::setThreadPriorities (const int newPriority)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
if (! threads.getUnchecked(i)->setPriority (newPriority))
|
||||
ok = false;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::pickNextJobToRun()
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
ThreadPoolJob* job = jobs[i];
|
||||
|
||||
if (job != nullptr && ! job->isActive)
|
||||
{
|
||||
if (job->shouldStop)
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
job->isActive = true;
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ThreadPool::runNextJob()
|
||||
{
|
||||
ThreadPoolJob* const job = pickNextJobToRun();
|
||||
|
||||
if (job == nullptr)
|
||||
return false;
|
||||
|
||||
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
|
||||
|
||||
result = job->runJob();
|
||||
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
job->isActive = false;
|
||||
|
||||
if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
|
||||
jobFinishedSignal.signal();
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the job to the end of the queue if it wants another go
|
||||
jobs.move (jobs.indexOf (job), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadPool::addToDeleteList (OwnedArray<ThreadPoolJob>& deletionList, ThreadPoolJob* const job) const
|
||||
{
|
||||
job->shouldStop = true;
|
||||
job->pool = nullptr;
|
||||
|
||||
if (job->shouldBeDeleted)
|
||||
deletionList.add (job);
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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_THREADPOOL_H_INCLUDED
|
||||
#define BEAST_THREADPOOL_H_INCLUDED
|
||||
|
||||
class ThreadPool;
|
||||
class ThreadPoolThread;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A task that is executed by a ThreadPool object.
|
||||
|
||||
A ThreadPool keeps a list of ThreadPoolJob objects which are executed by
|
||||
its threads.
|
||||
|
||||
The runJob() method needs to be implemented to do the task, and if the code that
|
||||
does the work takes a significant time to run, it must keep checking the shouldExit()
|
||||
method to see if something is trying to interrupt the job. If shouldExit() returns
|
||||
true, the runJob() method must return immediately.
|
||||
|
||||
@see ThreadPool, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPoolJob : LeakChecked <ThreadPoolJob>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool job object.
|
||||
After creating your job, add it to a thread pool with ThreadPool::addJob().
|
||||
*/
|
||||
explicit ThreadPoolJob (const String& name);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ThreadPoolJob();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the name of this job.
|
||||
@see setJobName
|
||||
*/
|
||||
String getJobName() const;
|
||||
|
||||
/** Changes the job's name.
|
||||
@see getJobName
|
||||
*/
|
||||
void setJobName (const String& newName);
|
||||
|
||||
//==============================================================================
|
||||
/** These are the values that can be returned by the runJob() method.
|
||||
*/
|
||||
enum JobStatus
|
||||
{
|
||||
jobHasFinished = 0, /**< indicates that the job has finished and can be
|
||||
removed from the pool. */
|
||||
|
||||
jobNeedsRunningAgain /**< indicates that the job would like to be called
|
||||
again when a thread is free. */
|
||||
};
|
||||
|
||||
/** Peforms the actual work that this job needs to do.
|
||||
|
||||
Your subclass must implement this method, in which is does its work.
|
||||
|
||||
If the code in this method takes a significant time to run, it must repeatedly check
|
||||
the shouldExit() method to see if something is trying to interrupt the job.
|
||||
If shouldExit() ever returns true, the runJob() method must return immediately.
|
||||
|
||||
If this method returns jobHasFinished, then the job will be removed from the pool
|
||||
immediately. If it returns jobNeedsRunningAgain, then the job will be left in the
|
||||
pool and will get a chance to run again as soon as a thread is free.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
virtual JobStatus runJob() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this job is currently running its runJob() method. */
|
||||
bool isRunning() const noexcept { return isActive; }
|
||||
|
||||
/** Returns true if something is trying to interrupt this job and make it stop.
|
||||
|
||||
Your runJob() method must call this whenever it gets a chance, and if it ever
|
||||
returns true, the runJob() method must return immediately.
|
||||
|
||||
@see signalJobShouldExit()
|
||||
*/
|
||||
bool shouldExit() const noexcept { return shouldStop; }
|
||||
|
||||
/** Calling this will cause the shouldExit() method to return true, and the job
|
||||
should (if it's been implemented correctly) stop as soon as possible.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
void signalJobShouldExit();
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
friend class ThreadPool;
|
||||
friend class ThreadPoolThread;
|
||||
String jobName;
|
||||
ThreadPool* pool;
|
||||
bool shouldStop, isActive, shouldBeDeleted;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of threads that will run a list of jobs.
|
||||
|
||||
When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method
|
||||
will be called by the next pooled thread that becomes free.
|
||||
|
||||
@see ThreadPoolJob, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPool : LeakChecked <ThreadPool>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
@param numberOfThreads the number of threads to run. These will be started
|
||||
immediately, and will run until the pool is deleted.
|
||||
*/
|
||||
ThreadPool (int numberOfThreads);
|
||||
|
||||
/** Creates a thread pool with one thread per CPU core.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
If you want to specify the number of threads, use the other constructor; this
|
||||
one creates a pool which has one thread for each CPU core.
|
||||
@see SystemStats::getNumCpus()
|
||||
*/
|
||||
ThreadPool();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
This will attempt to remove all the jobs before deleting, but if you want to
|
||||
specify a timeout, you should call removeAllJobs() explicitly before deleting
|
||||
the pool.
|
||||
*/
|
||||
~ThreadPool();
|
||||
|
||||
//==============================================================================
|
||||
/** A callback class used when you need to select which ThreadPoolJob objects are suitable
|
||||
for some kind of operation.
|
||||
@see ThreadPool::removeAllJobs
|
||||
*/
|
||||
class BEAST_API JobSelector
|
||||
{
|
||||
public:
|
||||
virtual ~JobSelector() {}
|
||||
|
||||
/** Should return true if the specified thread matches your criteria for whatever
|
||||
operation that this object is being used for.
|
||||
|
||||
Any implementation of this method must be extremely fast and thread-safe!
|
||||
*/
|
||||
virtual bool isJobSuitable (ThreadPoolJob* job) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a job to the queue.
|
||||
|
||||
Once a job has been added, then the next time a thread is free, it will run
|
||||
the job's ThreadPoolJob::runJob() method. Depending on the return value of the
|
||||
runJob() method, the pool will either remove the job from the pool or add it to
|
||||
the back of the queue to be run again.
|
||||
|
||||
If deleteJobWhenFinished is true, then the job object will be owned and deleted by
|
||||
the pool when not needed - if you do this, make sure that your object's destructor
|
||||
is thread-safe.
|
||||
|
||||
If deleteJobWhenFinished is false, the pointer will be used but not deleted, and
|
||||
the caller is responsible for making sure the object is not deleted before it has
|
||||
been removed from the pool.
|
||||
*/
|
||||
void addJob (ThreadPoolJob* job,
|
||||
bool deleteJobWhenFinished);
|
||||
|
||||
/** Tries to remove a job from the pool.
|
||||
|
||||
If the job isn't yet running, this will simply remove it. If it is running, it
|
||||
will wait for it to finish.
|
||||
|
||||
If the timeout period expires before the job finishes running, then the job will be
|
||||
left in the pool and this will return false. It returns true if the job is sucessfully
|
||||
stopped and removed.
|
||||
|
||||
@param job the job to remove
|
||||
@param interruptIfRunning if true, then if the job is currently busy, its
|
||||
ThreadPoolJob::signalJobShouldExit() method will be called to try
|
||||
to interrupt it. If false, then if the job will be allowed to run
|
||||
until it stops normally (or the timeout expires)
|
||||
@param timeOutMilliseconds the length of time this method should wait for the job to finish
|
||||
before giving up and returning false
|
||||
*/
|
||||
bool removeJob (ThreadPoolJob* job,
|
||||
bool interruptIfRunning,
|
||||
int timeOutMilliseconds);
|
||||
|
||||
/** Tries to remove all jobs from the pool.
|
||||
|
||||
@param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit()
|
||||
methods called to try to interrupt them
|
||||
@param timeOutMilliseconds the length of time this method should wait for all the jobs to finish
|
||||
before giving up and returning false
|
||||
@param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which
|
||||
jobs should be removed. If it is zero, all jobs are removed
|
||||
@returns true if all jobs are successfully stopped and removed; false if the timeout period
|
||||
expires while waiting for one or more jobs to stop
|
||||
*/
|
||||
bool removeAllJobs (bool interruptRunningJobs,
|
||||
int timeOutMilliseconds,
|
||||
JobSelector* selectedJobsToRemove = nullptr);
|
||||
|
||||
/** Returns the number of jobs currently running or queued.
|
||||
*/
|
||||
int getNumJobs() const;
|
||||
|
||||
/** Returns one of the jobs in the queue.
|
||||
|
||||
Note that this can be a very volatile list as jobs might be continuously getting shifted
|
||||
around in the list, and this method may return 0 if the index is currently out-of-range.
|
||||
*/
|
||||
ThreadPoolJob* getJob (int index) const;
|
||||
|
||||
/** Returns true if the given job is currently queued or running.
|
||||
|
||||
@see isJobRunning()
|
||||
*/
|
||||
bool contains (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Returns true if the given job is currently being run by a thread.
|
||||
*/
|
||||
bool isJobRunning (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Waits until a job has finished running and has been removed from the pool.
|
||||
|
||||
This will wait until the job is no longer in the pool - i.e. until its
|
||||
runJob() method returns ThreadPoolJob::jobHasFinished.
|
||||
|
||||
If the timeout period expires before the job finishes, this will return false;
|
||||
it returns true if the job has finished successfully.
|
||||
*/
|
||||
bool waitForJobToFinish (const ThreadPoolJob* job,
|
||||
int timeOutMilliseconds) const;
|
||||
|
||||
/** Returns a list of the names of all the jobs currently running or queued.
|
||||
If onlyReturnActiveJobs is true, only the ones currently running are returned.
|
||||
*/
|
||||
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
|
||||
|
||||
/** Changes the priority of all the threads.
|
||||
|
||||
This will call Thread::setPriority() for each thread in the pool.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
*/
|
||||
bool setThreadPriorities (int newPriority);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <ThreadPoolJob*> jobs;
|
||||
|
||||
class ThreadPoolThread;
|
||||
friend class ThreadPoolThread;
|
||||
friend class OwnedArray <ThreadPoolThread>;
|
||||
OwnedArray <ThreadPoolThread> threads;
|
||||
|
||||
CriticalSection lock;
|
||||
WaitableEvent jobFinishedSignal;
|
||||
|
||||
bool runNextJob();
|
||||
ThreadPoolJob* pickNextJobToRun();
|
||||
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
||||
void createThreads (int numThreads);
|
||||
void stopThreads();
|
||||
|
||||
// Note that this method has changed, and no longer has a parameter to indicate
|
||||
// whether the jobs should be deleted - see the new method for details.
|
||||
void removeAllJobs (bool, int, bool);
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_THREADPOOL_H_INCLUDED
|
||||
@@ -1,166 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
TimeSliceThread::TimeSliceThread (const String& name)
|
||||
: Thread (name),
|
||||
clientBeingCalled (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TimeSliceThread::~TimeSliceThread()
|
||||
{
|
||||
stopThread (2000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client, int millisecondsBeforeStarting)
|
||||
{
|
||||
if (client != nullptr)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
client->nextCallTime = Time::getCurrentTime() + RelativeTime::milliseconds (millisecondsBeforeStarting);
|
||||
clients.addIfNotAlreadyThere (client);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client)
|
||||
{
|
||||
const ScopedLock sl1 (listLock);
|
||||
|
||||
// if there's a chance we're in the middle of calling this client, we need to
|
||||
// also lock the outer lock..
|
||||
if (clientBeingCalled == client)
|
||||
{
|
||||
const ScopedUnlock ul (listLock); // unlock first to get the order right..
|
||||
|
||||
const ScopedLock sl2 (callbackLock);
|
||||
const ScopedLock sl3 (listLock);
|
||||
|
||||
clients.removeFirstMatchingValue (client);
|
||||
}
|
||||
else
|
||||
{
|
||||
clients.removeFirstMatchingValue (client);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSliceThread::moveToFrontOfQueue (TimeSliceClient* client)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
if (clients.contains (client))
|
||||
{
|
||||
client->nextCallTime = Time::getCurrentTime();
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
int TimeSliceThread::getNumClients() const
|
||||
{
|
||||
return clients.size();
|
||||
}
|
||||
|
||||
TimeSliceClient* TimeSliceThread::getClient (const int i) const
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
return clients [i];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TimeSliceClient* TimeSliceThread::getNextClient (int index) const
|
||||
{
|
||||
Time soonest;
|
||||
TimeSliceClient* client = nullptr;
|
||||
|
||||
for (int i = clients.size(); --i >= 0;)
|
||||
{
|
||||
TimeSliceClient* const c = clients.getUnchecked ((i + index) % clients.size());
|
||||
|
||||
if (client == nullptr || c->nextCallTime < soonest)
|
||||
{
|
||||
client = c;
|
||||
soonest = c->nextCallTime;
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void TimeSliceThread::run()
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int timeToWait = 500;
|
||||
|
||||
{
|
||||
Time nextClientTime;
|
||||
|
||||
{
|
||||
const ScopedLock sl2 (listLock);
|
||||
|
||||
index = clients.size() > 0 ? ((index + 1) % clients.size()) : 0;
|
||||
|
||||
if (TimeSliceClient* const firstClient = getNextClient (index))
|
||||
nextClientTime = firstClient->nextCallTime;
|
||||
}
|
||||
|
||||
const Time now (Time::getCurrentTime());
|
||||
|
||||
if (nextClientTime > now)
|
||||
{
|
||||
timeToWait = (int) bmin ((int64) 500, (nextClientTime - now).inMilliseconds());
|
||||
}
|
||||
else
|
||||
{
|
||||
timeToWait = index == 0 ? 1 : 0;
|
||||
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
{
|
||||
const ScopedLock sl2 (listLock);
|
||||
clientBeingCalled = getNextClient (index);
|
||||
}
|
||||
|
||||
if (clientBeingCalled != nullptr)
|
||||
{
|
||||
const int msUntilNextCall = clientBeingCalled->useTimeSlice();
|
||||
|
||||
const ScopedLock sl2 (listLock);
|
||||
|
||||
if (msUntilNextCall >= 0)
|
||||
clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall);
|
||||
else
|
||||
clients.removeFirstMatchingValue (clientBeingCalled);
|
||||
|
||||
clientBeingCalled = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeToWait > 0)
|
||||
wait (timeToWait);
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.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_TIMESLICETHREAD_H_INCLUDED
|
||||
#define BEAST_TIMESLICETHREAD_H_INCLUDED
|
||||
|
||||
class TimeSliceThread;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used by the TimeSliceThread class.
|
||||
|
||||
To register your class with a TimeSliceThread, derive from this class and
|
||||
use the TimeSliceThread::addTimeSliceClient() method to add it to the list.
|
||||
|
||||
Make sure you always call TimeSliceThread::removeTimeSliceClient() before
|
||||
deleting your client!
|
||||
|
||||
@see TimeSliceThread
|
||||
*/
|
||||
class BEAST_API TimeSliceClient
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~TimeSliceClient() {}
|
||||
|
||||
/** Called back by a TimeSliceThread.
|
||||
|
||||
When you register this class with it, a TimeSliceThread will repeatedly call
|
||||
this method.
|
||||
|
||||
The implementation of this method should use its time-slice to do something that's
|
||||
quick - never block for longer than absolutely necessary.
|
||||
|
||||
@returns Your method should return the number of milliseconds which it would like to wait before being called
|
||||
again. Returning 0 will make the thread call again as soon as possible (after possibly servicing
|
||||
other busy clients). If you return a value below zero, your client will be removed from the list of clients,
|
||||
and won't be called again. The value you specify isn't a guaranteee, and is only used as a hint by the
|
||||
thread - the actual time before the next callback may be more or less than specified.
|
||||
You can force the TimeSliceThread to wake up and poll again immediately by calling its notify() method.
|
||||
*/
|
||||
virtual int useTimeSlice() = 0;
|
||||
|
||||
|
||||
private:
|
||||
friend class TimeSliceThread;
|
||||
Time nextCallTime;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A thread that keeps a list of clients, and calls each one in turn, giving them
|
||||
all a chance to run some sort of short task.
|
||||
|
||||
@see TimeSliceClient, Thread
|
||||
*/
|
||||
class BEAST_API TimeSliceThread
|
||||
: public Thread
|
||||
, LeakChecked <TimeSliceThread>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a TimeSliceThread.
|
||||
|
||||
When first created, the thread is not running. Use the startThread()
|
||||
method to start it.
|
||||
*/
|
||||
explicit TimeSliceThread (const String& threadName);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Deleting a Thread object that is running will only give the thread a
|
||||
brief opportunity to stop itself cleanly, so it's recommended that you
|
||||
should always call stopThread() with a decent timeout before deleting,
|
||||
to avoid the thread being forcibly killed (which is a Bad Thing).
|
||||
*/
|
||||
~TimeSliceThread();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a client to the list.
|
||||
|
||||
The client's callbacks will start after the number of milliseconds specified
|
||||
by millisecondsBeforeStarting (and this may happen before this method has returned).
|
||||
*/
|
||||
void addTimeSliceClient (TimeSliceClient* client, int millisecondsBeforeStarting = 0);
|
||||
|
||||
/** Removes a client from the list.
|
||||
|
||||
This method will make sure that all callbacks to the client have completely
|
||||
finished before the method returns.
|
||||
*/
|
||||
void removeTimeSliceClient (TimeSliceClient* client);
|
||||
|
||||
/** If the given client is waiting in the queue, it will be moved to the front
|
||||
and given a time-slice as soon as possible.
|
||||
If the specified client has not been added, nothing will happen.
|
||||
*/
|
||||
void moveToFrontOfQueue (TimeSliceClient* client);
|
||||
|
||||
/** Returns the number of registered clients. */
|
||||
int getNumClients() const;
|
||||
|
||||
/** Returns one of the registered clients. */
|
||||
TimeSliceClient* getClient (int index) const;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
void run() override;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
CriticalSection callbackLock, listLock;
|
||||
Array <TimeSliceClient*> clients;
|
||||
TimeSliceClient* clientBeingCalled;
|
||||
|
||||
TimeSliceClient* getNextClient (int index) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_TIMESLICETHREAD_H_INCLUDED
|
||||
Reference in New Issue
Block a user