mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Squashed 'src/beast/' content from commit 43e6d34
git-subtree-dir: src/beast
git-subtree-split: 43e6d345e4
This commit is contained in:
85
modules/beast_core/threads/beast_ChildProcess.cpp
Normal file
85
modules/beast_core/threads/beast_ChildProcess.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ChildProcess::ChildProcess() {}
|
||||
ChildProcess::~ChildProcess() {}
|
||||
|
||||
bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const
|
||||
{
|
||||
const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;
|
||||
|
||||
do
|
||||
{
|
||||
if (! isRunning())
|
||||
return true;
|
||||
}
|
||||
while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String ChildProcess::readAllProcessOutput()
|
||||
{
|
||||
MemoryOutputStream result;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buffer [512];
|
||||
const int num = readProcessOutput (buffer, sizeof (buffer));
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
||||
result.write (buffer, (size_t) num);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class ChildProcessTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ChildProcessTests() : UnitTest ("ChildProcess", "beast") {}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("Child Processes");
|
||||
|
||||
#if BEAST_WINDOWS || BEAST_MAC || BEAST_LINUX
|
||||
ChildProcess p;
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
expect (p.start ("tasklist"));
|
||||
#else
|
||||
expect (p.start ("ls /"));
|
||||
#endif
|
||||
|
||||
//String output (p.readAllProcessOutput());
|
||||
//expect (output.isNotEmpty());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static ChildProcessTests childProcessTests;
|
||||
98
modules/beast_core/threads/beast_ChildProcess.h
Normal file
98
modules/beast_core/threads/beast_ChildProcess.h
Normal file
@@ -0,0 +1,98 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CHILDPROCESS_H_INCLUDED
|
||||
#define BEAST_CHILDPROCESS_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Launches and monitors a child process.
|
||||
|
||||
This class lets you launch an executable, and read its output. You can also
|
||||
use it to check whether the child process has finished.
|
||||
*/
|
||||
class BEAST_API ChildProcess : LeakChecked <ChildProcess>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a process object.
|
||||
To actually launch the process, use start().
|
||||
*/
|
||||
ChildProcess();
|
||||
|
||||
/** Destructor.
|
||||
Note that deleting this object won't terminate the child process.
|
||||
*/
|
||||
~ChildProcess();
|
||||
|
||||
/** Attempts to launch a child process command.
|
||||
|
||||
The command should be the name of the executable file, followed by any arguments
|
||||
that are required.
|
||||
If the process has already been launched, this will launch it again. If a problem
|
||||
occurs, the method will return false.
|
||||
*/
|
||||
bool start (const String& command);
|
||||
|
||||
/** Attempts to launch a child process command.
|
||||
|
||||
The first argument should be the name of the executable file, followed by any other
|
||||
arguments that are needed.
|
||||
If the process has already been launched, this will launch it again. If a problem
|
||||
occurs, the method will return false.
|
||||
*/
|
||||
bool start (const StringArray& arguments);
|
||||
|
||||
/** Returns true if the child process is alive. */
|
||||
bool isRunning() const;
|
||||
|
||||
/** Attempts to read some output from the child process.
|
||||
This will attempt to read up to the given number of bytes of data from the
|
||||
process. It returns the number of bytes that were actually read.
|
||||
*/
|
||||
int readProcessOutput (void* destBuffer, int numBytesToRead);
|
||||
|
||||
/** Blocks until the process has finished, and then returns its complete output
|
||||
as a string.
|
||||
*/
|
||||
String readAllProcessOutput();
|
||||
|
||||
/** Blocks until the process is no longer running. */
|
||||
bool waitForProcessToFinish (int timeoutMs) const;
|
||||
|
||||
/** Attempts to kill the child process.
|
||||
Returns true if it succeeded. Trying to read from the process after calling this may
|
||||
result in undefined behaviour.
|
||||
*/
|
||||
bool kill();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ActiveProcess;
|
||||
friend class ScopedPointer<ActiveProcess>;
|
||||
ScopedPointer<ActiveProcess> activeProcess;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_CHILDPROCESS_H_INCLUDED
|
||||
257
modules/beast_core/threads/beast_CriticalSection.h
Normal file
257
modules/beast_core/threads/beast_CriticalSection.h
Normal file
@@ -0,0 +1,257 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CRITICALSECTION_H_INCLUDED
|
||||
#define BEAST_CRITICALSECTION_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A re-entrant mutex.
|
||||
|
||||
A CriticalSection acts as a re-entrant mutex object. The best way to lock and unlock
|
||||
one of these is by using RAII in the form of a local ScopedLock object - have a look
|
||||
through the codebase for many examples of how to do this.
|
||||
|
||||
@see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock
|
||||
*/
|
||||
class BEAST_API CriticalSection : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a CriticalSection object. */
|
||||
CriticalSection() noexcept;
|
||||
|
||||
/** Destructor.
|
||||
If the critical section is deleted whilst locked, any subsequent behaviour
|
||||
is unpredictable.
|
||||
*/
|
||||
~CriticalSection() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Acquires the lock.
|
||||
|
||||
If the lock is already held by the caller thread, the method returns immediately.
|
||||
If the lock is currently held by another thread, this will wait until it becomes free.
|
||||
|
||||
It's strongly recommended that you never call this method directly - instead use the
|
||||
ScopedLock class to manage the locking using an RAII pattern instead.
|
||||
|
||||
@see exit, tryEnter, ScopedLock
|
||||
*/
|
||||
void enter() const noexcept;
|
||||
|
||||
/** Attempts to lock this critical section without blocking.
|
||||
|
||||
This method behaves identically to CriticalSection::enter, except that the caller thread
|
||||
does not wait if the lock is currently held by another thread but returns false immediately.
|
||||
|
||||
@returns false if the lock is currently held by another thread, true otherwise.
|
||||
@see enter
|
||||
*/
|
||||
bool tryEnter() const noexcept;
|
||||
|
||||
/** Releases the lock.
|
||||
|
||||
If the caller thread hasn't got the lock, this can have unpredictable results.
|
||||
|
||||
If the enter() method has been called multiple times by the thread, each
|
||||
call must be matched by a call to exit() before other threads will be allowed
|
||||
to take over the lock.
|
||||
|
||||
@see enter, ScopedLock
|
||||
*/
|
||||
void exit() const noexcept;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Provides the type of scoped lock to use with a CriticalSection. */
|
||||
typedef GenericScopedLock <CriticalSection> ScopedLockType;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a CriticalSection. */
|
||||
typedef GenericScopedUnlock <CriticalSection> ScopedUnlockType;
|
||||
|
||||
/** Provides the type of scoped try-locker to use with a CriticalSection. */
|
||||
typedef GenericScopedTryLock <CriticalSection> ScopedTryLockType;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Boost concept compatibility
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts
|
||||
//
|
||||
|
||||
// BasicLockable
|
||||
inline void lock () const noexcept { enter (); }
|
||||
inline void unlock () const noexcept { exit (); }
|
||||
|
||||
// Lockable
|
||||
inline bool try_lock () const noexcept { return tryEnter (); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
#if BEAST_WINDOWS
|
||||
// To avoid including windows.h in the public Beast headers, we'll just allocate
|
||||
// a block of memory here that's big enough to be used internally as a windows
|
||||
// CRITICAL_SECTION structure.
|
||||
#if BEAST_64BIT
|
||||
uint8 section[44];
|
||||
#else
|
||||
uint8 section[24];
|
||||
#endif
|
||||
#else
|
||||
mutable pthread_mutex_t mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class that can be used in place of a real CriticalSection object, but which
|
||||
doesn't perform any locking.
|
||||
|
||||
This is currently used by some templated classes, and most compilers should
|
||||
manage to optimise it out of existence.
|
||||
|
||||
@see CriticalSection, Array, OwnedArray, SharedObjectArray
|
||||
*/
|
||||
class BEAST_API DummyCriticalSection : public Uncopyable
|
||||
{
|
||||
public:
|
||||
inline DummyCriticalSection() noexcept {}
|
||||
inline ~DummyCriticalSection() noexcept {}
|
||||
|
||||
inline void enter() const noexcept {}
|
||||
inline bool tryEnter() const noexcept { return true; }
|
||||
inline void exit() const noexcept {}
|
||||
|
||||
//==============================================================================
|
||||
/** A dummy scoped-lock type to use with a dummy critical section. */
|
||||
struct ScopedLockType
|
||||
{
|
||||
ScopedLockType (const DummyCriticalSection&) noexcept {}
|
||||
};
|
||||
|
||||
/** A dummy scoped-unlocker type to use with a dummy critical section. */
|
||||
typedef ScopedLockType ScopedUnlockType;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a CriticalSection object.
|
||||
|
||||
Use one of these as a local variable to provide RAII-based locking of a CriticalSection.
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const ScopedLock myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
...do some stuff...
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see CriticalSection, ScopedUnlock
|
||||
*/
|
||||
typedef CriticalSection::ScopedLockType ScopedLock;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically unlocks and re-locks a CriticalSection object.
|
||||
|
||||
This is the reverse of a ScopedLock object - instead of locking the critical
|
||||
section for the lifetime of this object, it unlocks it.
|
||||
|
||||
Make sure you don't try to unlock critical sections that aren't actually locked!
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const ScopedLock myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
... do some stuff with it locked ..
|
||||
|
||||
while (xyz)
|
||||
{
|
||||
... do some stuff with it locked ..
|
||||
|
||||
const ScopedUnlock unlocker (myCriticalSection);
|
||||
|
||||
// myCriticalSection is now unlocked for the remainder of this block,
|
||||
// and re-locked at the end.
|
||||
|
||||
...do some stuff with it unlocked ...
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see CriticalSection, ScopedLock
|
||||
*/
|
||||
typedef CriticalSection::ScopedUnlockType ScopedUnlock;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically tries to lock and unlock a CriticalSection object.
|
||||
|
||||
Use one of these as a local variable to control access to a CriticalSection.
|
||||
|
||||
e.g. @code
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const ScopedTryLock myScopedTryLock (myCriticalSection);
|
||||
|
||||
// Unlike using a ScopedLock, this may fail to actually get the lock, so you
|
||||
// should test this with the isLocked() method before doing your thread-unsafe
|
||||
// action..
|
||||
if (myScopedTryLock.isLocked())
|
||||
{
|
||||
...do some stuff...
|
||||
}
|
||||
else
|
||||
{
|
||||
..our attempt at locking failed because another thread had already locked it..
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here (if it was locked)
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock
|
||||
*/
|
||||
typedef CriticalSection::ScopedTryLockType ScopedTryLock;
|
||||
|
||||
|
||||
#endif // BEAST_CRITICALSECTION_H_INCLUDED
|
||||
78
modules/beast_core/threads/beast_DynamicLibrary.h
Normal file
78
modules/beast_core/threads/beast_DynamicLibrary.h
Normal file
@@ -0,0 +1,78 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_DYNAMICLIBRARY_H_INCLUDED
|
||||
#define BEAST_DYNAMICLIBRARY_H_INCLUDED
|
||||
|
||||
/**
|
||||
Handles the opening and closing of DLLs.
|
||||
|
||||
This class can be used to open a DLL and get some function pointers from it.
|
||||
Since the DLL is freed when this object is deleted, it's handy for managing
|
||||
library lifetimes using RAII.
|
||||
*/
|
||||
class BEAST_API DynamicLibrary : LeakChecked <DynamicLibrary>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Creates an unopened DynamicLibrary object.
|
||||
Call open() to actually open one.
|
||||
*/
|
||||
DynamicLibrary() noexcept : handle (nullptr) {}
|
||||
|
||||
/**
|
||||
*/
|
||||
DynamicLibrary (const String& name) : handle (nullptr) { open (name); }
|
||||
|
||||
/** Destructor.
|
||||
If a library is currently open, it will be closed when this object is destroyed.
|
||||
*/
|
||||
~DynamicLibrary() { close(); }
|
||||
|
||||
/** Opens a DLL.
|
||||
The name and the method by which it gets found is of course platform-specific, and
|
||||
may or may not include a path, depending on the OS.
|
||||
If a library is already open when this method is called, it will first close the library
|
||||
before attempting to load the new one.
|
||||
@returns true if the library was successfully found and opened.
|
||||
*/
|
||||
bool open (const String& name);
|
||||
|
||||
/** Releases the currently-open DLL, or has no effect if none was open. */
|
||||
void close();
|
||||
|
||||
/** Tries to find a named function in the currently-open DLL, and returns a pointer to it.
|
||||
If no library is open, or if the function isn't found, this will return a null pointer.
|
||||
*/
|
||||
void* getFunction (const String& functionName) noexcept;
|
||||
|
||||
/** Returns the platform-specific native library handle.
|
||||
You'll need to cast this to whatever is appropriate for the OS that's in use.
|
||||
*/
|
||||
void* getNativeHandle() const noexcept { return handle; }
|
||||
|
||||
private:
|
||||
void* handle;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_DYNAMICLIBRARY_H_INCLUDED
|
||||
31
modules/beast_core/threads/beast_HighResolutionTimer.cpp
Normal file
31
modules/beast_core/threads/beast_HighResolutionTimer.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
HighResolutionTimer::HighResolutionTimer() { pimpl = new Pimpl (*this); }
|
||||
HighResolutionTimer::~HighResolutionTimer() { stopTimer(); }
|
||||
|
||||
void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (bmax (1, periodMs)); }
|
||||
void HighResolutionTimer::stopTimer() { pimpl->stop(); }
|
||||
|
||||
bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; }
|
||||
int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; }
|
||||
102
modules/beast_core/threads/beast_HighResolutionTimer.h
Normal file
102
modules/beast_core/threads/beast_HighResolutionTimer.h
Normal file
@@ -0,0 +1,102 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_HIGHRESOLUTIONTIMER_H_INCLUDED
|
||||
#define BEAST_HIGHRESOLUTIONTIMER_H_INCLUDED
|
||||
|
||||
/**
|
||||
A high-resolution periodic timer.
|
||||
|
||||
This provides accurately-timed regular callbacks. Unlike the normal Timer
|
||||
class, this one uses a dedicated thread, not the message thread, so is
|
||||
far more stable and precise.
|
||||
|
||||
You should only use this class in situations where you really need accuracy,
|
||||
because unlike the normal Timer class, which is very lightweight and cheap
|
||||
to start/stop, the HighResolutionTimer will use far more resources, and
|
||||
starting/stopping it may involve launching and killing threads.
|
||||
|
||||
@see Timer
|
||||
*/
|
||||
class BEAST_API HighResolutionTimer : LeakChecked <HighResolutionTimer>, public Uncopyable
|
||||
{
|
||||
protected:
|
||||
/** Creates a HighResolutionTimer.
|
||||
When created, the timer is stopped, so use startTimer() to get it going.
|
||||
*/
|
||||
HighResolutionTimer();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~HighResolutionTimer();
|
||||
|
||||
//==============================================================================
|
||||
/** The user-defined callback routine that actually gets called periodically.
|
||||
|
||||
This will be called on a dedicated timer thread, so make sure your
|
||||
implementation is thread-safe!
|
||||
|
||||
It's perfectly ok to call startTimer() or stopTimer() from within this
|
||||
callback to change the subsequent intervals.
|
||||
*/
|
||||
virtual void hiResTimerCallback() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts the timer and sets the length of interval required.
|
||||
|
||||
If the timer is already started, this will reset its counter, so the
|
||||
time between calling this method and the next timer callback will not be
|
||||
less than the interval length passed in.
|
||||
|
||||
@param intervalInMilliseconds the interval to use (any values less than 1 will be
|
||||
rounded up to 1)
|
||||
*/
|
||||
void startTimer (int intervalInMilliseconds);
|
||||
|
||||
/** Stops the timer.
|
||||
|
||||
This method may block while it waits for pending callbacks to complete. Once it
|
||||
returns, no more callbacks will be made. If it is called from the timer's own thread,
|
||||
it will cancel the timer after the current callback returns.
|
||||
*/
|
||||
void stopTimer();
|
||||
|
||||
/** Checks if the timer has been started.
|
||||
@returns true if the timer is running.
|
||||
*/
|
||||
bool isTimerRunning() const noexcept;
|
||||
|
||||
/** Returns the timer's interval.
|
||||
@returns the timer's interval in milliseconds if it's running, or 0 if it's not.
|
||||
*/
|
||||
int getTimerInterval() const noexcept;
|
||||
|
||||
private:
|
||||
struct Pimpl;
|
||||
friend struct Pimpl;
|
||||
friend class ScopedPointer<Pimpl>;
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_HIGHRESOLUTIONTIMER_H_INCLUDED
|
||||
118
modules/beast_core/threads/beast_InterProcessLock.h
Normal file
118
modules/beast_core/threads/beast_InterProcessLock.h
Normal file
@@ -0,0 +1,118 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_INTERPROCESSLOCK_H_INCLUDED
|
||||
#define BEAST_INTERPROCESSLOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Acts as a critical section which processes can use to block each other.
|
||||
|
||||
@see CriticalSection
|
||||
*/
|
||||
class BEAST_API InterProcessLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a lock object.
|
||||
@param name a name that processes will use to identify this lock object
|
||||
*/
|
||||
explicit InterProcessLock (const String& name);
|
||||
|
||||
/** Destructor.
|
||||
This will also release the lock if it's currently held by this process.
|
||||
*/
|
||||
~InterProcessLock();
|
||||
|
||||
//==============================================================================
|
||||
/** Attempts to lock the critical section.
|
||||
|
||||
@param timeOutMillisecs how many milliseconds to wait if the lock is already
|
||||
held by another process - a value of 0 will return
|
||||
immediately, negative values will wait forever
|
||||
@returns true if the lock could be gained within the timeout period, or
|
||||
false if the timeout expired.
|
||||
*/
|
||||
bool enter (int timeOutMillisecs = -1);
|
||||
|
||||
/** Releases the lock if it's currently held by this process. */
|
||||
void exit();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks an InterProcessLock object.
|
||||
|
||||
This works like a ScopedLock, but using an InterprocessLock rather than
|
||||
a CriticalSection.
|
||||
|
||||
@see ScopedLock
|
||||
*/
|
||||
class ScopedLockType : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a scoped lock.
|
||||
|
||||
As soon as it is created, this will lock the InterProcessLock, and
|
||||
when the ScopedLockType object is deleted, the InterProcessLock will
|
||||
be unlocked.
|
||||
|
||||
Note that since an InterprocessLock can fail due to errors, you should check
|
||||
isLocked() to make sure that the lock was successful before using it.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
explicit ScopedLockType (InterProcessLock& l) : ipLock (l) { lockWasSuccessful = l.enter(); }
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The InterProcessLock will be unlocked when the destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~ScopedLockType() { ipLock.exit(); }
|
||||
|
||||
/** Returns true if the InterProcessLock was successfully locked. */
|
||||
bool isLocked() const noexcept { return lockWasSuccessful; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
InterProcessLock& ipLock;
|
||||
bool lockWasSuccessful;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
friend class ScopedPointer <Pimpl>;
|
||||
ScopedPointer <Pimpl> pimpl;
|
||||
|
||||
CriticalSection lock;
|
||||
String name;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_INTERPROCESSLOCK_H_INCLUDED
|
||||
141
modules/beast_core/threads/beast_Process.h
Normal file
141
modules/beast_core/threads/beast_Process.h
Normal file
@@ -0,0 +1,141 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_PROCESS_H_INCLUDED
|
||||
#define BEAST_PROCESS_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/** Represents the current executable's process.
|
||||
|
||||
This contains methods for controlling the current application at the
|
||||
process-level.
|
||||
|
||||
@see Thread, BEASTApplication
|
||||
*/
|
||||
class BEAST_API Process : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum ProcessPriority
|
||||
{
|
||||
LowPriority = 0,
|
||||
NormalPriority = 1,
|
||||
HighPriority = 2,
|
||||
RealtimePriority = 3
|
||||
};
|
||||
|
||||
/** Changes the current process's priority.
|
||||
|
||||
@param priority the process priority, where
|
||||
0=low, 1=normal, 2=high, 3=realtime
|
||||
*/
|
||||
static void setPriority (const ProcessPriority priority);
|
||||
|
||||
/** Kills the current process immediately.
|
||||
|
||||
This is an emergency process terminator that kills the application
|
||||
immediately - it's intended only for use only when something goes
|
||||
horribly wrong.
|
||||
|
||||
@see BEASTApplication::quit
|
||||
*/
|
||||
static void terminate();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this application process is the one that the user is
|
||||
currently using.
|
||||
*/
|
||||
static bool isForegroundProcess();
|
||||
|
||||
/** Attempts to make the current process the active one.
|
||||
(This is not possible on some platforms).
|
||||
*/
|
||||
static void makeForegroundProcess();
|
||||
|
||||
//==============================================================================
|
||||
/** Raises the current process's privilege level.
|
||||
|
||||
Does nothing if this isn't supported by the current OS, or if process
|
||||
privilege level is fixed.
|
||||
*/
|
||||
static void raisePrivilege();
|
||||
|
||||
/** Lowers the current process's privilege level.
|
||||
|
||||
Does nothing if this isn't supported by the current OS, or if process
|
||||
privilege level is fixed.
|
||||
*/
|
||||
static void lowerPrivilege();
|
||||
|
||||
/** Returns true if this process is being hosted by a debugger. */
|
||||
static bool BEAST_CALLTYPE isRunningUnderDebugger();
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to launch the OS's default reader application for a given file or URL. */
|
||||
static bool openDocument (const String& documentURL, const String& parameters);
|
||||
|
||||
/** Tries to launch the OS's default email application to let the user create a message. */
|
||||
static bool openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
const String& bodyText,
|
||||
const StringArray& filesToAttach);
|
||||
|
||||
#if BEAST_WINDOWS || DOXYGEN
|
||||
//==============================================================================
|
||||
/** WINDOWS ONLY - This returns the HINSTANCE of the current module.
|
||||
|
||||
The return type is a void* to avoid being dependent on windows.h - just cast
|
||||
it to a HINSTANCE to use it.
|
||||
|
||||
In a normal BEAST application, this will be automatically set to the module
|
||||
handle of the executable.
|
||||
|
||||
If you've built a DLL and plan to use any BEAST messaging or windowing classes,
|
||||
you'll need to make sure you call the setCurrentModuleInstanceHandle()
|
||||
to provide the correct module handle in your DllMain() function, because
|
||||
the system relies on the correct instance handle when opening windows.
|
||||
*/
|
||||
static void* BEAST_CALLTYPE getCurrentModuleInstanceHandle() noexcept;
|
||||
|
||||
/** WINDOWS ONLY - Sets a new module handle to be used by the library.
|
||||
|
||||
The parameter type is a void* to avoid being dependent on windows.h, but it actually
|
||||
expects a HINSTANCE value.
|
||||
|
||||
@see getCurrentModuleInstanceHandle()
|
||||
*/
|
||||
static void BEAST_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept;
|
||||
#endif
|
||||
|
||||
#if BEAST_MAC || DOXYGEN
|
||||
//==============================================================================
|
||||
/** OSX ONLY - Shows or hides the OSX dock icon for this app. */
|
||||
static void setDockIconVisible (bool isVisible);
|
||||
#endif
|
||||
|
||||
private:
|
||||
Process();
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_PROCESS_H_INCLUDED
|
||||
153
modules/beast_core/threads/beast_ReadWriteLock.cpp
Normal file
153
modules/beast_core/threads/beast_ReadWriteLock.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ReadWriteLock::ReadWriteLock() noexcept
|
||||
: numWaitingWriters (0),
|
||||
numWriters (0),
|
||||
writerThreadId (0)
|
||||
{
|
||||
readerThreads.ensureStorageAllocated (16);
|
||||
}
|
||||
|
||||
ReadWriteLock::~ReadWriteLock() noexcept
|
||||
{
|
||||
bassert (readerThreads.size() == 0);
|
||||
bassert (numWriters == 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ReadWriteLock::enterRead() const noexcept
|
||||
{
|
||||
while (! tryEnterRead())
|
||||
waitEvent.wait (100);
|
||||
}
|
||||
|
||||
bool ReadWriteLock::tryEnterRead() const noexcept
|
||||
{
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
|
||||
const SpinLock::ScopedLockType sl (accessLock);
|
||||
|
||||
for (int i = 0; i < readerThreads.size(); ++i)
|
||||
{
|
||||
ThreadRecursionCount& trc = readerThreads.getReference(i);
|
||||
|
||||
if (trc.threadID == threadId)
|
||||
{
|
||||
trc.count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (numWriters + numWaitingWriters == 0
|
||||
|| (threadId == writerThreadId && numWriters > 0))
|
||||
{
|
||||
ThreadRecursionCount trc = { threadId, 1 };
|
||||
readerThreads.add (trc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadWriteLock::exitRead() const noexcept
|
||||
{
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
const SpinLock::ScopedLockType sl (accessLock);
|
||||
|
||||
for (int i = 0; i < readerThreads.size(); ++i)
|
||||
{
|
||||
ThreadRecursionCount& trc = readerThreads.getReference(i);
|
||||
|
||||
if (trc.threadID == threadId)
|
||||
{
|
||||
if (--(trc.count) == 0)
|
||||
{
|
||||
readerThreads.remove (i);
|
||||
waitEvent.signal();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bassertfalse; // unlocking a lock that wasn't locked..
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ReadWriteLock::enterWrite() const noexcept
|
||||
{
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
const SpinLock::ScopedLockType sl (accessLock);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (readerThreads.size() + numWriters == 0
|
||||
|| threadId == writerThreadId
|
||||
|| (readerThreads.size() == 1
|
||||
&& readerThreads.getReference(0).threadID == threadId))
|
||||
{
|
||||
writerThreadId = threadId;
|
||||
++numWriters;
|
||||
break;
|
||||
}
|
||||
|
||||
++numWaitingWriters;
|
||||
accessLock.exit();
|
||||
waitEvent.wait (100);
|
||||
accessLock.enter();
|
||||
--numWaitingWriters;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadWriteLock::tryEnterWrite() const noexcept
|
||||
{
|
||||
const Thread::ThreadID threadId = Thread::getCurrentThreadId();
|
||||
const SpinLock::ScopedLockType sl (accessLock);
|
||||
|
||||
if (readerThreads.size() + numWriters == 0
|
||||
|| threadId == writerThreadId
|
||||
|| (readerThreads.size() == 1
|
||||
&& readerThreads.getReference(0).threadID == threadId))
|
||||
{
|
||||
writerThreadId = threadId;
|
||||
++numWriters;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadWriteLock::exitWrite() const noexcept
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (accessLock);
|
||||
|
||||
// check this thread actually had the lock..
|
||||
bassert (numWriters > 0 && writerThreadId == Thread::getCurrentThreadId());
|
||||
|
||||
if (--numWriters == 0)
|
||||
{
|
||||
writerThreadId = 0;
|
||||
waitEvent.signal();
|
||||
}
|
||||
}
|
||||
145
modules/beast_core/threads/beast_ReadWriteLock.h
Normal file
145
modules/beast_core/threads/beast_ReadWriteLock.h
Normal file
@@ -0,0 +1,145 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_READWRITELOCK_H_INCLUDED
|
||||
#define BEAST_READWRITELOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A critical section that allows multiple simultaneous readers.
|
||||
|
||||
Features of this type of lock are:
|
||||
|
||||
- Multiple readers can hold the lock at the same time, but only one writer
|
||||
can hold it at once.
|
||||
- Writers trying to gain the lock will be blocked until all readers and writers
|
||||
have released it
|
||||
- Readers trying to gain the lock while a writer is waiting to acquire it will be
|
||||
blocked until the writer has obtained and released it
|
||||
- If a thread already has a read lock and tries to obtain a write lock, it will succeed if
|
||||
there are no other readers
|
||||
- If a thread already has the write lock and tries to obtain a read lock, this will succeed.
|
||||
- Recursive locking is supported.
|
||||
|
||||
@see ScopedReadLock, ScopedWriteLock, CriticalSection
|
||||
*/
|
||||
class BEAST_API ReadWriteLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a ReadWriteLock object.
|
||||
*/
|
||||
ReadWriteLock() noexcept;
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If the object is deleted whilst locked, any subsequent behaviour
|
||||
is unpredictable.
|
||||
*/
|
||||
~ReadWriteLock() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Locks this object for reading.
|
||||
|
||||
Multiple threads can simulaneously lock the object for reading, but if another
|
||||
thread has it locked for writing, then this will block until it releases the
|
||||
lock.
|
||||
|
||||
@see exitRead, ScopedReadLock
|
||||
*/
|
||||
void enterRead() const noexcept;
|
||||
|
||||
/** Tries to lock this object for reading.
|
||||
|
||||
Multiple threads can simulaneously lock the object for reading, but if another
|
||||
thread has it locked for writing, then this will fail and return false.
|
||||
|
||||
@returns true if the lock is successfully gained.
|
||||
@see exitRead, ScopedReadLock
|
||||
*/
|
||||
bool tryEnterRead() const noexcept;
|
||||
|
||||
/** Releases the read-lock.
|
||||
|
||||
If the caller thread hasn't got the lock, this can have unpredictable results.
|
||||
|
||||
If the enterRead() method has been called multiple times by the thread, each
|
||||
call must be matched by a call to exitRead() before other threads will be allowed
|
||||
to take over the lock.
|
||||
|
||||
@see enterRead, ScopedReadLock
|
||||
*/
|
||||
void exitRead() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Locks this object for writing.
|
||||
|
||||
This will block until any other threads that have it locked for reading or
|
||||
writing have released their lock.
|
||||
|
||||
@see exitWrite, ScopedWriteLock
|
||||
*/
|
||||
void enterWrite() const noexcept;
|
||||
|
||||
/** Tries to lock this object for writing.
|
||||
|
||||
This is like enterWrite(), but doesn't block - it returns true if it manages
|
||||
to obtain the lock.
|
||||
|
||||
@returns true if the lock is successfully gained.
|
||||
@see enterWrite
|
||||
*/
|
||||
bool tryEnterWrite() const noexcept;
|
||||
|
||||
/** Releases the write-lock.
|
||||
|
||||
If the caller thread hasn't got the lock, this can have unpredictable results.
|
||||
|
||||
If the enterWrite() method has been called multiple times by the thread, each
|
||||
call must be matched by a call to exit() before other threads will be allowed
|
||||
to take over the lock.
|
||||
|
||||
@see enterWrite, ScopedWriteLock
|
||||
*/
|
||||
void exitWrite() const noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SpinLock accessLock;
|
||||
WaitableEvent waitEvent;
|
||||
mutable int numWaitingWriters, numWriters;
|
||||
mutable Thread::ThreadID writerThreadId;
|
||||
|
||||
struct ThreadRecursionCount
|
||||
{
|
||||
Thread::ThreadID threadID;
|
||||
int count;
|
||||
};
|
||||
|
||||
mutable Array <ThreadRecursionCount> readerThreads;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_READWRITELOCK_H_INCLUDED
|
||||
98
modules/beast_core/threads/beast_ReadWriteMutex.cpp
Normal file
98
modules/beast_core/threads/beast_ReadWriteMutex.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ReadWriteMutex::ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
ReadWriteMutex::~ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterRead () const noexcept
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// attempt the lock optimistically
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
m_readers->addref ();
|
||||
|
||||
// is there a writer?
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
if (m_writes->isSignaled ())
|
||||
{
|
||||
// a writer exists, give up the read lock
|
||||
m_readers->release ();
|
||||
|
||||
// block until the writer is done
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
}
|
||||
|
||||
// now try the loop again
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitRead () const noexcept
|
||||
{
|
||||
m_readers->release ();
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterWrite () const noexcept
|
||||
{
|
||||
// Optimistically acquire the write lock.
|
||||
m_writes->addref ();
|
||||
|
||||
// Go for the mutex.
|
||||
// Another writer might block us here.
|
||||
m_mutex.enter ();
|
||||
|
||||
// Only one competing writer will get here,
|
||||
// but we don't know who, so we have to drain
|
||||
// readers no matter what. New readers will be
|
||||
// blocked by the mutex.
|
||||
//
|
||||
if (m_readers->isSignaled ())
|
||||
{
|
||||
SpinDelay delay;
|
||||
|
||||
do
|
||||
{
|
||||
delay.pause ();
|
||||
}
|
||||
while (m_readers->isSignaled ());
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitWrite () const noexcept
|
||||
{
|
||||
// Releasing the mutex first and then decrementing the
|
||||
// writer count allows another waiting writer to atomically
|
||||
// acquire the lock, thus starving readers. This fulfills
|
||||
// the write-preferencing requirement.
|
||||
|
||||
m_mutex.exit ();
|
||||
|
||||
m_writes->release ();
|
||||
}
|
||||
150
modules/beast_core/threads/beast_ReadWriteMutex.h
Normal file
150
modules/beast_core/threads/beast_ReadWriteMutex.h
Normal file
@@ -0,0 +1,150 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_READWRITEMUTEX_H_INCLUDED
|
||||
#define BEAST_READWRITEMUTEX_H_INCLUDED
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Multiple consumer, single producer (MCSP) synchronization.
|
||||
|
||||
This is an optimized lock for the multiple reader, single writer
|
||||
scenario. It provides only a subset of features of the more general
|
||||
traditional read/write lock. Specifically, these rules apply:
|
||||
|
||||
- A caller cannot hold a read lock while acquiring a write lock.
|
||||
|
||||
- Write locks are only recursive with respect to write locks.
|
||||
|
||||
- Read locks are only recursive with respect to read locks.
|
||||
|
||||
- A write lock cannot be downgraded.
|
||||
|
||||
- Writes are preferenced over reads.
|
||||
|
||||
For real-time applications, these restrictions are often not an issue.
|
||||
|
||||
The implementation is wait-free in the fast path: acquiring read access
|
||||
for a lock without contention - just one interlocked increment!
|
||||
|
||||
@class ReadWriteMutex
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped read lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedReadLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedReadLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterRead ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedReadLock () noexcept
|
||||
{
|
||||
m_lock.exitRead ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped write lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedWriteLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedWriteLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterWrite ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedWriteLock () noexcept
|
||||
{
|
||||
m_lock.exitWrite ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class BEAST_API ReadWriteMutex
|
||||
{
|
||||
public:
|
||||
/** Provides the type of scoped read lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedReadLock <ReadWriteMutex> ScopedReadLockType;
|
||||
|
||||
/** Provides the type of scoped write lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedWriteLock <ReadWriteMutex> ScopedWriteLockType;
|
||||
|
||||
/** Create a ReadWriteMutex */
|
||||
ReadWriteMutex () noexcept;
|
||||
|
||||
/** Destroy a ReadWriteMutex
|
||||
|
||||
If the object is destroyed while a lock is held, the result is
|
||||
undefined behavior.
|
||||
*/
|
||||
~ReadWriteMutex () noexcept;
|
||||
|
||||
/** Acquire a read lock.
|
||||
|
||||
This is recursive with respect to other read locks. Calling this while
|
||||
holding a write lock is undefined.
|
||||
*/
|
||||
void enterRead () const noexcept;
|
||||
|
||||
/** Release a previously acquired read lock */
|
||||
void exitRead () const noexcept;
|
||||
|
||||
/** Acquire a write lock.
|
||||
|
||||
This is recursive with respect to other write locks. Calling this while
|
||||
holding a read lock is undefined.
|
||||
*/
|
||||
void enterWrite () const noexcept;
|
||||
|
||||
/** Release a previously acquired write lock */
|
||||
void exitWrite () const noexcept;
|
||||
|
||||
private:
|
||||
CriticalSection m_mutex;
|
||||
|
||||
mutable CacheLine::Padded <AtomicCounter> m_writes;
|
||||
mutable CacheLine::Padded <AtomicCounter> m_readers;
|
||||
};
|
||||
|
||||
#endif
|
||||
244
modules/beast_core/threads/beast_ScopedLock.h
Normal file
244
modules/beast_core/threads/beast_ScopedLock.h
Normal file
@@ -0,0 +1,244 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SCOPEDLOCK_H_INCLUDED
|
||||
#define BEAST_SCOPEDLOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a mutex object.
|
||||
|
||||
Use one of these as a local variable to provide RAII-based locking of a mutex.
|
||||
|
||||
The templated class could be a CriticalSection, SpinLock, or anything else that
|
||||
provides enter() and exit() methods.
|
||||
|
||||
e.g. @code
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
...do some stuff...
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see GenericScopedUnlock, CriticalSection, SpinLock, ScopedLock, ScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedLock.
|
||||
|
||||
As soon as it is created, this will acquire the lock, and when the GenericScopedLock
|
||||
object is deleted, the lock will be released.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedLock (const LockType& lock) noexcept
|
||||
: lock_ (lock)
|
||||
{
|
||||
lock.enter();
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
The lock will be released when the destructor is called.
|
||||
Make sure this object is created and deleted by the same thread, otherwise there are
|
||||
no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedLock() noexcept
|
||||
{
|
||||
lock_.exit();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically unlocks and re-locks a mutex object.
|
||||
|
||||
This is the reverse of a GenericScopedLock object - instead of locking the mutex
|
||||
for the lifetime of this object, it unlocks it.
|
||||
|
||||
Make sure you don't try to unlock mutexes that aren't actually locked!
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
... do some stuff with it locked ..
|
||||
|
||||
while (xyz)
|
||||
{
|
||||
... do some stuff with it locked ..
|
||||
|
||||
const GenericScopedUnlock<CriticalSection> unlocker (myCriticalSection);
|
||||
|
||||
// myCriticalSection is now unlocked for the remainder of this block,
|
||||
// and re-locked at the end.
|
||||
|
||||
...do some stuff with it unlocked ...
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see GenericScopedLock, CriticalSection, ScopedLock, ScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedUnlock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedUnlock.
|
||||
|
||||
As soon as it is created, this will unlock the CriticalSection, and
|
||||
when the ScopedLock object is deleted, the CriticalSection will
|
||||
be re-locked.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedUnlock (const LockType& lock) noexcept
|
||||
: lock_ (lock)
|
||||
{
|
||||
lock.exit();
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The CriticalSection will be unlocked when the destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedUnlock() noexcept
|
||||
{
|
||||
lock_.enter();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a mutex object.
|
||||
|
||||
Use one of these as a local variable to provide RAII-based locking of a mutex.
|
||||
|
||||
The templated class could be a CriticalSection, SpinLock, or anything else that
|
||||
provides enter() and exit() methods.
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedTryLock<CriticalSection> myScopedTryLock (myCriticalSection);
|
||||
|
||||
// Unlike using a ScopedLock, this may fail to actually get the lock, so you
|
||||
// should test this with the isLocked() method before doing your thread-unsafe
|
||||
// action..
|
||||
if (myScopedTryLock.isLocked())
|
||||
{
|
||||
...do some stuff...
|
||||
}
|
||||
else
|
||||
{
|
||||
..our attempt at locking failed because another thread had already locked it..
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here (if it was locked)
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see CriticalSection::tryEnter, GenericScopedLock, GenericScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedTryLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedTryLock.
|
||||
|
||||
As soon as it is created, this will attempt to acquire the lock, and when the
|
||||
GenericScopedTryLock is deleted, the lock will be released (if the lock was
|
||||
successfully acquired).
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedTryLock (const LockType& lock) noexcept
|
||||
: lock_ (lock), lockWasSuccessful (lock.tryEnter()) {}
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The mutex will be unlocked (if it had been successfully locked) when the
|
||||
destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedTryLock() noexcept
|
||||
{
|
||||
if (lockWasSuccessful)
|
||||
lock_.exit();
|
||||
}
|
||||
|
||||
/** Returns true if the mutex was successfully locked. */
|
||||
bool isLocked() const noexcept
|
||||
{
|
||||
return lockWasSuccessful;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
const bool lockWasSuccessful;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
82
modules/beast_core/threads/beast_ScopedReadLock.h
Normal file
82
modules/beast_core/threads/beast_ScopedReadLock.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SCOPEDREADLOCK_H_INCLUDED
|
||||
#define BEAST_SCOPEDREADLOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a ReadWriteLock object.
|
||||
|
||||
Use one of these as a local variable to control access to a ReadWriteLock.
|
||||
|
||||
e.g. @code
|
||||
|
||||
ReadWriteLock myLock;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const ScopedReadLock myScopedLock (myLock);
|
||||
// myLock is now locked
|
||||
|
||||
...do some stuff...
|
||||
|
||||
// myLock gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see ReadWriteLock, ScopedWriteLock
|
||||
*/
|
||||
class BEAST_API ScopedReadLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ScopedReadLock.
|
||||
|
||||
As soon as it is created, this will call ReadWriteLock::enterRead(), and
|
||||
when the ScopedReadLock object is deleted, the ReadWriteLock will
|
||||
be unlocked.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit ScopedReadLock (const ReadWriteLock& lock) noexcept : lock_ (lock) { lock.enterRead(); }
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The ReadWriteLock's exitRead() method will be called when the destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~ScopedReadLock() noexcept { lock_.exitRead(); }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const ReadWriteLock& lock_;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_SCOPEDREADLOCK_H_INCLUDED
|
||||
82
modules/beast_core/threads/beast_ScopedWriteLock.h
Normal file
82
modules/beast_core/threads/beast_ScopedWriteLock.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SCOPEDWRITELOCK_H_INCLUDED
|
||||
#define BEAST_SCOPEDWRITELOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a ReadWriteLock object.
|
||||
|
||||
Use one of these as a local variable to control access to a ReadWriteLock.
|
||||
|
||||
e.g. @code
|
||||
|
||||
ReadWriteLock myLock;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const ScopedWriteLock myScopedLock (myLock);
|
||||
// myLock is now locked
|
||||
|
||||
...do some stuff...
|
||||
|
||||
// myLock gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see ReadWriteLock, ScopedReadLock
|
||||
*/
|
||||
class BEAST_API ScopedWriteLock : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ScopedWriteLock.
|
||||
|
||||
As soon as it is created, this will call ReadWriteLock::enterWrite(), and
|
||||
when the ScopedWriteLock object is deleted, the ReadWriteLock will
|
||||
be unlocked.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit ScopedWriteLock (const ReadWriteLock& lock) noexcept : lock_ (lock) { lock.enterWrite(); }
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The ReadWriteLock's exitWrite() method will be called when the destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~ScopedWriteLock() noexcept { lock_.exitWrite(); }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const ReadWriteLock& lock_;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_SCOPEDWRITELOCK_H_INCLUDED
|
||||
295
modules/beast_core/threads/beast_SharedData.h
Normal file
295
modules/beast_core/threads/beast_SharedData.h
Normal file
@@ -0,0 +1,295 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SHAREDDATA_H_INCLUDED
|
||||
#define BEAST_SHAREDDATA_H_INCLUDED
|
||||
|
||||
/** Structured access to a shared state.
|
||||
|
||||
This template wraps an object containing members representing state
|
||||
information shared between multiple threads of execution, where any thread
|
||||
may need to read or write as needed. Synchronized access to the concurrent
|
||||
state is enforced at compile time through strongly typed accessor classes.
|
||||
This interface design facilitates source code pattern matching to find all
|
||||
areas where a concurrent state is accessed.
|
||||
|
||||
There are three types of access:
|
||||
|
||||
- ReadAccess
|
||||
|
||||
Allows read access to the underlying object as `const`. ReadAccess may
|
||||
be granted to one or more threads simultaneously. If one or more
|
||||
threads have ReadAccess, requests to obtain WriteAccess are blocked.
|
||||
|
||||
- WriteAccess
|
||||
|
||||
Allows exclusive read/write access the underlying object. A WriteAccess
|
||||
request blocks until all existing ReadAccess and WriteAccess requests
|
||||
are released. While a WriteAccess exists, requests for ReadAccess
|
||||
will block.
|
||||
|
||||
- UnlockedAccess
|
||||
|
||||
Allows read access to the underlying object without using the lock.
|
||||
This can be helpful when designing concurrent structures through
|
||||
composition. It also makes it easier to search for places in code
|
||||
which use unlocked access.
|
||||
|
||||
This code example demonstrates various forms of access to a SharedData:
|
||||
|
||||
@code
|
||||
|
||||
struct SharedData
|
||||
{
|
||||
int value1;
|
||||
String value2;
|
||||
};
|
||||
|
||||
typedef SharedData <SharedData> SharedState;
|
||||
|
||||
SharedState sharedState;
|
||||
|
||||
void readExample ()
|
||||
{
|
||||
SharedState::ReadAccess state (sharedState);
|
||||
|
||||
print (state->value1); // read access
|
||||
print (state->value2); // read access
|
||||
|
||||
state->value1 = 42; // write disallowed: compile error
|
||||
}
|
||||
|
||||
void writeExample ()
|
||||
{
|
||||
SharedState::WriteAccess state (sharedState);
|
||||
|
||||
state->value2 = "Label"; // write access
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
Forwarding constructors with up to eight parameters are provided. This lets
|
||||
you write constructors into the underlying data object. For example:
|
||||
|
||||
@code
|
||||
|
||||
struct SharedData
|
||||
{
|
||||
explicit SharedData (int numSlots)
|
||||
{
|
||||
m_array.reserve (numSlots);
|
||||
}
|
||||
|
||||
std::vector <AudioSampleBuffer*> m_array;
|
||||
};
|
||||
|
||||
// Construct SharedData with one parameter
|
||||
SharedData <SharedData> sharedState (16);
|
||||
|
||||
@endcode
|
||||
|
||||
@param Object The type of object to encapsulate.
|
||||
|
||||
@warning Recursive calls are not supported. It is generally not possible for
|
||||
a thread of execution to acquire write access while it already has
|
||||
read access. Such an attempt will result in undefined behavior.
|
||||
Calling into unknown code while holding a lock can cause deadlock.
|
||||
See @ref CallQueue::queue().
|
||||
*/
|
||||
template <class Object>
|
||||
class SharedData : public Uncopyable
|
||||
{
|
||||
public:
|
||||
class ReadAccess;
|
||||
class WriteAccess;
|
||||
class UnlockedAccess;
|
||||
|
||||
/** Create a concurrent state.
|
||||
|
||||
Up to 8 parameters can be specified in the constructor. These parameters
|
||||
are forwarded to the corresponding constructor in Object. If no
|
||||
constructor in Object matches the parameter list, a compile error is
|
||||
generated.
|
||||
*/
|
||||
/** @{ */
|
||||
SharedData () { }
|
||||
|
||||
template <class T1>
|
||||
explicit SharedData (T1 t1)
|
||||
: m_obj (t1) { }
|
||||
|
||||
template <class T1, class T2>
|
||||
SharedData (T1 t1, T2 t2)
|
||||
: m_obj (t1, t2) { }
|
||||
|
||||
template <class T1, class T2, class T3>
|
||||
SharedData (T1 t1, T2 t2, T3 t3)
|
||||
: m_obj (t1, t2, t3) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
: m_obj (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_obj (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_obj (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_obj (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_obj (t1, t2, t3, t4, t5, t6, t7, t8) { }
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
typedef ReadWriteMutex ReadWriteMutexType;
|
||||
|
||||
Object m_obj;
|
||||
ReadWriteMutexType m_mutex;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Unlocked access to a SharedData.
|
||||
|
||||
Use sparingly.
|
||||
*/
|
||||
template <class Object>
|
||||
class SharedData <Object>::UnlockedAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit UnlockedAccess (SharedData const& state)
|
||||
: m_state (state)
|
||||
{
|
||||
}
|
||||
|
||||
Object const& getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read only access to a SharedData */
|
||||
template <class Object>
|
||||
class SharedData <Object>::ReadAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create a ReadAccess from the specified SharedData */
|
||||
explicit ReadAccess (SharedData const volatile& state)
|
||||
: m_state (const_cast <SharedData const&> (state))
|
||||
, m_lock (m_state.m_mutex)
|
||||
{
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read only smart pointer to Object */
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
ReadWriteMutexType::ScopedReadLockType m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read/write access to a SharedData */
|
||||
template <class Object>
|
||||
class SharedData <Object>::WriteAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit WriteAccess (SharedData& state)
|
||||
: m_state (state)
|
||||
, m_lock (m_state.m_mutex)
|
||||
{
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const* getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read only smart pointer to Object */
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read/write pointer to Object */
|
||||
Object& getObject ()
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read/write reference to Object */
|
||||
Object& operator* ()
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read/write smart pointer to Object */
|
||||
Object* operator-> ()
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData& m_state;
|
||||
ReadWriteMutexType::ScopedWriteLockType m_lock;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
modules/beast_core/threads/beast_SpinDelay.cpp
Normal file
29
modules/beast_core/threads/beast_SpinDelay.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SpinDelay::SpinDelay ()
|
||||
: m_count (0)
|
||||
{
|
||||
}
|
||||
|
||||
void SpinDelay::pause ()
|
||||
{
|
||||
if (++m_count > 20)
|
||||
Thread::yield ();
|
||||
}
|
||||
36
modules/beast_core/threads/beast_SpinDelay.h
Normal file
36
modules/beast_core/threads/beast_SpinDelay.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SPINDELAY_H_INCLUDED
|
||||
#define BEAST_SPINDELAY_H_INCLUDED
|
||||
|
||||
/** A simple delay used to synchronize threads.
|
||||
*/
|
||||
class BEAST_API SpinDelay
|
||||
{
|
||||
public:
|
||||
SpinDelay ();
|
||||
|
||||
void pause ();
|
||||
|
||||
private:
|
||||
int m_count;
|
||||
};
|
||||
|
||||
#endif
|
||||
83
modules/beast_core/threads/beast_SpinLock.h
Normal file
83
modules/beast_core/threads/beast_SpinLock.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_SPINLOCK_H_INCLUDED
|
||||
#define BEAST_SPINLOCK_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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:
|
||||
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 lock.compareAndSetBool (1, 0);
|
||||
}
|
||||
|
||||
/** Releases the lock. */
|
||||
inline void exit() const noexcept
|
||||
{
|
||||
bassert (lock.value == 1); // Agh! Releasing a lock that isn't currently held!
|
||||
lock = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Provides the type of scoped lock to use for locking a SpinLock. */
|
||||
typedef GenericScopedLock <SpinLock> ScopedLockType;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a SpinLock. */
|
||||
typedef GenericScopedUnlock <SpinLock> ScopedUnlockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
mutable Atomic<int> lock;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_SPINLOCK_H_INCLUDED
|
||||
365
modules/beast_core/threads/beast_Thread.cpp
Normal file
365
modules/beast_core/threads/beast_Thread.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Thread::Thread (const String& threadName_)
|
||||
: threadName (threadName_),
|
||||
threadHandle (nullptr),
|
||||
threadId (0),
|
||||
threadPriority (5),
|
||||
affinityMask (0),
|
||||
shouldExit (false)
|
||||
{
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
/* If your thread class's destructor has been called without first stopping the thread, that
|
||||
means that this partially destructed object is still performing some work - and that's
|
||||
probably a Bad Thing!
|
||||
|
||||
To avoid this type of nastiness, always make sure you call stopThread() before or during
|
||||
your subclass's destructor.
|
||||
*/
|
||||
check_precondition (! isThreadRunning());
|
||||
|
||||
stopThread ();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Use a ref-counted object to hold this shared data, so that it can outlive its static
|
||||
// shared pointer when threads are still running during static shutdown.
|
||||
struct CurrentThreadHolder : public SharedObject
|
||||
{
|
||||
CurrentThreadHolder() noexcept {}
|
||||
|
||||
typedef SharedPtr <CurrentThreadHolder> Ptr;
|
||||
ThreadLocalValue<Thread*> value;
|
||||
};
|
||||
|
||||
static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros).
|
||||
|
||||
static SpinLock* castToSpinLockWithoutAliasingWarning (void* s)
|
||||
{
|
||||
return static_cast<SpinLock*> (s);
|
||||
}
|
||||
|
||||
static CurrentThreadHolder::Ptr getCurrentThreadHolder()
|
||||
{
|
||||
static CurrentThreadHolder::Ptr currentThreadHolder;
|
||||
SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock));
|
||||
|
||||
if (currentThreadHolder == nullptr)
|
||||
currentThreadHolder = new CurrentThreadHolder();
|
||||
|
||||
return currentThreadHolder;
|
||||
}
|
||||
|
||||
void Thread::threadEntryPoint()
|
||||
{
|
||||
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
||||
currentThreadHolder->value = this;
|
||||
|
||||
if (threadName.isNotEmpty())
|
||||
setCurrentThreadName (threadName);
|
||||
|
||||
if (startSuspensionEvent.wait (10000))
|
||||
{
|
||||
bassert (getCurrentThreadId() == threadId);
|
||||
|
||||
if (affinityMask != 0)
|
||||
setCurrentThreadAffinityMask (affinityMask);
|
||||
|
||||
run();
|
||||
}
|
||||
|
||||
currentThreadHolder->value.releaseCurrentThreadStorage();
|
||||
closeThreadHandle();
|
||||
}
|
||||
|
||||
// used to wrap the incoming call from the platform-specific code
|
||||
void BEAST_API beast_threadEntryPoint (void* userData)
|
||||
{
|
||||
ProtectedCall (&Thread::threadEntryPoint, static_cast <Thread*> (userData));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::startThread()
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
launchThread();
|
||||
setThreadPriority (threadHandle, threadPriority);
|
||||
startSuspensionEvent.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::startThread (const int priority)
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
threadPriority = priority;
|
||||
startThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPriority (priority);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::isThreadRunning() const
|
||||
{
|
||||
return threadHandle != nullptr;
|
||||
}
|
||||
|
||||
Thread* Thread::getCurrentThread()
|
||||
{
|
||||
return getCurrentThreadHolder()->value.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::signalThreadShouldExit()
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
|
||||
{
|
||||
// Doh! So how exactly do you expect this thread to wait for itself to stop??
|
||||
bassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0);
|
||||
|
||||
const uint32 timeoutEnd = Time::getMillisecondCounter() + (uint32) timeOutMilliseconds;
|
||||
|
||||
while (isThreadRunning())
|
||||
{
|
||||
if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd)
|
||||
return false;
|
||||
|
||||
sleep (2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thread::stopThread (const int timeOutMilliseconds)
|
||||
{
|
||||
bool cleanExit = true;
|
||||
|
||||
// agh! You can't stop the thread that's calling this method! How on earth
|
||||
// would that work??
|
||||
bassert (getCurrentThreadId() != getThreadId());
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
notify();
|
||||
|
||||
if (timeOutMilliseconds != 0)
|
||||
{
|
||||
cleanExit = waitForThreadToExit (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
bassert (! cleanExit);
|
||||
|
||||
// very bad karma if this point is reached, as there are bound to be
|
||||
// locks and events left in silly states when a thread is killed by force..
|
||||
killThread();
|
||||
|
||||
threadHandle = nullptr;
|
||||
threadId = 0;
|
||||
|
||||
cleanExit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanExit = true;
|
||||
}
|
||||
}
|
||||
|
||||
return cleanExit;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::setPriority (const int newPriority)
|
||||
{
|
||||
// NB: deadlock possible if you try to set the thread prio from the thread itself,
|
||||
// so using setCurrentThreadPriority instead in that case.
|
||||
if (getCurrentThreadId() == getThreadId())
|
||||
return setCurrentThreadPriority (newPriority);
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (setThreadPriority (threadHandle, newPriority))
|
||||
{
|
||||
threadPriority = newPriority;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thread::setCurrentThreadPriority (const int newPriority)
|
||||
{
|
||||
return setThreadPriority (0, newPriority);
|
||||
}
|
||||
|
||||
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||
{
|
||||
affinityMask = newAffinityMask;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::wait (const int timeOutMilliseconds) const
|
||||
{
|
||||
return defaultEvent.wait (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
void Thread::notify() const
|
||||
{
|
||||
defaultEvent.signal();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SpinLock::enter() const noexcept
|
||||
{
|
||||
if (! tryEnter())
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
if (tryEnter())
|
||||
return;
|
||||
|
||||
while (! tryEnter())
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class AtomicTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AtomicTests() : UnitTest ("Atomic", "beast") {}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("Misc");
|
||||
|
||||
char a1[7];
|
||||
expect (numElementsInArray(a1) == 7);
|
||||
int a2[3];
|
||||
expect (numElementsInArray(a2) == 3);
|
||||
|
||||
expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
|
||||
expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
|
||||
expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211));
|
||||
|
||||
beginTestCase ("int");
|
||||
AtomicTester <int>::testInteger (*this);
|
||||
beginTestCase ("unsigned int");
|
||||
AtomicTester <unsigned int>::testInteger (*this);
|
||||
beginTestCase ("int32");
|
||||
AtomicTester <int32>::testInteger (*this);
|
||||
beginTestCase ("uint32");
|
||||
AtomicTester <uint32>::testInteger (*this);
|
||||
beginTestCase ("long");
|
||||
AtomicTester <long>::testInteger (*this);
|
||||
beginTestCase ("void*");
|
||||
AtomicTester <void*>::testInteger (*this);
|
||||
beginTestCase ("int*");
|
||||
AtomicTester <int*>::testInteger (*this);
|
||||
beginTestCase ("float");
|
||||
AtomicTester <float>::testFloat (*this);
|
||||
#if ! BEAST_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms
|
||||
beginTestCase ("int64");
|
||||
AtomicTester <int64>::testInteger (*this);
|
||||
beginTestCase ("uint64");
|
||||
AtomicTester <uint64>::testInteger (*this);
|
||||
beginTestCase ("double");
|
||||
AtomicTester <double>::testFloat (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
class AtomicTester
|
||||
{
|
||||
public:
|
||||
AtomicTester() {}
|
||||
|
||||
static void testInteger (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a.set ((Type) 10);
|
||||
test.expect (a.value == (Type) 10);
|
||||
test.expect (a.get() == (Type) 10);
|
||||
a += (Type) 15;
|
||||
test.expect (a.get() == (Type) 25);
|
||||
a.memoryBarrier();
|
||||
a -= (Type) 5;
|
||||
test.expect (a.get() == (Type) 20);
|
||||
test.expect (++a == (Type) 21);
|
||||
++a;
|
||||
test.expect (--a == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
a.memoryBarrier();
|
||||
|
||||
testFloat (test);
|
||||
}
|
||||
|
||||
static void testFloat (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a = (Type) 21;
|
||||
a.memoryBarrier();
|
||||
|
||||
/* These are some simple test cases to check the atomics - let me know
|
||||
if any of these assertions fail on your system!
|
||||
*/
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (a.compareAndSetBool ((Type) 200, a.get()));
|
||||
test.expect (a.get() == (Type) 200);
|
||||
|
||||
test.expect (a.exchange ((Type) 300) == (Type) 200);
|
||||
test.expect (a.get() == (Type) 300);
|
||||
|
||||
b = a;
|
||||
test.expect (b.get() == a.get());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static AtomicTests atomicTests;
|
||||
280
modules/beast_core/threads/beast_Thread.h
Normal file
280
modules/beast_core/threads/beast_Thread.h
Normal file
@@ -0,0 +1,280 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREAD_H_INCLUDED
|
||||
#define BEAST_THREAD_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates a thread.
|
||||
|
||||
Subclasses derive from Thread and implement the run() method, in which they
|
||||
do their business. The thread can then be started with the startThread() method
|
||||
and controlled with various other methods.
|
||||
|
||||
This class also contains some thread-related static methods, such
|
||||
as sleep(), yield(), getCurrentThreadId() etc.
|
||||
|
||||
@see CriticalSection, WaitableEvent, Process, ThreadWithProgressWindow,
|
||||
MessageManagerLock
|
||||
*/
|
||||
class BEAST_API Thread : LeakChecked <Thread>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a thread.
|
||||
|
||||
When first created, the thread is not running. Use the startThread()
|
||||
method to start it.
|
||||
*/
|
||||
explicit Thread (const String& threadName);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If the thread has not been stopped first, this will generate a fatal error.
|
||||
*/
|
||||
virtual ~Thread();
|
||||
|
||||
//==============================================================================
|
||||
/** Must be implemented to perform the thread's actual code.
|
||||
|
||||
Remember that the thread must regularly check the threadShouldExit()
|
||||
method whilst running, and if this returns true it should return from
|
||||
the run() method as soon as possible to avoid being forcibly killed.
|
||||
|
||||
@see threadShouldExit, startThread
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
//==============================================================================
|
||||
// Thread control functions..
|
||||
|
||||
/** Starts the thread running.
|
||||
|
||||
This will start the thread's run() method.
|
||||
(if it's already started, startThread() won't do anything).
|
||||
|
||||
@see stopThread
|
||||
*/
|
||||
void startThread();
|
||||
|
||||
/** Starts the thread with a given priority.
|
||||
|
||||
Launches the thread with a given priority, where 0 = lowest, 10 = highest.
|
||||
If the thread is already running, its priority will be changed.
|
||||
|
||||
@see startThread, setPriority
|
||||
*/
|
||||
void startThread (int priority);
|
||||
|
||||
/** Attempts to stop the thread running.
|
||||
|
||||
This method will cause the threadShouldExit() method to return true
|
||||
and call notify() in case the thread is currently waiting.
|
||||
|
||||
Hopefully the thread will then respond to this by exiting cleanly, and
|
||||
the stopThread method will wait for a given time-period for this to
|
||||
happen.
|
||||
|
||||
If the thread is stuck and fails to respond after the time-out, it gets
|
||||
forcibly killed, which is a very bad thing to happen, as it could still
|
||||
be holding locks, etc. which are needed by other parts of your program.
|
||||
|
||||
@param timeOutMilliseconds The number of milliseconds to wait for the
|
||||
thread to finish before killing it by force. A negative
|
||||
value in here will wait forever.
|
||||
@see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning
|
||||
|
||||
@returns true if the thread exits, or false if the timeout expires first.
|
||||
*/
|
||||
bool stopThread (int timeOutMilliseconds = -1);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the thread is currently active */
|
||||
bool isThreadRunning() const;
|
||||
|
||||
/** Sets a flag to tell the thread it should stop.
|
||||
|
||||
Calling this means that the threadShouldExit() method will then return true.
|
||||
The thread should be regularly checking this to see whether it should exit.
|
||||
|
||||
If your thread makes use of wait(), you might want to call notify() after calling
|
||||
this method, to interrupt any waits that might be in progress, and allow it
|
||||
to reach a point where it can exit.
|
||||
|
||||
@see threadShouldExit
|
||||
@see waitForThreadToExit
|
||||
*/
|
||||
void signalThreadShouldExit();
|
||||
|
||||
/** Checks whether the thread has been told to stop running.
|
||||
|
||||
Threads need to check this regularly, and if it returns true, they should
|
||||
return from their run() method at the first possible opportunity.
|
||||
|
||||
@see signalThreadShouldExit
|
||||
*/
|
||||
inline bool threadShouldExit() const { return shouldExit; }
|
||||
|
||||
/** Waits for the thread to stop.
|
||||
|
||||
This will waits until isThreadRunning() is false or until a timeout expires.
|
||||
|
||||
@param timeOutMilliseconds the time to wait, in milliseconds. If this value
|
||||
is less than zero, it will wait forever.
|
||||
@returns true if the thread exits, or false if the timeout expires first.
|
||||
*/
|
||||
bool waitForThreadToExit (int timeOutMilliseconds = -1) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the thread's priority.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
|
||||
@param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority
|
||||
of 5 is normal.
|
||||
*/
|
||||
bool setPriority (int priority);
|
||||
|
||||
/** Changes the priority of the caller thread.
|
||||
|
||||
Similar to setPriority(), but this static method acts on the caller thread.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
|
||||
@see setPriority
|
||||
*/
|
||||
static bool setCurrentThreadPriority (int priority);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the affinity mask for the thread.
|
||||
|
||||
This will only have an effect next time the thread is started - i.e. if the
|
||||
thread is already running when called, it'll have no effect.
|
||||
|
||||
@see setCurrentThreadAffinityMask
|
||||
*/
|
||||
void setAffinityMask (uint32 affinityMask);
|
||||
|
||||
/** Changes the affinity mask for the caller thread.
|
||||
|
||||
This will change the affinity mask for the thread that calls this static method.
|
||||
|
||||
@see setAffinityMask
|
||||
*/
|
||||
static void setCurrentThreadAffinityMask (uint32 affinityMask);
|
||||
|
||||
//==============================================================================
|
||||
// this can be called from any thread that needs to pause..
|
||||
static void BEAST_CALLTYPE sleep (int milliseconds);
|
||||
|
||||
/** Yields the calling thread's current time-slot. */
|
||||
static void BEAST_CALLTYPE yield();
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the thread wait for a notification.
|
||||
|
||||
This puts the thread to sleep until either the timeout period expires, or
|
||||
another thread calls the notify() method to wake it up.
|
||||
|
||||
A negative time-out value means that the method will wait indefinitely.
|
||||
|
||||
@returns true if the event has been signalled, false if the timeout expires.
|
||||
*/
|
||||
bool wait (int timeOutMilliseconds = -1) const;
|
||||
|
||||
/** Wakes up the thread.
|
||||
|
||||
If the thread has called the wait() method, this will wake it up.
|
||||
|
||||
@see wait
|
||||
*/
|
||||
void notify() const;
|
||||
|
||||
//==============================================================================
|
||||
/** A value type used for thread IDs.
|
||||
@see getCurrentThreadId(), getThreadId()
|
||||
*/
|
||||
typedef void* ThreadID;
|
||||
|
||||
/** Returns an id that identifies the caller thread.
|
||||
|
||||
To find the ID of a particular thread object, use getThreadId().
|
||||
|
||||
@returns a unique identifier that identifies the calling thread.
|
||||
@see getThreadId
|
||||
*/
|
||||
static ThreadID getCurrentThreadId();
|
||||
|
||||
/** Finds the thread object that is currently running.
|
||||
|
||||
Note that the main UI thread (or other non-Beast threads) don't have a Thread
|
||||
object associated with them, so this will return 0.
|
||||
*/
|
||||
static Thread* getCurrentThread();
|
||||
|
||||
/** Returns the ID of this thread.
|
||||
|
||||
That means the ID of this thread object - not of the thread that's calling the method.
|
||||
|
||||
This can change when the thread is started and stopped, and will be invalid if the
|
||||
thread's not actually running.
|
||||
|
||||
@see getCurrentThreadId
|
||||
*/
|
||||
ThreadID getThreadId() const noexcept { return threadId; }
|
||||
|
||||
/** Returns the name of the thread.
|
||||
|
||||
This is the name that gets set in the constructor.
|
||||
*/
|
||||
const String& getThreadName() const { return threadName; }
|
||||
|
||||
/** Changes the name of the caller thread.
|
||||
Different OSes may place different length or content limits on this name.
|
||||
*/
|
||||
static void setCurrentThreadName (const String& newThreadName);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const String threadName;
|
||||
void* volatile threadHandle;
|
||||
ThreadID threadId;
|
||||
CriticalSection startStopLock;
|
||||
WaitableEvent startSuspensionEvent, defaultEvent;
|
||||
int threadPriority;
|
||||
uint32 affinityMask;
|
||||
bool volatile shouldExit;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
friend void BEAST_API beast_threadEntryPoint (void*);
|
||||
#endif
|
||||
|
||||
void launchThread();
|
||||
void closeThreadHandle();
|
||||
void killThread();
|
||||
void threadEntryPoint();
|
||||
static bool setThreadPriority (void*, int);
|
||||
};
|
||||
|
||||
#endif // BEAST_THREAD_H_INCLUDED
|
||||
190
modules/beast_core/threads/beast_ThreadLocalValue.h
Normal file
190
modules/beast_core/threads/beast_ThreadLocalValue.h
Normal file
@@ -0,0 +1,190 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_THREADLOCALVALUE_H_INCLUDED
|
||||
#define BEAST_THREADLOCALVALUE_H_INCLUDED
|
||||
|
||||
// (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 // BEAST_THREADLOCALVALUE_H_INCLUDED
|
||||
371
modules/beast_core/threads/beast_ThreadPool.cpp
Normal file
371
modules/beast_core/threads/beast_ThreadPool.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ThreadPoolJob::ThreadPoolJob (const String& name)
|
||||
: jobName (name),
|
||||
pool (nullptr),
|
||||
shouldStop (false),
|
||||
isActive (false),
|
||||
shouldBeDeleted (false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadPoolJob::~ThreadPoolJob()
|
||||
{
|
||||
// you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
|
||||
// to remove it first!
|
||||
bassert (pool == nullptr || ! pool->contains (this));
|
||||
}
|
||||
|
||||
String ThreadPoolJob::getJobName() const
|
||||
{
|
||||
return jobName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::setJobName (const String& newName)
|
||||
{
|
||||
jobName = newName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::signalJobShouldExit()
|
||||
{
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ThreadPool::ThreadPoolThread
|
||||
: public Thread
|
||||
, LeakChecked <ThreadPoolThread>
|
||||
{
|
||||
public:
|
||||
ThreadPoolThread (ThreadPool& pool_)
|
||||
: Thread ("Pool"),
|
||||
pool (pool_)
|
||||
{
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (! pool.runNextJob())
|
||||
wait (500);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadPool& pool;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool::ThreadPool (const int numThreads)
|
||||
{
|
||||
bassert (numThreads > 0); // not much point having a pool without any threads!
|
||||
|
||||
createThreads (numThreads);
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool()
|
||||
{
|
||||
createThreads (SystemStats::getNumCpus());
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
removeAllJobs (true, 5000);
|
||||
stopThreads();
|
||||
}
|
||||
|
||||
void ThreadPool::createThreads (int numThreads)
|
||||
{
|
||||
for (int i = bmax (1, numThreads); --i >= 0;)
|
||||
threads.add (new ThreadPoolThread (*this));
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->startThread();
|
||||
}
|
||||
|
||||
void ThreadPool::stopThreads()
|
||||
{
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->signalThreadShouldExit();
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->stopThread (500);
|
||||
}
|
||||
|
||||
void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinished)
|
||||
{
|
||||
bassert (job != nullptr);
|
||||
bassert (job->pool == nullptr);
|
||||
|
||||
if (job->pool == nullptr)
|
||||
{
|
||||
job->pool = this;
|
||||
job->shouldStop = false;
|
||||
job->isActive = false;
|
||||
job->shouldBeDeleted = deleteJobWhenFinished;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
jobs.add (job);
|
||||
}
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->notify();
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadPool::getNumJobs() const
|
||||
{
|
||||
return jobs.size();
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::getJob (const int index) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs [index];
|
||||
}
|
||||
|
||||
bool ThreadPool::contains (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job));
|
||||
}
|
||||
|
||||
bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job)) && job->isActive;
|
||||
}
|
||||
|
||||
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
|
||||
const int timeOutMs) const
|
||||
{
|
||||
if (job != nullptr)
|
||||
{
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
while (contains (job))
|
||||
{
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (2);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThreadPool::removeJob (ThreadPoolJob* const job,
|
||||
const bool interruptIfRunning,
|
||||
const int timeOutMs)
|
||||
{
|
||||
bool dontWait = true;
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
if (job != nullptr)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
if (interruptIfRunning)
|
||||
job->signalJobShouldExit();
|
||||
|
||||
dontWait = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dontWait || waitForJobToFinish (job, timeOutMs);
|
||||
}
|
||||
|
||||
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs,
|
||||
ThreadPool::JobSelector* selectedJobsToRemove)
|
||||
{
|
||||
Array <ThreadPoolJob*> jobsToWaitFor;
|
||||
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = jobs.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
|
||||
if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
jobsToWaitFor.add (job);
|
||||
|
||||
if (interruptRunningJobs)
|
||||
job->signalJobShouldExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (int i = jobsToWaitFor.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobsToWaitFor.getUnchecked (i);
|
||||
|
||||
if (! isJobRunning (job))
|
||||
jobsToWaitFor.remove (i);
|
||||
}
|
||||
|
||||
if (jobsToWaitFor.size() == 0)
|
||||
break;
|
||||
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const
|
||||
{
|
||||
StringArray s;
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
const ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
if (job->isActive || ! onlyReturnActiveJobs)
|
||||
s.add (job->getJobName());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ThreadPool::setThreadPriorities (const int newPriority)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
if (! threads.getUnchecked(i)->setPriority (newPriority))
|
||||
ok = false;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::pickNextJobToRun()
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
ThreadPoolJob* job = jobs[i];
|
||||
|
||||
if (job != nullptr && ! job->isActive)
|
||||
{
|
||||
if (job->shouldStop)
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
job->isActive = true;
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ThreadPool::runNextJob()
|
||||
{
|
||||
ThreadPoolJob* const job = pickNextJobToRun();
|
||||
|
||||
if (job == nullptr)
|
||||
return false;
|
||||
|
||||
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
|
||||
|
||||
result = job->runJob();
|
||||
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
job->isActive = false;
|
||||
|
||||
if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
|
||||
jobFinishedSignal.signal();
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the job to the end of the queue if it wants another go
|
||||
jobs.move (jobs.indexOf (job), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadPool::addToDeleteList (OwnedArray<ThreadPoolJob>& deletionList, ThreadPoolJob* const job) const
|
||||
{
|
||||
job->shouldStop = true;
|
||||
job->pool = nullptr;
|
||||
|
||||
if (job->shouldBeDeleted)
|
||||
deletionList.add (job);
|
||||
}
|
||||
304
modules/beast_core/threads/beast_ThreadPool.h
Normal file
304
modules/beast_core/threads/beast_ThreadPool.h
Normal file
@@ -0,0 +1,304 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_THREADPOOL_H_INCLUDED
|
||||
#define BEAST_THREADPOOL_H_INCLUDED
|
||||
|
||||
class ThreadPool;
|
||||
class ThreadPoolThread;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A task that is executed by a ThreadPool object.
|
||||
|
||||
A ThreadPool keeps a list of ThreadPoolJob objects which are executed by
|
||||
its threads.
|
||||
|
||||
The runJob() method needs to be implemented to do the task, and if the code that
|
||||
does the work takes a significant time to run, it must keep checking the shouldExit()
|
||||
method to see if something is trying to interrupt the job. If shouldExit() returns
|
||||
true, the runJob() method must return immediately.
|
||||
|
||||
@see ThreadPool, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPoolJob : LeakChecked <ThreadPoolJob>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool job object.
|
||||
After creating your job, add it to a thread pool with ThreadPool::addJob().
|
||||
*/
|
||||
explicit ThreadPoolJob (const String& name);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ThreadPoolJob();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the name of this job.
|
||||
@see setJobName
|
||||
*/
|
||||
String getJobName() const;
|
||||
|
||||
/** Changes the job's name.
|
||||
@see getJobName
|
||||
*/
|
||||
void setJobName (const String& newName);
|
||||
|
||||
//==============================================================================
|
||||
/** These are the values that can be returned by the runJob() method.
|
||||
*/
|
||||
enum JobStatus
|
||||
{
|
||||
jobHasFinished = 0, /**< indicates that the job has finished and can be
|
||||
removed from the pool. */
|
||||
|
||||
jobNeedsRunningAgain /**< indicates that the job would like to be called
|
||||
again when a thread is free. */
|
||||
};
|
||||
|
||||
/** Peforms the actual work that this job needs to do.
|
||||
|
||||
Your subclass must implement this method, in which is does its work.
|
||||
|
||||
If the code in this method takes a significant time to run, it must repeatedly check
|
||||
the shouldExit() method to see if something is trying to interrupt the job.
|
||||
If shouldExit() ever returns true, the runJob() method must return immediately.
|
||||
|
||||
If this method returns jobHasFinished, then the job will be removed from the pool
|
||||
immediately. If it returns jobNeedsRunningAgain, then the job will be left in the
|
||||
pool and will get a chance to run again as soon as a thread is free.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
virtual JobStatus runJob() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this job is currently running its runJob() method. */
|
||||
bool isRunning() const noexcept { return isActive; }
|
||||
|
||||
/** Returns true if something is trying to interrupt this job and make it stop.
|
||||
|
||||
Your runJob() method must call this whenever it gets a chance, and if it ever
|
||||
returns true, the runJob() method must return immediately.
|
||||
|
||||
@see signalJobShouldExit()
|
||||
*/
|
||||
bool shouldExit() const noexcept { return shouldStop; }
|
||||
|
||||
/** Calling this will cause the shouldExit() method to return true, and the job
|
||||
should (if it's been implemented correctly) stop as soon as possible.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
void signalJobShouldExit();
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
friend class ThreadPool;
|
||||
friend class ThreadPoolThread;
|
||||
String jobName;
|
||||
ThreadPool* pool;
|
||||
bool shouldStop, isActive, shouldBeDeleted;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of threads that will run a list of jobs.
|
||||
|
||||
When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method
|
||||
will be called by the next pooled thread that becomes free.
|
||||
|
||||
@see ThreadPoolJob, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPool : LeakChecked <ThreadPool>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
@param numberOfThreads the number of threads to run. These will be started
|
||||
immediately, and will run until the pool is deleted.
|
||||
*/
|
||||
ThreadPool (int numberOfThreads);
|
||||
|
||||
/** Creates a thread pool with one thread per CPU core.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
If you want to specify the number of threads, use the other constructor; this
|
||||
one creates a pool which has one thread for each CPU core.
|
||||
@see SystemStats::getNumCpus()
|
||||
*/
|
||||
ThreadPool();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
This will attempt to remove all the jobs before deleting, but if you want to
|
||||
specify a timeout, you should call removeAllJobs() explicitly before deleting
|
||||
the pool.
|
||||
*/
|
||||
~ThreadPool();
|
||||
|
||||
//==============================================================================
|
||||
/** A callback class used when you need to select which ThreadPoolJob objects are suitable
|
||||
for some kind of operation.
|
||||
@see ThreadPool::removeAllJobs
|
||||
*/
|
||||
class BEAST_API JobSelector
|
||||
{
|
||||
public:
|
||||
virtual ~JobSelector() {}
|
||||
|
||||
/** Should return true if the specified thread matches your criteria for whatever
|
||||
operation that this object is being used for.
|
||||
|
||||
Any implementation of this method must be extremely fast and thread-safe!
|
||||
*/
|
||||
virtual bool isJobSuitable (ThreadPoolJob* job) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a job to the queue.
|
||||
|
||||
Once a job has been added, then the next time a thread is free, it will run
|
||||
the job's ThreadPoolJob::runJob() method. Depending on the return value of the
|
||||
runJob() method, the pool will either remove the job from the pool or add it to
|
||||
the back of the queue to be run again.
|
||||
|
||||
If deleteJobWhenFinished is true, then the job object will be owned and deleted by
|
||||
the pool when not needed - if you do this, make sure that your object's destructor
|
||||
is thread-safe.
|
||||
|
||||
If deleteJobWhenFinished is false, the pointer will be used but not deleted, and
|
||||
the caller is responsible for making sure the object is not deleted before it has
|
||||
been removed from the pool.
|
||||
*/
|
||||
void addJob (ThreadPoolJob* job,
|
||||
bool deleteJobWhenFinished);
|
||||
|
||||
/** Tries to remove a job from the pool.
|
||||
|
||||
If the job isn't yet running, this will simply remove it. If it is running, it
|
||||
will wait for it to finish.
|
||||
|
||||
If the timeout period expires before the job finishes running, then the job will be
|
||||
left in the pool and this will return false. It returns true if the job is sucessfully
|
||||
stopped and removed.
|
||||
|
||||
@param job the job to remove
|
||||
@param interruptIfRunning if true, then if the job is currently busy, its
|
||||
ThreadPoolJob::signalJobShouldExit() method will be called to try
|
||||
to interrupt it. If false, then if the job will be allowed to run
|
||||
until it stops normally (or the timeout expires)
|
||||
@param timeOutMilliseconds the length of time this method should wait for the job to finish
|
||||
before giving up and returning false
|
||||
*/
|
||||
bool removeJob (ThreadPoolJob* job,
|
||||
bool interruptIfRunning,
|
||||
int timeOutMilliseconds);
|
||||
|
||||
/** Tries to remove all jobs from the pool.
|
||||
|
||||
@param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit()
|
||||
methods called to try to interrupt them
|
||||
@param timeOutMilliseconds the length of time this method should wait for all the jobs to finish
|
||||
before giving up and returning false
|
||||
@param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which
|
||||
jobs should be removed. If it is zero, all jobs are removed
|
||||
@returns true if all jobs are successfully stopped and removed; false if the timeout period
|
||||
expires while waiting for one or more jobs to stop
|
||||
*/
|
||||
bool removeAllJobs (bool interruptRunningJobs,
|
||||
int timeOutMilliseconds,
|
||||
JobSelector* selectedJobsToRemove = nullptr);
|
||||
|
||||
/** Returns the number of jobs currently running or queued.
|
||||
*/
|
||||
int getNumJobs() const;
|
||||
|
||||
/** Returns one of the jobs in the queue.
|
||||
|
||||
Note that this can be a very volatile list as jobs might be continuously getting shifted
|
||||
around in the list, and this method may return 0 if the index is currently out-of-range.
|
||||
*/
|
||||
ThreadPoolJob* getJob (int index) const;
|
||||
|
||||
/** Returns true if the given job is currently queued or running.
|
||||
|
||||
@see isJobRunning()
|
||||
*/
|
||||
bool contains (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Returns true if the given job is currently being run by a thread.
|
||||
*/
|
||||
bool isJobRunning (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Waits until a job has finished running and has been removed from the pool.
|
||||
|
||||
This will wait until the job is no longer in the pool - i.e. until its
|
||||
runJob() method returns ThreadPoolJob::jobHasFinished.
|
||||
|
||||
If the timeout period expires before the job finishes, this will return false;
|
||||
it returns true if the job has finished successfully.
|
||||
*/
|
||||
bool waitForJobToFinish (const ThreadPoolJob* job,
|
||||
int timeOutMilliseconds) const;
|
||||
|
||||
/** Returns a list of the names of all the jobs currently running or queued.
|
||||
If onlyReturnActiveJobs is true, only the ones currently running are returned.
|
||||
*/
|
||||
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
|
||||
|
||||
/** Changes the priority of all the threads.
|
||||
|
||||
This will call Thread::setPriority() for each thread in the pool.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
*/
|
||||
bool setThreadPriorities (int newPriority);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <ThreadPoolJob*> jobs;
|
||||
|
||||
class ThreadPoolThread;
|
||||
friend class ThreadPoolThread;
|
||||
friend class OwnedArray <ThreadPoolThread>;
|
||||
OwnedArray <ThreadPoolThread> threads;
|
||||
|
||||
CriticalSection lock;
|
||||
WaitableEvent jobFinishedSignal;
|
||||
|
||||
bool runNextJob();
|
||||
ThreadPoolJob* pickNextJobToRun();
|
||||
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
||||
void createThreads (int numThreads);
|
||||
void stopThreads();
|
||||
|
||||
// Note that this method has changed, and no longer has a parameter to indicate
|
||||
// whether the jobs should be deleted - see the new method for details.
|
||||
void removeAllJobs (bool, int, bool);
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_THREADPOOL_H_INCLUDED
|
||||
166
modules/beast_core/threads/beast_TimeSliceThread.cpp
Normal file
166
modules/beast_core/threads/beast_TimeSliceThread.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
TimeSliceThread::TimeSliceThread (const String& name)
|
||||
: Thread (name),
|
||||
clientBeingCalled (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TimeSliceThread::~TimeSliceThread()
|
||||
{
|
||||
stopThread (2000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client, int millisecondsBeforeStarting)
|
||||
{
|
||||
if (client != nullptr)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
client->nextCallTime = Time::getCurrentTime() + RelativeTime::milliseconds (millisecondsBeforeStarting);
|
||||
clients.addIfNotAlreadyThere (client);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client)
|
||||
{
|
||||
const ScopedLock sl1 (listLock);
|
||||
|
||||
// if there's a chance we're in the middle of calling this client, we need to
|
||||
// also lock the outer lock..
|
||||
if (clientBeingCalled == client)
|
||||
{
|
||||
const ScopedUnlock ul (listLock); // unlock first to get the order right..
|
||||
|
||||
const ScopedLock sl2 (callbackLock);
|
||||
const ScopedLock sl3 (listLock);
|
||||
|
||||
clients.removeFirstMatchingValue (client);
|
||||
}
|
||||
else
|
||||
{
|
||||
clients.removeFirstMatchingValue (client);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSliceThread::moveToFrontOfQueue (TimeSliceClient* client)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
if (clients.contains (client))
|
||||
{
|
||||
client->nextCallTime = Time::getCurrentTime();
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
int TimeSliceThread::getNumClients() const
|
||||
{
|
||||
return clients.size();
|
||||
}
|
||||
|
||||
TimeSliceClient* TimeSliceThread::getClient (const int i) const
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
return clients [i];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TimeSliceClient* TimeSliceThread::getNextClient (int index) const
|
||||
{
|
||||
Time soonest;
|
||||
TimeSliceClient* client = nullptr;
|
||||
|
||||
for (int i = clients.size(); --i >= 0;)
|
||||
{
|
||||
TimeSliceClient* const c = clients.getUnchecked ((i + index) % clients.size());
|
||||
|
||||
if (client == nullptr || c->nextCallTime < soonest)
|
||||
{
|
||||
client = c;
|
||||
soonest = c->nextCallTime;
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void TimeSliceThread::run()
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int timeToWait = 500;
|
||||
|
||||
{
|
||||
Time nextClientTime;
|
||||
|
||||
{
|
||||
const ScopedLock sl2 (listLock);
|
||||
|
||||
index = clients.size() > 0 ? ((index + 1) % clients.size()) : 0;
|
||||
|
||||
if (TimeSliceClient* const firstClient = getNextClient (index))
|
||||
nextClientTime = firstClient->nextCallTime;
|
||||
}
|
||||
|
||||
const Time now (Time::getCurrentTime());
|
||||
|
||||
if (nextClientTime > now)
|
||||
{
|
||||
timeToWait = (int) bmin ((int64) 500, (nextClientTime - now).inMilliseconds());
|
||||
}
|
||||
else
|
||||
{
|
||||
timeToWait = index == 0 ? 1 : 0;
|
||||
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
{
|
||||
const ScopedLock sl2 (listLock);
|
||||
clientBeingCalled = getNextClient (index);
|
||||
}
|
||||
|
||||
if (clientBeingCalled != nullptr)
|
||||
{
|
||||
const int msUntilNextCall = clientBeingCalled->useTimeSlice();
|
||||
|
||||
const ScopedLock sl2 (listLock);
|
||||
|
||||
if (msUntilNextCall >= 0)
|
||||
clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall);
|
||||
else
|
||||
clients.removeFirstMatchingValue (clientBeingCalled);
|
||||
|
||||
clientBeingCalled = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeToWait > 0)
|
||||
wait (timeToWait);
|
||||
}
|
||||
}
|
||||
143
modules/beast_core/threads/beast_TimeSliceThread.h
Normal file
143
modules/beast_core/threads/beast_TimeSliceThread.h
Normal file
@@ -0,0 +1,143 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_TIMESLICETHREAD_H_INCLUDED
|
||||
#define BEAST_TIMESLICETHREAD_H_INCLUDED
|
||||
|
||||
class TimeSliceThread;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used by the TimeSliceThread class.
|
||||
|
||||
To register your class with a TimeSliceThread, derive from this class and
|
||||
use the TimeSliceThread::addTimeSliceClient() method to add it to the list.
|
||||
|
||||
Make sure you always call TimeSliceThread::removeTimeSliceClient() before
|
||||
deleting your client!
|
||||
|
||||
@see TimeSliceThread
|
||||
*/
|
||||
class BEAST_API TimeSliceClient
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~TimeSliceClient() {}
|
||||
|
||||
/** Called back by a TimeSliceThread.
|
||||
|
||||
When you register this class with it, a TimeSliceThread will repeatedly call
|
||||
this method.
|
||||
|
||||
The implementation of this method should use its time-slice to do something that's
|
||||
quick - never block for longer than absolutely necessary.
|
||||
|
||||
@returns Your method should return the number of milliseconds which it would like to wait before being called
|
||||
again. Returning 0 will make the thread call again as soon as possible (after possibly servicing
|
||||
other busy clients). If you return a value below zero, your client will be removed from the list of clients,
|
||||
and won't be called again. The value you specify isn't a guaranteee, and is only used as a hint by the
|
||||
thread - the actual time before the next callback may be more or less than specified.
|
||||
You can force the TimeSliceThread to wake up and poll again immediately by calling its notify() method.
|
||||
*/
|
||||
virtual int useTimeSlice() = 0;
|
||||
|
||||
|
||||
private:
|
||||
friend class TimeSliceThread;
|
||||
Time nextCallTime;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A thread that keeps a list of clients, and calls each one in turn, giving them
|
||||
all a chance to run some sort of short task.
|
||||
|
||||
@see TimeSliceClient, Thread
|
||||
*/
|
||||
class BEAST_API TimeSliceThread
|
||||
: public Thread
|
||||
, LeakChecked <TimeSliceThread>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a TimeSliceThread.
|
||||
|
||||
When first created, the thread is not running. Use the startThread()
|
||||
method to start it.
|
||||
*/
|
||||
explicit TimeSliceThread (const String& threadName);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Deleting a Thread object that is running will only give the thread a
|
||||
brief opportunity to stop itself cleanly, so it's recommended that you
|
||||
should always call stopThread() with a decent timeout before deleting,
|
||||
to avoid the thread being forcibly killed (which is a Bad Thing).
|
||||
*/
|
||||
~TimeSliceThread();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a client to the list.
|
||||
|
||||
The client's callbacks will start after the number of milliseconds specified
|
||||
by millisecondsBeforeStarting (and this may happen before this method has returned).
|
||||
*/
|
||||
void addTimeSliceClient (TimeSliceClient* client, int millisecondsBeforeStarting = 0);
|
||||
|
||||
/** Removes a client from the list.
|
||||
|
||||
This method will make sure that all callbacks to the client have completely
|
||||
finished before the method returns.
|
||||
*/
|
||||
void removeTimeSliceClient (TimeSliceClient* client);
|
||||
|
||||
/** If the given client is waiting in the queue, it will be moved to the front
|
||||
and given a time-slice as soon as possible.
|
||||
If the specified client has not been added, nothing will happen.
|
||||
*/
|
||||
void moveToFrontOfQueue (TimeSliceClient* client);
|
||||
|
||||
/** Returns the number of registered clients. */
|
||||
int getNumClients() const;
|
||||
|
||||
/** Returns one of the registered clients. */
|
||||
TimeSliceClient* getClient (int index) const;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
void run() override;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
CriticalSection callbackLock, listLock;
|
||||
Array <TimeSliceClient*> clients;
|
||||
TimeSliceClient* clientBeingCalled;
|
||||
|
||||
TimeSliceClient* getNextClient (int index) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_TIMESLICETHREAD_H_INCLUDED
|
||||
112
modules/beast_core/threads/beast_WaitableEvent.h
Normal file
112
modules/beast_core/threads/beast_WaitableEvent.h
Normal file
@@ -0,0 +1,112 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_WAITABLEEVENT_H_INCLUDED
|
||||
#define BEAST_WAITABLEEVENT_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Allows threads to wait for events triggered by other threads.
|
||||
|
||||
A thread can call wait() on a WaitableObject, and this will suspend the
|
||||
calling thread until another thread wakes it up by calling the signal()
|
||||
method.
|
||||
*/
|
||||
class BEAST_API WaitableEvent : LeakChecked <WaitableEvent>, public Uncopyable
|
||||
{
|
||||
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) noexcept;
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If other threads are waiting on this object when it gets deleted, this
|
||||
can cause nasty errors, so be careful!
|
||||
*/
|
||||
~WaitableEvent() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (int timeOutMilliseconds = -1) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** 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 noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Resets the event to an unsignalled state.
|
||||
|
||||
If it's not already signalled, this does nothing.
|
||||
*/
|
||||
void reset() const noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
#if BEAST_WINDOWS
|
||||
void* handle;
|
||||
#else
|
||||
mutable pthread_cond_t condition;
|
||||
mutable pthread_mutex_t mutex;
|
||||
mutable bool triggered, manualReset;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_WAITABLEEVENT_H_INCLUDED
|
||||
Reference in New Issue
Block a user