Move many Thread related classes

This commit is contained in:
Vinnie Falco
2013-10-04 13:49:24 -07:00
parent 93e9d8622e
commit 6c7f5d093c
48 changed files with 1043 additions and 2158 deletions

View File

@@ -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"

View File

@@ -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"
}

View File

@@ -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 ();
}
}

View File

@@ -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

View File

@@ -170,8 +170,7 @@ public:
}
};
/** The type of a list of tests.
*/
/** The type of a list of tests. */
typedef Array <UnitTest*, CriticalSection> TestList;
//--------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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, &param) != 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, &param) == 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();

View File

@@ -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;

View File

@@ -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 ();
}

View File

@@ -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

View File

@@ -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 ();
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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