mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move many Thread related classes
This commit is contained in:
50
beast/threads/LockGuard.h
Normal file
50
beast/threads/LockGuard.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_LOCKGUARD_H_INCLUDED
|
||||
#define BEAST_THREADS_LOCKGUARD_H_INCLUDED
|
||||
|
||||
#include "../Uncopyable.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
template <typename Mutex>
|
||||
class LockGuard : public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef Mutex MutexType;
|
||||
|
||||
explicit LockGuard (Mutex const& mutex)
|
||||
: m_mutex (mutex)
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~LockGuard ()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex const& m_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
84
beast/threads/RecursiveMutex.h
Normal file
84
beast/threads/RecursiveMutex.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_RECURSIVEMUTEX_H_INCLUDED
|
||||
#define BEAST_THREADS_RECURSIVEMUTEX_H_INCLUDED
|
||||
|
||||
#include "../Config.h"
|
||||
#include "LockGuard.h"
|
||||
#include "UnlockGuard.h"
|
||||
#include "TryLockGuard.h"
|
||||
|
||||
#if ! BEAST_WINDOWS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
|
||||
class RecursiveMutex
|
||||
{
|
||||
public:
|
||||
typedef LockGuard <RecursiveMutex> ScopedLockType;
|
||||
typedef UnlockGuard <RecursiveMutex> ScopedUnlockType;
|
||||
typedef TryLockGuard <RecursiveMutex> ScopedTryLockType;
|
||||
|
||||
/** Create the mutex.
|
||||
The mutux is initially unowned.
|
||||
*/
|
||||
RecursiveMutex ();
|
||||
|
||||
/** Destroy the mutex.
|
||||
If the lock is owned, the result is undefined.
|
||||
*/
|
||||
~RecursiveMutex ();
|
||||
|
||||
// Boost concept compatibility:
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts
|
||||
|
||||
/** BasicLockable */
|
||||
/** @{ */
|
||||
void lock () const;
|
||||
void unlock () const;
|
||||
/** @} */
|
||||
|
||||
/** Lockable */
|
||||
bool try_lock () const;
|
||||
|
||||
private:
|
||||
// To avoid including windows.h in the public Beast headers, we'll just
|
||||
// reserve storage here that's big enough to be used internally as a windows
|
||||
// CRITICAL_SECTION structure.
|
||||
#if BEAST_WINDOWS
|
||||
# if BEAST_64BIT
|
||||
char section[44];
|
||||
# else
|
||||
char section[24];
|
||||
# endif
|
||||
#else
|
||||
mutable pthread_mutex_t mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
618
beast/threads/ServiceQueue.h
Normal file
618
beast/threads/ServiceQueue.h
Normal file
@@ -0,0 +1,618 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_SERVICEQUEUE_H_INCLUDED
|
||||
#define BEAST_THREADS_SERVICEQUEUE_H_INCLUDED
|
||||
|
||||
#include "../chrono/CPUMeter.h"
|
||||
#include "../intrusive/List.h"
|
||||
#include "../intrusive/LockFreeStack.h"
|
||||
#include "SharedData.h"
|
||||
#include "ThreadLocalValue.h"
|
||||
#include "WaitableEvent.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO NOTE This allocator is a work in progress
|
||||
|
||||
#if 0
|
||||
|
||||
class ServiceQueueAllocatorArena : public SharedObject
|
||||
{
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
|
||||
class Page : public LockFreeStack <Page>::Node
|
||||
{
|
||||
public:
|
||||
size_type const m_pageBytes;
|
||||
Atomic <int> m_refs;
|
||||
char* m_pos;
|
||||
bool m_full;
|
||||
size_type m_count;
|
||||
|
||||
static std::size_t overhead()
|
||||
{ return sizeof (Page); }
|
||||
|
||||
static std::size_t pointer_overhead()
|
||||
{ return sizeof (Page*); }
|
||||
|
||||
// pageBytes doesn't include the Page structure
|
||||
explicit Page (size_type pageBytes)
|
||||
: m_pageBytes (pageBytes)
|
||||
, m_pos (begin())
|
||||
, m_full (false)
|
||||
, m_count (0)
|
||||
{
|
||||
}
|
||||
|
||||
~Page ()
|
||||
{
|
||||
// This means someone forgot to deallocate something
|
||||
bassert (!m_full && m_refs.get() == 0);
|
||||
}
|
||||
|
||||
static Page* create (size_type pageBytes)
|
||||
{
|
||||
return new (new uint8[pageBytes + overhead()]) Page (pageBytes);
|
||||
}
|
||||
|
||||
static void destroy (Page* page)
|
||||
{
|
||||
page->~Page();
|
||||
delete[] ((uint8*)page);
|
||||
}
|
||||
|
||||
void reset ()
|
||||
{
|
||||
m_refs.set (0);
|
||||
m_pos = begin();
|
||||
m_full = false;
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
bool full() const
|
||||
{
|
||||
return m_full;
|
||||
}
|
||||
|
||||
void* allocate (size_type n)
|
||||
{
|
||||
size_type const needed (n + pointer_overhead());
|
||||
char* pos = m_pos + needed;
|
||||
if (pos > end())
|
||||
{
|
||||
m_full = true;
|
||||
return nullptr;
|
||||
}
|
||||
++m_refs;
|
||||
void* p (m_pos + pointer_overhead());
|
||||
get_page(p) = this;
|
||||
m_pos = pos;
|
||||
++m_count;
|
||||
return p;
|
||||
}
|
||||
|
||||
char* begin() const
|
||||
{
|
||||
return const_cast <char*> (
|
||||
reinterpret_cast <char const*> (this) + overhead());
|
||||
}
|
||||
|
||||
char const* end() const
|
||||
{
|
||||
return begin() + m_pageBytes;
|
||||
}
|
||||
|
||||
// Returns true if the page can be recycled
|
||||
bool deallocate (void* p, size_type)
|
||||
{
|
||||
bool const unused ((--m_refs) == 0);
|
||||
return unused && m_full;
|
||||
}
|
||||
|
||||
// Returns a reference to the per-allocation overhead area
|
||||
static Page*& get_page (void* p)
|
||||
{
|
||||
return *reinterpret_cast <Page**>(
|
||||
static_cast <char*>(p) - pointer_overhead());
|
||||
}
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
State()
|
||||
{
|
||||
}
|
||||
|
||||
~State()
|
||||
{
|
||||
// If this goes off, someone forgot to call deallocate!
|
||||
bassert (full.get() == 0);
|
||||
|
||||
destroy (active);
|
||||
destroy (recycle);
|
||||
}
|
||||
|
||||
void destroy (LockFreeStack <Page>& stack)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
Page* const page (stack.pop_front());
|
||||
if (page == nullptr)
|
||||
break;
|
||||
Page::destroy (page);
|
||||
}
|
||||
}
|
||||
|
||||
Atomic <int> full;
|
||||
Atomic <int> page_count;
|
||||
LockFreeStack <Page> active;
|
||||
LockFreeStack <Page> recycle;
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
|
||||
size_type const m_maxBytes;
|
||||
SharedState m_state;
|
||||
|
||||
explicit ServiceQueueAllocatorArena (size_type maxBytes = 16 * 1024)
|
||||
: m_maxBytes (maxBytes)
|
||||
{
|
||||
}
|
||||
|
||||
~ServiceQueueAllocatorArena()
|
||||
{
|
||||
}
|
||||
|
||||
void* allocate (size_type n)
|
||||
{
|
||||
SharedState::UnlockedAccess state (m_state);
|
||||
|
||||
// Loop until we satisfy the allocation from an
|
||||
// active page, or we run out of active pages.
|
||||
//
|
||||
for (;;)
|
||||
{
|
||||
// Acquire ownership of an active page
|
||||
// This prevents other threads from seeing it.
|
||||
Page* page (state->active.pop_front());
|
||||
if (page == nullptr)
|
||||
break;
|
||||
|
||||
void* p = page->allocate (n);
|
||||
if (p != nullptr)
|
||||
{
|
||||
// Put the page back so other threads can use it
|
||||
state->active.push_front (page);
|
||||
return p;
|
||||
}
|
||||
|
||||
// Page is full, count it for diagnostics
|
||||
++state->full;
|
||||
}
|
||||
|
||||
// No active page, get a recycled page or create a new page.
|
||||
//
|
||||
Page* page (state->recycle.pop_front());
|
||||
if (page == nullptr)
|
||||
{
|
||||
page = Page::create (std::max (m_maxBytes, n));
|
||||
++state->page_count;
|
||||
}
|
||||
|
||||
void* p = page->allocate (n);
|
||||
bassert (p != nullptr);
|
||||
// Throw page into the active list so other threads can use it
|
||||
state->active.push_front (page);
|
||||
return p;
|
||||
}
|
||||
|
||||
void deallocate (void* p, size_type n)
|
||||
{
|
||||
SharedState::UnlockedAccess state (m_state);
|
||||
Page* const page (Page::get_page(p));
|
||||
if (page->deallocate (p, n))
|
||||
{
|
||||
--state->full;
|
||||
page->reset();
|
||||
Page::destroy (page);
|
||||
//state->recycle.push_front (page);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
struct ServiceQueueAllocator
|
||||
{
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
typedef T const* const_pointer;
|
||||
typedef T const& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
ServiceQueueAllocator ()
|
||||
: m_arena (new ServiceQueueAllocatorArena)
|
||||
{
|
||||
}
|
||||
|
||||
ServiceQueueAllocator (ServiceQueueAllocator const& other)
|
||||
: m_arena (other.m_arena)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
ServiceQueueAllocator (ServiceQueueAllocator <U> const& other)
|
||||
: m_arena (other.m_arena)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef ServiceQueueAllocator <U> other;
|
||||
};
|
||||
|
||||
pointer address (reference x) const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
const_pointer address (const_reference x) const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
pointer allocate (size_type n,
|
||||
std::allocator<void>::const_pointer = nullptr) const
|
||||
{
|
||||
size_type const bytes (n * sizeof (value_type));
|
||||
pointer const p (static_cast <pointer> (
|
||||
m_arena->allocate (bytes)));
|
||||
return p;
|
||||
}
|
||||
|
||||
void deallocate (pointer p, size_type n) const
|
||||
{
|
||||
size_type const bytes = (n * sizeof (value_type));
|
||||
m_arena->deallocate (p, bytes);
|
||||
}
|
||||
|
||||
size_type max_size () const
|
||||
{
|
||||
return std::numeric_limits <size_type>::max () / sizeof (value_type);
|
||||
}
|
||||
|
||||
void construct (pointer p, const_reference val) const
|
||||
{
|
||||
new ((void *)p) value_type (val);
|
||||
}
|
||||
|
||||
void destroy (pointer p) const
|
||||
{
|
||||
p->~value_type ();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend struct ServiceQueueAllocator;
|
||||
|
||||
SharedPtr <ServiceQueueAllocatorArena> m_arena;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ServiceQueueBase
|
||||
{
|
||||
public:
|
||||
ServiceQueueBase();
|
||||
~ServiceQueueBase();
|
||||
|
||||
std::size_t poll();
|
||||
std::size_t poll_one();
|
||||
std::size_t run();
|
||||
std::size_t run_one();
|
||||
void stop();
|
||||
bool stopped() const
|
||||
{ return m_stopped.get() != 0; }
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
class Item;
|
||||
class Waiter;
|
||||
class ScopedServiceThread;
|
||||
|
||||
void wait();
|
||||
void enqueue (Item* item);
|
||||
|
||||
virtual std::size_t dequeue() = 0;
|
||||
virtual Waiter* new_waiter() = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class Item : public List <Item>::Node
|
||||
{
|
||||
public:
|
||||
virtual ~Item() { }
|
||||
virtual void operator()() = 0;
|
||||
virtual std::size_t size() const = 0;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class Waiter : public LockFreeStack <Waiter>::Node
|
||||
{
|
||||
public:
|
||||
Waiter()
|
||||
{ }
|
||||
void wait()
|
||||
{ m_event.wait(); }
|
||||
void signal()
|
||||
{ m_event.signal(); }
|
||||
private:
|
||||
WaitableEvent m_event;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct State
|
||||
{
|
||||
// handlers
|
||||
List <Item> handlers;
|
||||
LockFreeStack <Waiter> waiting;
|
||||
LockFreeStack <Waiter> unused;
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
SharedState m_state;
|
||||
CPUMeter m_cpuMeter;
|
||||
Atomic <int> m_stopped;
|
||||
|
||||
static ThreadLocalValue <ServiceQueueBase*> s_service;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A queue for disatching function calls on other threads.
|
||||
Handlers are guaranteed to be called only from threads that are currently
|
||||
calling run, run_one, poll, or poll_one.
|
||||
*/
|
||||
template <class Allocator = std::allocator <char> >
|
||||
class ServiceQueueType : public detail::ServiceQueueBase
|
||||
{
|
||||
private:
|
||||
using ServiceQueueBase::Item;
|
||||
using ServiceQueueBase::Waiter;
|
||||
|
||||
template <typename Handler>
|
||||
class ItemType : public Item
|
||||
{
|
||||
public:
|
||||
explicit ItemType (BEAST_MOVE_ARG(Handler) handler)
|
||||
: m_handler (BEAST_MOVE_CAST(Handler)(handler))
|
||||
{ }
|
||||
void operator() ()
|
||||
{ m_handler(); }
|
||||
std::size_t size() const
|
||||
{ return sizeof (*this); }
|
||||
private:
|
||||
Handler m_handler;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Allocator allocator_type; // for std::uses_allocator<>
|
||||
|
||||
explicit ServiceQueueType (int expectedConcurrency = 1,
|
||||
Allocator alloc = Allocator())
|
||||
: m_alloc (alloc)
|
||||
{
|
||||
typename Allocator::template rebind <Waiter>::other a (m_alloc);
|
||||
SharedState::Access state (m_state);
|
||||
while (expectedConcurrency--)
|
||||
{
|
||||
state->unused.push_front (new (a.allocate (1)) Waiter);
|
||||
}
|
||||
}
|
||||
|
||||
~ServiceQueueType()
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
// Must be empty
|
||||
bassert (state->handlers.empty());
|
||||
bassert (state->waiting.empty());
|
||||
|
||||
typename Allocator::template rebind <Waiter>::other a (m_alloc);
|
||||
for(;;)
|
||||
{
|
||||
Waiter* const waiter (state->unused.pop_front());
|
||||
if (waiter == nullptr)
|
||||
break;
|
||||
a.destroy (waiter);
|
||||
a.deallocate (waiter, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the percentage of time the queue is using the CPU. */
|
||||
double getUtilizaton () const
|
||||
{ return m_cpuMeter.getUtilizaton(); }
|
||||
|
||||
/** Returns the allocator associated with the container. */
|
||||
allocator_type get_allocator() const
|
||||
{
|
||||
return m_alloc;
|
||||
}
|
||||
|
||||
/** Returns `true` if the current thread is processing events.
|
||||
If the current thread of execution is inside a call to run,
|
||||
run_one, poll, or poll_one, this function returns `true`.
|
||||
*/
|
||||
bool is_service_thread() const
|
||||
{ return s_service.get() == this; }
|
||||
|
||||
/** Run the handler on a service thread.
|
||||
If the current thread of execution is a service thread then this
|
||||
function wil dispatch the handler on the caller's thread before
|
||||
returning.
|
||||
The function signature of the handler must be:
|
||||
@code
|
||||
void handler();
|
||||
@endcode
|
||||
*/
|
||||
template <typename Handler>
|
||||
void dispatch (BEAST_MOVE_ARG(Handler) handler)
|
||||
{
|
||||
if (is_service_thread())
|
||||
{
|
||||
handler();
|
||||
}
|
||||
else
|
||||
{
|
||||
typename Allocator::template rebind <ItemType <Handler> >::other a (m_alloc);
|
||||
enqueue (new (a.allocate (1))
|
||||
ItemType <Handler> (BEAST_MOVE_CAST(Handler)(handler)));
|
||||
}
|
||||
}
|
||||
|
||||
/** Request the handler to run on a service thread.
|
||||
This returns immediately, even if the current thread of execution is
|
||||
a service thread.
|
||||
The function signature of the handler must be:
|
||||
@code
|
||||
void handler();
|
||||
@endcode
|
||||
*/
|
||||
template <typename Handler>
|
||||
void post (BEAST_MOVE_ARG(Handler) handler)
|
||||
{
|
||||
typename Allocator::template rebind <ItemType <Handler> >::other a (m_alloc);
|
||||
enqueue (new (a.allocate (1))
|
||||
ItemType <Handler> (BEAST_MOVE_CAST(Handler)(handler)));
|
||||
}
|
||||
|
||||
/** Run the event loop to execute ready handlers.
|
||||
This runs handlers that are ready to run, without blocking, until
|
||||
there are no more handlers ready or the service queue has been stopped.
|
||||
@return The number of handlers that were executed.
|
||||
*/
|
||||
std::size_t poll ()
|
||||
{ return ServiceQueueBase::poll(); }
|
||||
|
||||
/** Run the event loop to execute at most one ready handler.
|
||||
This will run zero or one handlers, without blocking, depending on
|
||||
whether or not there is handler immediately ready to run.
|
||||
@return The number of handlers that were executed.
|
||||
*/
|
||||
std::size_t poll_one ()
|
||||
{ return ServiceQueueBase::poll_one(); }
|
||||
|
||||
/** Runs the queue's processing loop.
|
||||
The current thread of execution becomes a service thread. This call
|
||||
blocks until there is no more work remaining.
|
||||
@return The number of handlers that were executed.
|
||||
*/
|
||||
std::size_t run ()
|
||||
{ return ServiceQueueBase::run(); }
|
||||
|
||||
/** Runs the queue's processing loop to execute at most one handler.
|
||||
@return The number of handlers that were executed.
|
||||
*/
|
||||
std::size_t run_one ()
|
||||
{ return ServiceQueueBase::run_one(); }
|
||||
|
||||
/** Stop the queue's processing loop.
|
||||
All threads executing run or run_one will return as soon as possible.
|
||||
Future calls to run, run_one, poll, or poll_one will return immediately
|
||||
until reset is called.
|
||||
@see reset
|
||||
*/
|
||||
void stop()
|
||||
{ return ServiceQueueBase::stop(); }
|
||||
|
||||
/** Returns `true` if the queue has been stopped.
|
||||
When a queue is stopped, calls to run, run_one, poll, or poll_one will
|
||||
return immediately without invoking any handlers.
|
||||
*/
|
||||
bool stopped() const
|
||||
{ return ServiceQueueBase::stopped(); }
|
||||
|
||||
/** Reset the queue after a stop.
|
||||
This allows the event loop to be restarted. This may not be called while
|
||||
there are any threads currently executing the run, run_one, poll, or
|
||||
poll_one functions, or undefined behavior will result.
|
||||
*/
|
||||
void reset()
|
||||
{ return ServiceQueueBase::reset(); }
|
||||
|
||||
private:
|
||||
// Dispatch a single queued handler if possible.
|
||||
// Returns the number of handlers dispatched (0 or 1)
|
||||
//
|
||||
std::size_t dequeue ()
|
||||
{
|
||||
if (stopped())
|
||||
return 0;
|
||||
|
||||
Item* item (nullptr);
|
||||
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
if (state->handlers.empty())
|
||||
return 0;
|
||||
item = &state->handlers.front();
|
||||
state->handlers.erase (
|
||||
state->handlers.iterator_to (*item));
|
||||
}
|
||||
|
||||
(*item)();
|
||||
|
||||
typename Allocator::template rebind <uint8>::other a (m_alloc);
|
||||
std::size_t const size (item->size());
|
||||
item->~Item();
|
||||
a.deallocate (reinterpret_cast<uint8*>(item), size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create a new Waiter
|
||||
Waiter* new_waiter()
|
||||
{
|
||||
typename Allocator::template rebind <Waiter>::other a (m_alloc);
|
||||
return new (a.allocate (1)) Waiter;
|
||||
}
|
||||
|
||||
Allocator m_alloc;
|
||||
};
|
||||
|
||||
typedef ServiceQueueType <std::allocator <char> > ServiceQueue;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
272
beast/threads/SharedData.h
Normal file
272
beast/threads/SharedData.h
Normal file
@@ -0,0 +1,272 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_SHAREDDATA_H_INCLUDED
|
||||
#define BEAST_THREADS_SHAREDDATA_H_INCLUDED
|
||||
|
||||
#include "RecursiveMutex.h"
|
||||
#include "SharedMutexAdapter.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Structured, multi-threaded access to a shared state.
|
||||
|
||||
This container combines locking semantics with data access semantics to
|
||||
create an alternative to the typical synchronization method of first
|
||||
acquiring a lock and then accessing data members.
|
||||
|
||||
With this container, access to the underlying data is only possible after
|
||||
first acquiring a lock. The steps of acquiring the lock and obtaining
|
||||
a const or non-const reference to the data are combined into one
|
||||
RAII style operation.
|
||||
|
||||
There are three types of access:
|
||||
|
||||
- Access
|
||||
Provides access to the shared state via a non-const reference or pointer.
|
||||
Access acquires a unique lock on the mutex associated with the
|
||||
container.
|
||||
|
||||
- ConstAccess
|
||||
Provides access to the shared state via a const reference or pointer.
|
||||
ConstAccess acquires a shared lock on the mutex associated with the
|
||||
container.
|
||||
|
||||
- ConstUnlockedAccess
|
||||
Provides access to the shared state via a const reference or pointer.
|
||||
No locking is performed. It is the callers responsibility to ensure that
|
||||
the operation is synchronized. This can be useful for diagnostics or
|
||||
assertions, or for when it is known that no other threads can access
|
||||
the data.
|
||||
|
||||
- UnlockedAccess
|
||||
Provides access to the shared state via a reference or pointer.
|
||||
No locking is performed. It is the callers responsibility to ensure that
|
||||
the operation is synchronized. This can be useful for diagnostics or
|
||||
assertions, or for when it is known that no other threads can access
|
||||
the data.
|
||||
|
||||
Usage example:
|
||||
|
||||
@code
|
||||
|
||||
struct State
|
||||
{
|
||||
int value1;
|
||||
String value2;
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
|
||||
SharedState m_state;
|
||||
|
||||
void readExample ()
|
||||
{
|
||||
SharedState::ConstAccess state (m_state);
|
||||
|
||||
print (state->value1); // read access
|
||||
print (state->value2); // read access
|
||||
|
||||
state->value1 = 42; // write disallowed: compile error
|
||||
}
|
||||
|
||||
void writeExample ()
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
|
||||
state->value2 = "Label"; // write access, allowed
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
Requirements for Value:
|
||||
Constructible
|
||||
Destructible
|
||||
|
||||
Requirements for SharedMutexType:
|
||||
X is SharedMutexType, a is an instance of X:
|
||||
X a; DefaultConstructible
|
||||
X::LockGuardType Names a type that implements the LockGuard concept.
|
||||
X::SharedLockGuardType Names a type that implements the SharedLockGuard concept.
|
||||
|
||||
@tparam Value The type which the container holds.
|
||||
@tparam SharedMutexType The type of shared mutex to use.
|
||||
*/
|
||||
template <typename Value, class SharedMutexType =
|
||||
SharedMutexAdapter <RecursiveMutex> >
|
||||
class SharedData : public Uncopyable
|
||||
{
|
||||
private:
|
||||
typedef typename SharedMutexType::LockGuardType LockGuardType;
|
||||
typedef typename SharedMutexType::SharedLockGuardType SharedLockGuardType;
|
||||
|
||||
public:
|
||||
typedef Value ValueType;
|
||||
|
||||
class Access;
|
||||
class ConstAccess;
|
||||
class UnlockedAccess;
|
||||
class ConstUnlockedAccess;
|
||||
|
||||
/** Create a shared data container.
|
||||
Up to 8 parameters can be specified in the constructor. These parameters
|
||||
are forwarded to the corresponding constructor in Data. If no
|
||||
constructor in Data matches the parameter list, a compile error is
|
||||
generated.
|
||||
*/
|
||||
/** @{ */
|
||||
SharedData () { }
|
||||
|
||||
template <class T1>
|
||||
explicit SharedData (T1 t1)
|
||||
: m_value (t1) { }
|
||||
|
||||
template <class T1, class T2>
|
||||
SharedData (T1 t1, T2 t2)
|
||||
: m_value (t1, t2) { }
|
||||
|
||||
template <class T1, class T2, class T3>
|
||||
SharedData (T1 t1, T2 t2, T3 t3)
|
||||
: m_value (t1, t2, t3) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
: m_value (t1, t2, t3, t4) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
: m_value (t1, t2, t3, t4, t5) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
: m_value (t1, t2, t3, t4, t5, t6) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) : m_value (t1, t2, t3, t4, t5, t6, t7) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
: m_value (t1, t2, t3, t4, t5, t6, t7, t8) { }
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
Value m_value;
|
||||
SharedMutexType m_mutex;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Provides non-const access to the contents of a SharedData.
|
||||
This acquires a unique lock on the underlying mutex.
|
||||
*/
|
||||
template <class Data, class SharedMutexType>
|
||||
class SharedData <Data, SharedMutexType>::Access : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit Access (SharedData& state)
|
||||
: m_state (state)
|
||||
, m_lock (m_state.m_mutex)
|
||||
{ }
|
||||
|
||||
Data const& get () const { return m_state.m_value; }
|
||||
Data const& operator* () const { return get (); }
|
||||
Data const* operator-> () const { return &get (); }
|
||||
Data& get () { return m_state.m_value; }
|
||||
Data& operator* () { return get (); }
|
||||
Data* operator-> () { return &get (); }
|
||||
|
||||
private:
|
||||
SharedData& m_state;
|
||||
typename SharedData::LockGuardType m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Provides const access to the contents of a SharedData.
|
||||
This acquires a shared lock on the underlying mutex.
|
||||
*/
|
||||
template <class Data, class SharedMutexType>
|
||||
class SharedData <Data, SharedMutexType>::ConstAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create a ConstAccess from the specified SharedData */
|
||||
explicit ConstAccess (SharedData const volatile& state)
|
||||
: m_state (const_cast <SharedData const&> (state))
|
||||
, m_lock (m_state.m_mutex)
|
||||
{ }
|
||||
|
||||
Data const& get () const { return m_state.m_value; }
|
||||
Data const& operator* () const { return get (); }
|
||||
Data const* operator-> () const { return &get (); }
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
typename SharedData::SharedLockGuardType m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Provides const access to the contents of a SharedData.
|
||||
This acquires a shared lock on the underlying mutex.
|
||||
*/
|
||||
template <class Data, class SharedMutexType>
|
||||
class SharedData <Data, SharedMutexType>::ConstUnlockedAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create an UnlockedAccess from the specified SharedData */
|
||||
explicit ConstUnlockedAccess (SharedData const volatile& state)
|
||||
: m_state (const_cast <SharedData const&> (state))
|
||||
{ }
|
||||
|
||||
Data const& get () const { return m_state.m_value; }
|
||||
Data const& operator* () const { return get (); }
|
||||
Data const* operator-> () const { return &get (); }
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Provides access to the contents of a SharedData.
|
||||
This acquires a shared lock on the underlying mutex.
|
||||
*/
|
||||
template <class Data, class SharedMutexType>
|
||||
class SharedData <Data, SharedMutexType>::UnlockedAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create an UnlockedAccess from the specified SharedData */
|
||||
explicit UnlockedAccess (SharedData& state)
|
||||
: m_state (state)
|
||||
{ }
|
||||
|
||||
Data const& get () const { return m_state.m_value; }
|
||||
Data const& operator* () const { return get (); }
|
||||
Data const* operator-> () const { return &get (); }
|
||||
Data& get () { return m_state.m_value; }
|
||||
Data& operator* () { return get (); }
|
||||
Data* operator-> () { return &get (); }
|
||||
|
||||
private:
|
||||
SharedData& m_state;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
52
beast/threads/SharedLockGuard.h
Normal file
52
beast/threads/SharedLockGuard.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_SHAREDLOCKGUARD_H_INCLUDED
|
||||
#define BEAST_THREADS_SHAREDLOCKGUARD_H_INCLUDED
|
||||
|
||||
#include "../Uncopyable.h"
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
/** A scoped container that acquires a shared lock. */
|
||||
template <typename Mutex>
|
||||
class SharedLockGuard : public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef Mutex MutexType;
|
||||
|
||||
explicit SharedLockGuard (Mutex const& mutex)
|
||||
: m_mutex (mutex)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
~SharedLockGuard ()
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex const& m_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
67
beast/threads/SharedMutexAdapter.h
Normal file
67
beast/threads/SharedMutexAdapter.h
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_SHAREDMUTEXADAPTER_H_INCLUDED
|
||||
#define BEAST_THREADS_SHAREDMUTEXADAPTER_H_INCLUDED
|
||||
|
||||
#include "LockGuard.h"
|
||||
#include "SharedLockGuard.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Adapts a regular Lockable to conform to the SharedMutex concept.
|
||||
Shared locks become unique locks with this interface. Two threads may not
|
||||
simultaneously acquire ownership of the lock. Typically the Mutex template
|
||||
parameter will be a CriticalSection.
|
||||
*/
|
||||
template <class Mutex>
|
||||
class SharedMutexAdapter
|
||||
{
|
||||
public:
|
||||
typedef Mutex MutexType;
|
||||
typedef LockGuard <SharedMutexAdapter> LockGuardType;
|
||||
typedef SharedLockGuard <SharedMutexAdapter> SharedLockGuardType;
|
||||
|
||||
void lock() const
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock() const
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void lock_shared() const
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock_shared() const
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mutable m_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
97
beast/threads/SpinLock.h
Normal file
97
beast/threads/SpinLock.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_SPINLOCK_H_INCLUDED
|
||||
#define BEAST_THREADS_SPINLOCK_H_INCLUDED
|
||||
|
||||
#include "../Atomic.h"
|
||||
#include "LockGuard.h"
|
||||
#include "UnlockGuard.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple spin-lock class that can be used as a simple, low-overhead mutex for
|
||||
uncontended situations.
|
||||
|
||||
Note that unlike a CriticalSection, this type of lock is not re-entrant, and may
|
||||
be less efficient when used it a highly contended situation, but it's very small and
|
||||
requires almost no initialisation.
|
||||
It's most appropriate for simple situations where you're only going to hold the
|
||||
lock for a very brief time.
|
||||
|
||||
@see CriticalSection
|
||||
*/
|
||||
class BEAST_API SpinLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Provides the type of scoped lock to use for locking a SpinLock. */
|
||||
typedef LockGuard <SpinLock> ScopedLockType;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a SpinLock. */
|
||||
typedef UnlockGuard <SpinLock> ScopedUnlockType;
|
||||
|
||||
inline SpinLock() noexcept {}
|
||||
inline ~SpinLock() noexcept {}
|
||||
|
||||
/** Acquires the lock.
|
||||
This will block until the lock has been successfully acquired by this thread.
|
||||
Note that a SpinLock is NOT re-entrant, and is not smart enough to know whether the
|
||||
caller thread already has the lock - so if a thread tries to acquire a lock that it
|
||||
already holds, this method will never return!
|
||||
|
||||
It's strongly recommended that you never call this method directly - instead use the
|
||||
ScopedLockType class to manage the locking using an RAII pattern instead.
|
||||
*/
|
||||
void enter() const noexcept;
|
||||
|
||||
/** Attempts to acquire the lock, returning true if this was successful. */
|
||||
inline bool tryEnter() const noexcept
|
||||
{
|
||||
return m_lock.compareAndSetBool (1, 0);
|
||||
}
|
||||
|
||||
/** Releases the lock. */
|
||||
inline void exit() const noexcept
|
||||
{
|
||||
bassert (m_lock.value == 1); // Agh! Releasing a lock that isn't currently held!
|
||||
m_lock = 0;
|
||||
}
|
||||
|
||||
void lock () const
|
||||
{ enter(); }
|
||||
void unlock () const
|
||||
{ exit(); }
|
||||
bool try_lock () const
|
||||
{ return tryEnter(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
mutable Atomic<int> m_lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
327
beast/threads/Stoppable.h
Normal file
327
beast/threads/Stoppable.h
Normal file
@@ -0,0 +1,327 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_STOPPABLE_H_INCLUDED
|
||||
#define BEAST_THREADS_STOPPABLE_H_INCLUDED
|
||||
|
||||
#include "../Atomic.h"
|
||||
#include "../intrusive/LockFreeStack.h"
|
||||
#include "../utility/Journal.h"
|
||||
|
||||
#include "WaitableEvent.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
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
|
||||
294
beast/threads/Thread.h
Normal file
294
beast/threads/Thread.h
Normal file
@@ -0,0 +1,294 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_THREAD_H_INCLUDED
|
||||
#define BEAST_THREADS_THREAD_H_INCLUDED
|
||||
|
||||
#include "../utility/LeakChecked.h"
|
||||
#include "RecursiveMutex.h"
|
||||
#include "WaitableEvent.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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;
|
||||
RecursiveMutex 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
|
||||
|
||||
197
beast/threads/ThreadLocalValue.h
Normal file
197
beast/threads/ThreadLocalValue.h
Normal file
@@ -0,0 +1,197 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_THREADLOCALVALUE_H_INCLUDED
|
||||
#define BEAST_THREADS_THREADLOCALVALUE_H_INCLUDED
|
||||
|
||||
#include "../Config.h"
|
||||
#include "SpinLock.h"
|
||||
#include "Thread.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
// (NB: on win32, native thread-locals aren't possible in a dynamically loaded DLL in XP).
|
||||
#if ! ((BEAST_MSVC && (BEAST_64BIT || ! defined (BeastPlugin_PluginCode))) \
|
||||
|| (BEAST_MAC && BEAST_CLANG && defined (MAC_OS_X_VERSION_10_7) \
|
||||
&& MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7))
|
||||
#define BEAST_NO_COMPILER_THREAD_LOCAL 1
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Provides cross-platform support for thread-local objects.
|
||||
|
||||
This class holds an internal list of objects of the templated type, keeping
|
||||
an instance for each thread that requests one. The first time a thread attempts
|
||||
to access its value, an object is created and added to the list for that thread.
|
||||
|
||||
Typically, you'll probably want to create a static instance of a ThreadLocalValue
|
||||
object, or hold one within a singleton.
|
||||
|
||||
The templated class for your value could be a primitive type, or any class that
|
||||
has a default constructor and copy operator.
|
||||
|
||||
When a thread no longer needs to use its value, it can call releaseCurrentThreadStorage()
|
||||
to allow the storage to be re-used by another thread. If a thread exits without calling
|
||||
this method, the object storage will be left allocated until the ThreadLocalValue object
|
||||
is deleted.
|
||||
*/
|
||||
template <typename Type>
|
||||
class ThreadLocalValue : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
ThreadLocalValue() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
When this object is deleted, all the value objects for all threads will be deleted.
|
||||
*/
|
||||
~ThreadLocalValue()
|
||||
{
|
||||
#if BEAST_NO_COMPILER_THREAD_LOCAL
|
||||
for (ObjectHolder* o = first.value; o != nullptr;)
|
||||
{
|
||||
ObjectHolder* const next = o->next;
|
||||
delete o;
|
||||
o = next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns a reference to this thread's instance of the value.
|
||||
Note that the first time a thread tries to access the value, an instance of the
|
||||
value object will be created - so if your value's class has a non-trivial
|
||||
constructor, be aware that this method could invoke it.
|
||||
*/
|
||||
Type& operator*() const noexcept { return get(); }
|
||||
|
||||
/** Returns a pointer to this thread's instance of the value.
|
||||
Note that the first time a thread tries to access the value, an instance of the
|
||||
value object will be created - so if your value's class has a non-trivial
|
||||
constructor, be aware that this method could invoke it.
|
||||
*/
|
||||
operator Type*() const noexcept { return &get(); }
|
||||
|
||||
/** Accesses a method or field of the value object.
|
||||
Note that the first time a thread tries to access the value, an instance of the
|
||||
value object will be created - so if your value's class has a non-trivial
|
||||
constructor, be aware that this method could invoke it.
|
||||
*/
|
||||
Type* operator->() const noexcept { return &get(); }
|
||||
|
||||
/** Assigns a new value to the thread-local object. */
|
||||
ThreadLocalValue& operator= (const Type& newValue) { get() = newValue; return *this; }
|
||||
|
||||
/** Returns a reference to this thread's instance of the value.
|
||||
Note that the first time a thread tries to access the value, an instance of the
|
||||
value object will be created - so if your value's class has a non-trivial
|
||||
constructor, be aware that this method could invoke it.
|
||||
*/
|
||||
Type& get() const noexcept
|
||||
{
|
||||
#if BEAST_NO_COMPILER_THREAD_LOCAL
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
|
||||
for (ObjectHolder* o = first.get(); o != nullptr; o = o->next)
|
||||
if (o->threadId == threadId)
|
||||
return o->object;
|
||||
|
||||
for (ObjectHolder* o = first.get(); o != nullptr; o = o->next)
|
||||
{
|
||||
if (o->threadId == nullptr)
|
||||
{
|
||||
{
|
||||
SpinLock::ScopedLockType sl (lock);
|
||||
|
||||
if (o->threadId != nullptr)
|
||||
continue;
|
||||
|
||||
o->threadId = threadId;
|
||||
}
|
||||
|
||||
o->object = Type();
|
||||
return o->object;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectHolder* const newObject = new ObjectHolder (threadId);
|
||||
|
||||
do
|
||||
{
|
||||
newObject->next = first.get();
|
||||
}
|
||||
while (! first.compareAndSetBool (newObject, newObject->next));
|
||||
|
||||
return newObject->object;
|
||||
#elif BEAST_MAC
|
||||
static __thread Type object;
|
||||
return object;
|
||||
#elif BEAST_MSVC
|
||||
static __declspec(thread) Type object;
|
||||
return object;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Called by a thread before it terminates, to allow this class to release
|
||||
any storage associated with the thread.
|
||||
*/
|
||||
void releaseCurrentThreadStorage()
|
||||
{
|
||||
#if BEAST_NO_COMPILER_THREAD_LOCAL
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
|
||||
for (ObjectHolder* o = first.get(); o != nullptr; o = o->next)
|
||||
{
|
||||
if (o->threadId == threadId)
|
||||
{
|
||||
SpinLock::ScopedLockType sl (lock);
|
||||
o->threadId = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
#if BEAST_NO_COMPILER_THREAD_LOCAL
|
||||
struct ObjectHolder : public Uncopyable
|
||||
{
|
||||
ObjectHolder (const Thread::ThreadID& tid)
|
||||
: threadId (tid), object()
|
||||
{}
|
||||
|
||||
Thread::ThreadID threadId;
|
||||
ObjectHolder* next;
|
||||
Type object;
|
||||
};
|
||||
|
||||
mutable Atomic<ObjectHolder*> first;
|
||||
SpinLock lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
27
beast/threads/Threads.cpp
Normal file
27
beast/threads/Threads.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "BeastConfig.h"
|
||||
|
||||
#include "impl/Atomic.cpp"
|
||||
#include "impl/RecursiveMutex.cpp"
|
||||
#include "impl/ServiceQueue.cpp"
|
||||
#include "impl/Stoppable.cpp"
|
||||
#include "impl/Thread.cpp"
|
||||
#include "impl/WaitableEvent.cpp"
|
||||
55
beast/threads/TryLockGuard.h
Normal file
55
beast/threads/TryLockGuard.h
Normal file
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_TRYLOCKGUARD_H_INCLUDED
|
||||
#define BEAST_THREADS_TRYLOCKGUARD_H_INCLUDED
|
||||
|
||||
#include "../Uncopyable.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
template <typename Mutex>
|
||||
class TryLockGuard : public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef Mutex MutexType;
|
||||
|
||||
explicit TryLockGuard (Mutex const& mutex)
|
||||
: m_mutex (mutex)
|
||||
, m_owns_lock (m_mutex.try_lock())
|
||||
{
|
||||
}
|
||||
|
||||
~TryLockGuard ()
|
||||
{
|
||||
if (m_owns_lock)
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
bool owns_lock() const
|
||||
{ return m_owns_lock; }
|
||||
|
||||
private:
|
||||
Mutex const& m_mutex;
|
||||
bool m_owns_lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
50
beast/threads/UnlockGuard.h
Normal file
50
beast/threads/UnlockGuard.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_UNLOCKGUARD_H_INCLUDED
|
||||
#define BEAST_THREADS_UNLOCKGUARD_H_INCLUDED
|
||||
|
||||
#include "../Uncopyable.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
template <typename Mutex>
|
||||
class UnlockGuard : public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef Mutex MutexType;
|
||||
|
||||
explicit UnlockGuard (Mutex const& mutex)
|
||||
: m_mutex (mutex)
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
~UnlockGuard ()
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex const& m_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
124
beast/threads/WaitableEvent.h
Normal file
124
beast/threads/WaitableEvent.h
Normal file
@@ -0,0 +1,124 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADS_WAITABLEEVENT_H_INCLUDED
|
||||
#define BEAST_THREADS_WAITABLEEVENT_H_INCLUDED
|
||||
|
||||
#include "../Config.h"
|
||||
#include "../Uncopyable.h"
|
||||
|
||||
#if ! BEAST_WINDOWS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Allows threads to wait for events triggered by other threads.
|
||||
A thread can call wait() on a WaitableEvent, and this will suspend the
|
||||
calling thread until another thread wakes it up by calling the signal()
|
||||
method.
|
||||
*/
|
||||
class WaitableEvent
|
||||
: public Uncopyable
|
||||
//, LeakChecked <WaitableEvent> // VFALCO TODO Move LeakChecked to beast/
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WaitableEvent object.
|
||||
|
||||
@param manualReset If this is false, the event will be reset automatically when the wait()
|
||||
method is called. If manualReset is true, then once the event is signalled,
|
||||
the only way to reset it will be by calling the reset() method.
|
||||
|
||||
@param initiallySignaled If this is true then the event will be signaled when
|
||||
the constructor returns.
|
||||
*/
|
||||
explicit WaitableEvent (bool manualReset = false, bool initiallySignaled = false);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If other threads are waiting on this object when it gets deleted, this
|
||||
can cause nasty errors, so be careful!
|
||||
*/
|
||||
~WaitableEvent();
|
||||
|
||||
//==============================================================================
|
||||
/** Suspends the calling thread until the event has been signalled.
|
||||
|
||||
This will wait until the object's signal() method is called by another thread,
|
||||
or until the timeout expires.
|
||||
|
||||
After the event has been signalled, this method will return true and if manualReset
|
||||
was set to false in the WaitableEvent's constructor, then the event will be reset.
|
||||
|
||||
@param timeOutMilliseconds the maximum time to wait, in milliseconds. A negative
|
||||
value will cause it to wait forever.
|
||||
|
||||
@returns true if the object has been signalled, false if the timeout expires first.
|
||||
@see signal, reset
|
||||
*/
|
||||
/** @{ */
|
||||
bool wait () const; // wait forever
|
||||
// VFALCO TODO Change wait() to seconds instead of millis
|
||||
bool wait (int timeOutMilliseconds) const; // DEPRECATED
|
||||
/** @} */
|
||||
|
||||
//==============================================================================
|
||||
/** Wakes up any threads that are currently waiting on this object.
|
||||
|
||||
If signal() is called when nothing is waiting, the next thread to call wait()
|
||||
will return immediately and reset the signal.
|
||||
|
||||
If the WaitableEvent is manual reset, all current and future threads that wait upon this
|
||||
object will be woken, until reset() is explicitly called.
|
||||
|
||||
If the WaitableEvent is automatic reset, and one or more threads is waiting upon the object,
|
||||
then one of them will be woken up. If no threads are currently waiting, then the next thread
|
||||
to call wait() will be woken up. As soon as a thread is woken, the signal is automatically
|
||||
reset.
|
||||
|
||||
@see wait, reset
|
||||
*/
|
||||
void signal() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Resets the event to an unsignalled state.
|
||||
|
||||
If it's not already signalled, this does nothing.
|
||||
*/
|
||||
void reset() const;
|
||||
|
||||
private:
|
||||
#if BEAST_WINDOWS
|
||||
void* handle;
|
||||
#else
|
||||
mutable pthread_cond_t condition;
|
||||
mutable pthread_mutex_t mutex;
|
||||
mutable bool triggered;
|
||||
mutable bool manualReset;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
133
beast/threads/impl/Atomic.cpp
Normal file
133
beast/threads/impl/Atomic.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../../../modules/beast_core/beast_core.h" // for UnitTest
|
||||
|
||||
namespace beast {
|
||||
|
||||
class AtomicTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AtomicTests() : UnitTest ("Atomic", "beast") {}
|
||||
|
||||
template <typename Type>
|
||||
void testFloat ()
|
||||
{
|
||||
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!
|
||||
*/
|
||||
expect (a.get() == (Type) 21);
|
||||
expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21);
|
||||
expect (a.get() == (Type) 21);
|
||||
expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21);
|
||||
expect (a.get() == (Type) 101);
|
||||
expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
|
||||
expect (a.get() == (Type) 101);
|
||||
expect (a.compareAndSetBool ((Type) 200, a.get()));
|
||||
expect (a.get() == (Type) 200);
|
||||
|
||||
expect (a.exchange ((Type) 300) == (Type) 200);
|
||||
expect (a.get() == (Type) 300);
|
||||
|
||||
b = a;
|
||||
expect (b.get() == a.get());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void testInteger ()
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a.set ((Type) 10);
|
||||
expect (a.value == (Type) 10);
|
||||
expect (a.get() == (Type) 10);
|
||||
a += (Type) 15;
|
||||
expect (a.get() == (Type) 25);
|
||||
memoryBarrier();
|
||||
a -= (Type) 5;
|
||||
expect (a.get() == (Type) 20);
|
||||
expect (++a == (Type) 21);
|
||||
++a;
|
||||
expect (--a == (Type) 21);
|
||||
expect (a.get() == (Type) 21);
|
||||
memoryBarrier();
|
||||
|
||||
testFloat <Type> ();
|
||||
}
|
||||
|
||||
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");
|
||||
testInteger <int> ();
|
||||
|
||||
beginTestCase ("unsigned int");
|
||||
testInteger <unsigned int> ();
|
||||
|
||||
beginTestCase ("int32");
|
||||
testInteger <int32> ();
|
||||
|
||||
beginTestCase ("uint32");
|
||||
testInteger <uint32> ();
|
||||
|
||||
beginTestCase ("long");
|
||||
testInteger <long> ();
|
||||
|
||||
beginTestCase ("void*");
|
||||
testInteger <void*> ();
|
||||
|
||||
beginTestCase ("int*");
|
||||
testInteger <int*> ();
|
||||
|
||||
beginTestCase ("float");
|
||||
testFloat <float> ();
|
||||
|
||||
#if ! BEAST_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms
|
||||
beginTestCase ("int64");
|
||||
testInteger <int64> ();
|
||||
|
||||
beginTestCase ("uint64");
|
||||
testInteger <uint64> ();
|
||||
|
||||
beginTestCase ("double");
|
||||
testFloat <double> ();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static AtomicTests atomicTests;
|
||||
|
||||
}
|
||||
111
beast/threads/impl/RecursiveMutex.cpp
Normal file
111
beast/threads/impl/RecursiveMutex.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../RecursiveMutex.h"
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
|
||||
#include "../StaticAssert.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#undef check
|
||||
#undef direct
|
||||
#undef max
|
||||
#undef min
|
||||
#undef TYPE_BOOL
|
||||
|
||||
namespace beast {
|
||||
|
||||
RecursiveMutex::RecursiveMutex()
|
||||
{
|
||||
// (just to check the MS haven't changed this structure and broken things...)
|
||||
#if BEAST_VC7_OR_EARLIER
|
||||
static_bassert (sizeof (CRITICAL_SECTION) <= 24);
|
||||
#else
|
||||
static_bassert (sizeof (CRITICAL_SECTION) <= sizeof (section));
|
||||
#endif
|
||||
|
||||
InitializeCriticalSection ((CRITICAL_SECTION*) section);
|
||||
}
|
||||
|
||||
RecursiveMutex::~RecursiveMutex()
|
||||
{
|
||||
DeleteCriticalSection ((CRITICAL_SECTION*) section);
|
||||
}
|
||||
|
||||
void RecursiveMutex::lock() const
|
||||
{
|
||||
EnterCriticalSection ((CRITICAL_SECTION*) section);
|
||||
}
|
||||
|
||||
void RecursiveMutex::unlock() const
|
||||
{
|
||||
LeaveCriticalSection ((CRITICAL_SECTION*) section);
|
||||
}
|
||||
|
||||
bool RecursiveMutex::try_lock() const
|
||||
{
|
||||
return TryEnterCriticalSection ((CRITICAL_SECTION*) section) != FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace beast {
|
||||
|
||||
RecursiveMutex::RecursiveMutex()
|
||||
{
|
||||
pthread_mutexattr_t atts;
|
||||
pthread_mutexattr_init (&atts);
|
||||
pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE);
|
||||
#if ! BEAST_ANDROID
|
||||
pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
|
||||
#endif
|
||||
pthread_mutex_init (&mutex, &atts);
|
||||
pthread_mutexattr_destroy (&atts);
|
||||
}
|
||||
|
||||
RecursiveMutex::~RecursiveMutex()
|
||||
{
|
||||
pthread_mutex_destroy (&mutex);
|
||||
}
|
||||
|
||||
void RecursiveMutex::lock() const
|
||||
{
|
||||
pthread_mutex_lock (&mutex);
|
||||
}
|
||||
|
||||
void RecursiveMutex::unlock() const
|
||||
{
|
||||
pthread_mutex_unlock (&mutex);
|
||||
}
|
||||
|
||||
bool RecursiveMutex::try_lock() const
|
||||
{
|
||||
return pthread_mutex_trylock (&mutex) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
465
beast/threads/impl/ServiceQueue.cpp
Normal file
465
beast/threads/impl/ServiceQueue.cpp
Normal file
@@ -0,0 +1,465 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../ServiceQueue.h"
|
||||
|
||||
#include "../../../modules/beast_core/beast_core.h" // for UnitTest
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ServiceQueueBase::ScopedServiceThread : public List <ScopedServiceThread>::Node
|
||||
{
|
||||
public:
|
||||
explicit ScopedServiceThread (ServiceQueueBase* queue)
|
||||
: m_saved (ServiceQueueBase::s_service.get())
|
||||
{
|
||||
ServiceQueueBase::s_service.get() = queue;
|
||||
}
|
||||
|
||||
~ScopedServiceThread()
|
||||
{
|
||||
ServiceQueueBase::s_service.get() = m_saved;
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceQueueBase* m_saved;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ServiceQueueBase::ServiceQueueBase()
|
||||
{
|
||||
}
|
||||
|
||||
ServiceQueueBase::~ServiceQueueBase()
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t ServiceQueueBase::poll ()
|
||||
{
|
||||
CPUMeter::ScopedActiveTime interval (m_cpuMeter);
|
||||
|
||||
std::size_t total (0);
|
||||
ScopedServiceThread thread (this);
|
||||
for (;;)
|
||||
{
|
||||
std::size_t const n (dequeue());
|
||||
if (! n)
|
||||
break;
|
||||
total += n;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
std::size_t ServiceQueueBase::poll_one ()
|
||||
{
|
||||
CPUMeter::ScopedActiveTime interval (m_cpuMeter);
|
||||
|
||||
ScopedServiceThread thread (this);
|
||||
return dequeue();
|
||||
}
|
||||
|
||||
std::size_t ServiceQueueBase::run ()
|
||||
{
|
||||
std::size_t total (0);
|
||||
ScopedServiceThread thread (this);
|
||||
while (! stopped())
|
||||
{
|
||||
{
|
||||
CPUMeter::ScopedActiveTime interval (m_cpuMeter);
|
||||
total += poll ();
|
||||
}
|
||||
|
||||
{
|
||||
CPUMeter::ScopedIdleTime interval (m_cpuMeter);
|
||||
wait ();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
std::size_t ServiceQueueBase::run_one ()
|
||||
{
|
||||
std::size_t n;
|
||||
ScopedServiceThread (this);
|
||||
for (;;)
|
||||
{
|
||||
{
|
||||
CPUMeter::ScopedActiveTime interval (m_cpuMeter);
|
||||
n = poll_one();
|
||||
if (n != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
CPUMeter::ScopedIdleTime interval (m_cpuMeter);
|
||||
wait();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void ServiceQueueBase::stop ()
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
m_stopped.set (1);
|
||||
for(;;)
|
||||
{
|
||||
Waiter* waiting (state->waiting.pop_front());
|
||||
if (waiting == nullptr)
|
||||
break;
|
||||
waiting->signal();
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceQueueBase::reset()
|
||||
{
|
||||
// Must be stopped
|
||||
bassert (m_stopped.get () != 0);
|
||||
m_stopped.set (0);
|
||||
}
|
||||
|
||||
// Block on the event if there are no items
|
||||
// in the queue and we are not stopped.
|
||||
//
|
||||
void ServiceQueueBase::wait ()
|
||||
{
|
||||
Waiter* waiter (nullptr);
|
||||
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
if (stopped ())
|
||||
return;
|
||||
if (! state->handlers.empty())
|
||||
return;
|
||||
waiter = state->unused.pop_front();
|
||||
if (! waiter)
|
||||
waiter = new_waiter();
|
||||
state->waiting.push_front (waiter);
|
||||
}
|
||||
|
||||
waiter->wait();
|
||||
|
||||
// Waiter got taken off the waiting list
|
||||
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
state->unused.push_front (waiter);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceQueueBase::enqueue (Item* item)
|
||||
{
|
||||
Waiter* waiter;
|
||||
|
||||
{
|
||||
SharedState::Access state (m_state);
|
||||
state->handlers.push_back (*item);
|
||||
// Signal a Waiter if one exists
|
||||
waiter = state->waiting.pop_front();
|
||||
}
|
||||
|
||||
if (waiter != nullptr)
|
||||
waiter->signal();
|
||||
}
|
||||
|
||||
// A thread can only be blocked on one ServiceQueue so we store the pointer
|
||||
// to which ServiceQueue it is blocked on to determine if the thread belongs
|
||||
// to that queue.
|
||||
//
|
||||
ThreadLocalValue <ServiceQueueBase*> ServiceQueueBase::s_service;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ServiceQueueTimingTests
|
||||
: public UnitTest
|
||||
{
|
||||
public:
|
||||
class Stopwatch
|
||||
{
|
||||
public:
|
||||
Stopwatch () { start(); }
|
||||
void start () { m_startTime = Time::getHighResolutionTicks (); }
|
||||
double getElapsed ()
|
||||
{
|
||||
int64 const now = Time::getHighResolutionTicks();
|
||||
return Time::highResolutionTicksToSeconds (now - m_startTime);
|
||||
}
|
||||
private:
|
||||
int64 m_startTime;
|
||||
};
|
||||
|
||||
static int const callsPerThread = 50000;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename ServiceType>
|
||||
struct Consumer : Thread
|
||||
{
|
||||
ServiceType& m_service;
|
||||
Random m_random;
|
||||
String m_string;
|
||||
|
||||
Consumer (int id, int64 seedValue, ServiceType& service)
|
||||
: Thread ("C#" + String::fromNumber (id))
|
||||
, m_service (service)
|
||||
, m_random (seedValue)
|
||||
{ startThread(); }
|
||||
|
||||
~Consumer ()
|
||||
{ stopThread(); }
|
||||
|
||||
static Consumer*& thread()
|
||||
{
|
||||
static ThreadLocalValue <Consumer*> local;
|
||||
return local.get();
|
||||
}
|
||||
|
||||
static void stop_one ()
|
||||
{ thread()->signalThreadShouldExit(); }
|
||||
|
||||
static void handler ()
|
||||
{ thread()->do_handler(); }
|
||||
|
||||
void do_handler()
|
||||
{
|
||||
String const s (String::fromNumber (m_random.nextInt()));
|
||||
m_string += s;
|
||||
if (m_string.length() > 100)
|
||||
m_string = String::empty;
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
thread() = this;
|
||||
while (! threadShouldExit())
|
||||
m_service.run_one();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename ServiceType>
|
||||
struct Producer : Thread
|
||||
{
|
||||
ServiceType& m_service;
|
||||
Random m_random;
|
||||
String m_string;
|
||||
|
||||
Producer (int id, int64 seedValue, ServiceType& service)
|
||||
: Thread ("P#" + String::fromNumber (id))
|
||||
, m_service (service)
|
||||
, m_random (seedValue)
|
||||
{ }
|
||||
|
||||
~Producer ()
|
||||
{ stopThread(); }
|
||||
|
||||
void run ()
|
||||
{
|
||||
for (std::size_t i = 0; i < callsPerThread; ++i)
|
||||
{
|
||||
String const s (String::fromNumber (m_random.nextInt()));
|
||||
m_string += s;
|
||||
if (m_string.length() > 100)
|
||||
m_string = String::empty;
|
||||
m_service.dispatch (bind (&Consumer<ServiceType>::handler));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename Allocator>
|
||||
void testThreads (std::size_t nConsumers, std::size_t nProducers)
|
||||
{
|
||||
beginTestCase (String::fromNumber (nConsumers) + " consumers, " +
|
||||
String::fromNumber (nProducers) + " producers, " +
|
||||
"Allocator = " + std::string(typeid(Allocator).name()));
|
||||
|
||||
typedef ServiceQueueType <Allocator> ServiceType;
|
||||
|
||||
ServiceType service (nConsumers);
|
||||
std::vector <ScopedPointer <Consumer <ServiceType> > > consumers;
|
||||
std::vector <ScopedPointer <Producer <ServiceType> > > producers;
|
||||
consumers.reserve (nConsumers);
|
||||
producers.reserve (nProducers);
|
||||
|
||||
for (std::size_t i = 0; i < nConsumers; ++i)
|
||||
consumers.push_back (new Consumer <ServiceType> (i + 1,
|
||||
random().nextInt64(), service));
|
||||
|
||||
for (std::size_t i = 0; i < nProducers; ++i)
|
||||
producers.push_back (new Producer <ServiceType> (i + 1,
|
||||
random().nextInt64(), service));
|
||||
|
||||
Stopwatch t;
|
||||
|
||||
for (std::size_t i = 0; i < producers.size(); ++i)
|
||||
producers[i]->startThread();
|
||||
|
||||
for (std::size_t i = 0; i < producers.size(); ++i)
|
||||
producers[i]->waitForThreadToExit();
|
||||
|
||||
for (std::size_t i = 0; i < consumers.size(); ++i)
|
||||
service.dispatch (bind (&Consumer <ServiceType>::stop_one));
|
||||
|
||||
for (std::size_t i = 0; i < consumers.size(); ++i)
|
||||
consumers[i]->waitForThreadToExit();
|
||||
|
||||
double const seconds (t.getElapsed());
|
||||
logMessage (String (seconds, 2) + " seconds");
|
||||
|
||||
pass();
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
#if 1
|
||||
testThreads <std::allocator<char> > (1, 1);
|
||||
testThreads <std::allocator<char> > (1, 4);
|
||||
testThreads <std::allocator<char> > (1, 16);
|
||||
testThreads <std::allocator<char> > (4, 1);
|
||||
testThreads <std::allocator<char> > (8, 16);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
testThreads <detail::ServiceQueueAllocator<char> > (1, 1);
|
||||
testThreads <detail::ServiceQueueAllocator<char> > (1, 4);
|
||||
testThreads <detail::ServiceQueueAllocator<char> > (1, 16);
|
||||
testThreads <detail::ServiceQueueAllocator<char> > (4, 1);
|
||||
testThreads <detail::ServiceQueueAllocator<char> > (8, 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
ServiceQueueTimingTests () : UnitTest ("ServiceQueueTiming", "beast", runManual)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static ServiceQueueTimingTests serviceQueueTimingTests;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ServiceQueueTests
|
||||
: public UnitTest
|
||||
{
|
||||
public:
|
||||
struct ServiceThread : Thread
|
||||
{
|
||||
Random m_random;
|
||||
ServiceQueue& m_service;
|
||||
String m_string;
|
||||
|
||||
ServiceThread (int id, int64 seedValue,
|
||||
ServiceQueue& service)
|
||||
: Thread ("#" + String::fromNumber (id))
|
||||
, m_random (seedValue)
|
||||
, m_service (service)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~ServiceThread ()
|
||||
{
|
||||
stopThread();
|
||||
}
|
||||
|
||||
static ServiceThread*& thread()
|
||||
{
|
||||
static ThreadLocalValue <ServiceThread*> local;
|
||||
return local.get();
|
||||
}
|
||||
|
||||
static void stop_one ()
|
||||
{
|
||||
thread()->signalThreadShouldExit();
|
||||
}
|
||||
|
||||
static void handler ()
|
||||
{
|
||||
thread()->do_handler();
|
||||
}
|
||||
|
||||
void do_handler()
|
||||
{
|
||||
#if 1
|
||||
String const s (String::fromNumber (m_random.nextInt()));
|
||||
m_string += s;
|
||||
if (m_string.length() > 100)
|
||||
m_string = String::empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
thread() = this;
|
||||
while (! threadShouldExit())
|
||||
m_service.run_one();
|
||||
}
|
||||
};
|
||||
|
||||
static int const callsPerThread = 10000;
|
||||
|
||||
void testThreads (std::size_t n)
|
||||
{
|
||||
beginTestCase (String::fromNumber (n) + " threads");
|
||||
ServiceQueue service (n);
|
||||
std::vector <ScopedPointer <ServiceThread> > threads;
|
||||
threads.reserve (n);
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
threads.push_back (new ServiceThread (i + 1,
|
||||
random().nextInt64(), service));
|
||||
for (std::size_t i = n * callsPerThread; i; --i)
|
||||
service.dispatch (bind (&ServiceThread::handler));
|
||||
for (std::size_t i = 0; i < threads.size(); ++i)
|
||||
service.dispatch (bind (&ServiceThread::stop_one));
|
||||
for (std::size_t i = 0; i < threads.size(); ++i)
|
||||
threads[i]->waitForThreadToExit();
|
||||
pass();
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
testThreads (1);
|
||||
testThreads (4);
|
||||
testThreads (16);
|
||||
}
|
||||
|
||||
ServiceQueueTests () : UnitTest ("ServiceQueue", "beast")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static ServiceQueueTests serviceQueueTests;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
198
beast/threads/impl/Stoppable.cpp
Normal file
198
beast/threads/impl/Stoppable.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../Stoppable.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
}
|
||||
599
beast/threads/impl/Thread.cpp
Normal file
599
beast/threads/impl/Thread.cpp
Normal file
@@ -0,0 +1,599 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../Thread.h"
|
||||
|
||||
namespace beast {
|
||||
|
||||
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 RecursiveMutex::ScopedLockType sl (startStopLock);
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
launchThread();
|
||||
setThreadPriority (threadHandle, threadPriority);
|
||||
startSuspensionEvent.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::startThread (const int priority)
|
||||
{
|
||||
const RecursiveMutex::ScopedLockType 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 RecursiveMutex::ScopedLockType 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 RecursiveMutex::ScopedLockType 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 RecursiveMutex::ScopedLockType 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();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
// This is here so we dont have circular includes
|
||||
//
|
||||
void SpinLock::enter() const noexcept
|
||||
{
|
||||
if (! tryEnter())
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
if (tryEnter())
|
||||
return;
|
||||
|
||||
while (! tryEnter())
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <tchar.h>
|
||||
|
||||
namespace beast {
|
||||
|
||||
HWND beast_messageWindowHandle = 0; // (this is used by other parts of the codebase)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#else
|
||||
|
||||
#include <time.h>
|
||||
#if BEAST_BSD
|
||||
// ???
|
||||
#else
|
||||
# include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
|
||||
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 BEAST_API beast_threadEntryPoint (void*);
|
||||
|
||||
extern "C" void* threadEntryProc (void*);
|
||||
extern "C" void* threadEntryProc (void* userData)
|
||||
{
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
#if BEAST_ANDROID
|
||||
struct AndroidThreadScope
|
||||
{
|
||||
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
|
||||
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
|
||||
};
|
||||
|
||||
const AndroidThreadScope androidEnv;
|
||||
#endif
|
||||
|
||||
beast_threadEntryPoint (userData);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Thread::launchThread()
|
||||
{
|
||||
threadHandle = 0;
|
||||
pthread_t handle = 0;
|
||||
|
||||
if (pthread_create (&handle, 0, threadEntryProc, this) == 0)
|
||||
{
|
||||
pthread_detach (handle);
|
||||
threadHandle = (void*) handle;
|
||||
threadId = (ThreadID) threadHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
threadId = 0;
|
||||
threadHandle = 0;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle != 0)
|
||||
{
|
||||
#if BEAST_ANDROID
|
||||
bassertfalse; // pthread_cancel not available!
|
||||
#else
|
||||
pthread_cancel ((pthread_t) threadHandle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::setCurrentThreadName (const String& name)
|
||||
{
|
||||
#if BEAST_IOS || (BEAST_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSThread currentThread] setName: beastStringToNS (name)];
|
||||
}
|
||||
#elif BEAST_LINUX
|
||||
#if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
|
||||
pthread_setname_np (pthread_self(), name.toRawUTF8());
|
||||
#else
|
||||
prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Thread::setThreadPriority (void* handle, int priority)
|
||||
{
|
||||
struct sched_param param;
|
||||
int policy;
|
||||
priority = blimit (0, 10, priority);
|
||||
|
||||
if (handle == nullptr)
|
||||
handle = (void*) pthread_self();
|
||||
|
||||
if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0)
|
||||
return false;
|
||||
|
||||
policy = priority == 0 ? SCHED_OTHER : SCHED_RR;
|
||||
|
||||
const int minPriority = sched_get_priority_min (policy);
|
||||
const int maxPriority = sched_get_priority_max (policy);
|
||||
|
||||
param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
|
||||
return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0;
|
||||
}
|
||||
|
||||
Thread::ThreadID Thread::getCurrentThreadId()
|
||||
{
|
||||
return (ThreadID) pthread_self();
|
||||
}
|
||||
|
||||
void Thread::yield()
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/* Remove this macro if you're having problems compiling the cpu affinity
|
||||
calls (the API for these has changed about quite a bit in various Linux
|
||||
versions, and a lot of distros seem to ship with obsolete versions)
|
||||
*/
|
||||
#if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES)
|
||||
#define SUPPORT_AFFINITIES 1
|
||||
#endif
|
||||
|
||||
void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
||||
{
|
||||
#if SUPPORT_AFFINITIES
|
||||
cpu_set_t affinity;
|
||||
CPU_ZERO (&affinity);
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
if ((affinityMask & (1 << i)) != 0)
|
||||
CPU_SET (i, &affinity);
|
||||
|
||||
/*
|
||||
N.B. If this line causes a compile error, then you've probably not got the latest
|
||||
version of glibc installed.
|
||||
|
||||
If you don't want to update your copy of glibc and don't care about cpu affinities,
|
||||
then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0.
|
||||
*/
|
||||
sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
|
||||
sched_yield();
|
||||
|
||||
#else
|
||||
/* affinities aren't supported because either the appropriate header files weren't found,
|
||||
or the SUPPORT_AFFINITIES macro was turned off
|
||||
*/
|
||||
bassertfalse;
|
||||
(void) affinityMask;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
|
||||
168
beast/threads/impl/WaitableEvent.cpp
Normal file
168
beast/threads/impl/WaitableEvent.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "../WaitableEvent.h"
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
#undef check
|
||||
#undef direct
|
||||
#undef max
|
||||
#undef min
|
||||
#undef TYPE_BOOL
|
||||
|
||||
namespace beast {
|
||||
|
||||
WaitableEvent::WaitableEvent (const bool manualReset, bool initiallySignaled)
|
||||
: handle (CreateEvent (0, manualReset ? TRUE : FALSE, initiallySignaled ? TRUE : FALSE, 0))
|
||||
{
|
||||
}
|
||||
|
||||
WaitableEvent::~WaitableEvent()
|
||||
{
|
||||
CloseHandle (handle);
|
||||
}
|
||||
|
||||
void WaitableEvent::signal() const
|
||||
{
|
||||
SetEvent (handle);
|
||||
}
|
||||
|
||||
void WaitableEvent::reset() const
|
||||
{
|
||||
ResetEvent (handle);
|
||||
}
|
||||
|
||||
bool WaitableEvent::wait () const
|
||||
{
|
||||
return WaitForSingleObject (handle, INFINITE) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
bool WaitableEvent::wait (const int timeOutMs) const
|
||||
{
|
||||
if (timeOutMs >= 0)
|
||||
return WaitForSingleObject (handle,
|
||||
(DWORD) timeOutMs) == WAIT_OBJECT_0;
|
||||
return wait ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace beast {
|
||||
|
||||
WaitableEvent::WaitableEvent (const bool useManualReset, bool initiallySignaled)
|
||||
: triggered (false), manualReset (useManualReset)
|
||||
{
|
||||
pthread_cond_init (&condition, 0);
|
||||
|
||||
pthread_mutexattr_t atts;
|
||||
pthread_mutexattr_init (&atts);
|
||||
#if ! BEAST_ANDROID
|
||||
pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
|
||||
#endif
|
||||
pthread_mutex_init (&mutex, &atts);
|
||||
|
||||
if (initiallySignaled)
|
||||
signal ();
|
||||
}
|
||||
|
||||
WaitableEvent::~WaitableEvent()
|
||||
{
|
||||
pthread_cond_destroy (&condition);
|
||||
pthread_mutex_destroy (&mutex);
|
||||
}
|
||||
|
||||
bool WaitableEvent::wait () const
|
||||
{
|
||||
return wait (-1);
|
||||
}
|
||||
|
||||
bool WaitableEvent::wait (const int timeOutMillisecs) const
|
||||
{
|
||||
pthread_mutex_lock (&mutex);
|
||||
|
||||
if (! triggered)
|
||||
{
|
||||
if (timeOutMillisecs < 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
pthread_cond_wait (&condition, &mutex);
|
||||
}
|
||||
while (! triggered);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday (&now, 0);
|
||||
|
||||
struct timespec time;
|
||||
time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000);
|
||||
time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000;
|
||||
|
||||
if (time.tv_nsec >= 1000000000)
|
||||
{
|
||||
time.tv_nsec -= 1000000000;
|
||||
time.tv_sec++;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT)
|
||||
{
|
||||
pthread_mutex_unlock (&mutex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (! triggered);
|
||||
}
|
||||
}
|
||||
|
||||
if (! manualReset)
|
||||
triggered = false;
|
||||
|
||||
pthread_mutex_unlock (&mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaitableEvent::signal() const
|
||||
{
|
||||
pthread_mutex_lock (&mutex);
|
||||
triggered = true;
|
||||
pthread_cond_broadcast (&condition);
|
||||
pthread_mutex_unlock (&mutex);
|
||||
}
|
||||
|
||||
void WaitableEvent::reset() const
|
||||
{
|
||||
pthread_mutex_lock (&mutex);
|
||||
triggered = false;
|
||||
pthread_mutex_unlock (&mutex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user