Squashed 'src/beast/' content from commit 43e6d34

git-subtree-dir: src/beast
git-subtree-split: 43e6d345e4
This commit is contained in:
Vinnie Falco
2013-09-11 09:57:14 -07:00
commit 86ba8ffca6
487 changed files with 263286 additions and 0 deletions

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

View 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

View 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

View 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

View 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; }

View 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

View 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

View 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

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

View 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

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

View 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

View 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

View 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

View 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

View 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

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

View 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

View 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

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

View 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

View 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

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

View 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

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

View 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

View 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