git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@ -0,0 +1,118 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ChildProcess::ChildProcess() {}
ChildProcess::~ChildProcess() {}
bool ChildProcess::isRunning() const
{
return activeProcess != nullptr && activeProcess->isRunning();
}
int ChildProcess::readProcessOutput (void* dest, int numBytes)
{
return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
}
bool ChildProcess::kill()
{
return activeProcess == nullptr || activeProcess->killProcess();
}
uint32 ChildProcess::getExitCode() const
{
return activeProcess != nullptr ? activeProcess->getExitCode() : 0;
}
bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const
{
auto timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;
do
{
if (! isRunning())
return true;
Thread::sleep (2);
}
while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime);
return false;
}
String ChildProcess::readAllProcessOutput()
{
MemoryOutputStream result;
for (;;)
{
char buffer[512];
auto num = readProcessOutput (buffer, sizeof (buffer));
if (num <= 0)
break;
result.write (buffer, (size_t) num);
}
return result.toString();
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class ChildProcessTests : public UnitTest
{
public:
ChildProcessTests()
: UnitTest ("ChildProcess", UnitTestCategories::threads)
{}
void runTest() override
{
beginTest ("Child Processes");
#if JUCE_WINDOWS || JUCE_MAC || JUCE_LINUX || JUCE_BSD
ChildProcess p;
#if JUCE_WINDOWS
expect (p.start ("tasklist"));
#else
expect (p.start ("ls /"));
#endif
auto output = p.readAllProcessOutput();
expect (output.isNotEmpty());
#endif
}
};
static ChildProcessTests childProcessUnitTests;
#endif
} // namespace juce

View File

@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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.
@tags{Core}
*/
class JUCE_API ChildProcess
{
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();
/** These flags are used by the start() methods. */
enum StreamFlags
{
wantStdOut = 1,
wantStdErr = 2
};
/** 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.
The streamFlags is a combinations of values to indicate which of the child's output
streams should be read and returned by readProcessOutput().
*/
bool start (const String& command, int streamFlags = wantStdOut | wantStdErr);
/** 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.
The streamFlags is a combinations of values to indicate which of the child's output
streams should be read and returned by readProcessOutput().
*/
bool start (const StringArray& arguments, int streamFlags = wantStdOut | wantStdErr);
/** 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;
/** If the process has finished, this returns its exit code. */
uint32 getExitCode() 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;
std::unique_ptr<ActiveProcess> activeProcess;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
};
} // namespace juce

View File

@ -0,0 +1,262 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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.
In almost all cases you'll want to declare your CriticalSection as a member variable.
Occasionally you may want to declare one as a static variable, but in that case the usual
C++ static object order-of-construction warnings should be heeded.
@see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock
@tags{Core}
*/
class JUCE_API CriticalSection
{
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. */
using ScopedLockType = GenericScopedLock<CriticalSection>;
/** Provides the type of scoped unlocker to use with a CriticalSection. */
using ScopedUnlockType = GenericScopedUnlock<CriticalSection>;
/** Provides the type of scoped try-locker to use with a CriticalSection. */
using ScopedTryLockType = GenericScopedTryLock<CriticalSection>;
private:
//==============================================================================
#if JUCE_WINDOWS
// To avoid including windows.h in the public JUCE 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 JUCE_64BIT
std::aligned_storage<44, 8>::type lock;
#else
std::aligned_storage<24, 8>::type lock;
#endif
#else
mutable pthread_mutex_t lock;
#endif
JUCE_DECLARE_NON_COPYABLE (CriticalSection)
};
//==============================================================================
/**
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, ReferenceCountedArray
@tags{Core}
*/
class JUCE_API DummyCriticalSection
{
public:
inline DummyCriticalSection() = default;
inline ~DummyCriticalSection() = default;
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. */
using ScopedUnlockType = ScopedLockType;
private:
JUCE_DECLARE_NON_COPYABLE (DummyCriticalSection)
};
//==============================================================================
/**
Automatically locks and unlocks a CriticalSection object.
You can use a ScopedLock as a local variable to provide RAII-based locking of a CriticalSection.
e.g. @code
struct MyObject
{
CriticalSection objectLock;
// assuming that this example function will be called by multiple threads
void foo()
{
const ScopedLock myScopedLock (objectLock);
// objectLock is now locked..
...do some thread-safe work here...
// ..and objectLock gets unlocked here, as myScopedLock goes out of
// scope at the end of the block
}
};
@endcode
@see CriticalSection, ScopedUnlock
*/
using ScopedLock = CriticalSection::ScopedLockType;
//==============================================================================
/**
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
struct MyObject
{
CriticalSection objectLock;
void foo()
{
{
const ScopedLock myScopedLock (objectLock);
// objectLock is now locked..
{
ScopedUnlock myUnlocker (objectLock);
// ..and now unlocked..
}
// ..and now locked again..
}
// ..and finally unlocked.
}
};
@endcode
@see CriticalSection, ScopedLock
*/
using ScopedUnlock = CriticalSection::ScopedUnlockType;
//==============================================================================
/**
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
struct MyObject
{
CriticalSection objectLock;
void foo()
{
const ScopedTryLock myScopedTryLock (objectLock);
// Unlike using a ScopedLock, this may fail to actually get the lock, so you
// must call the isLocked() method before making any assumptions..
if (myScopedTryLock.isLocked())
{
...safely do some work...
}
else
{
// If we get here, then our attempt at locking failed because another thread had already locked it..
}
}
};
@endcode
@see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock
*/
using ScopedTryLock = CriticalSection::ScopedTryLockType;
} // namespace juce

View File

@ -0,0 +1,86 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
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.
@tags{Core}
*/
class JUCE_API DynamicLibrary
{
public:
/** Creates an unopened DynamicLibrary object.
Call open() to actually open one.
*/
DynamicLibrary() = default;
/**
*/
DynamicLibrary (const String& name) { open (name); }
/** Move constructor */
DynamicLibrary (DynamicLibrary&& other) noexcept
{
std::swap (handle, other.handle);
}
/** 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 = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DynamicLibrary)
};
} // namespace juce

View File

@ -0,0 +1,35 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
HighResolutionTimer::HighResolutionTimer() : pimpl (new Pimpl (*this)) {}
HighResolutionTimer::~HighResolutionTimer() { stopTimer(); }
void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); }
void HighResolutionTimer::stopTimer() { pimpl->stop(); }
bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; }
int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; }
} // namespace juce

View File

@ -0,0 +1,102 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
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
@tags{Core}
*/
class JUCE_API HighResolutionTimer
{
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;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer)
};
} // namespace juce

View File

@ -0,0 +1,121 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Acts as a critical section which processes can use to block each other.
@see CriticalSection
@tags{Core}
*/
class JUCE_API InterProcessLock
{
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:
//==============================================================================
/** 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;
JUCE_DECLARE_NON_COPYABLE (ScopedLockType)
};
private:
//==============================================================================
class Pimpl;
std::unique_ptr<Pimpl> pimpl;
CriticalSection lock;
String name;
JUCE_DECLARE_NON_COPYABLE (InterProcessLock)
};
} // namespace juce

View File

@ -0,0 +1,156 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Represents the current executable's process.
This contains methods for controlling the current application at the
process-level.
@see Thread, JUCEApplicationBase
@tags{Core}
*/
class JUCE_API Process
{
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 JUCE_CALLTYPE 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 JUCEApplicationBase::quit
*/
static void JUCE_CALLTYPE terminate();
//==============================================================================
/** Returns true if this application process is the one that the user is
currently using.
*/
static bool JUCE_CALLTYPE isForegroundProcess();
/** Attempts to make the current process the active one.
(This is not possible on some platforms).
*/
static void JUCE_CALLTYPE makeForegroundProcess();
/** Hides the application (on an OS that supports this, e.g. OSX, iOS, Android) */
static void JUCE_CALLTYPE hide();
//==============================================================================
/** 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 JUCE_CALLTYPE 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 JUCE_CALLTYPE lowerPrivilege();
//==============================================================================
/** Returns true if this process is being hosted by a debugger. */
static bool JUCE_CALLTYPE isRunningUnderDebugger() noexcept;
//==============================================================================
/** Tries to launch the OS's default reader application for a given file or URL. */
static bool JUCE_CALLTYPE 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 JUCE_CALLTYPE openEmailWithAttachments (const String& targetEmailAddress,
const String& emailSubject,
const String& bodyText,
const StringArray& filesToAttach);
//==============================================================================
#if JUCE_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 JUCE application, this will be automatically set to the module
handle of the executable.
If you've built a DLL and plan to use any JUCE 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* JUCE_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 JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept;
#endif
//==============================================================================
#if (JUCE_MAC && JUCE_MODULE_AVAILABLE_juce_gui_basics) || DOXYGEN
/** OSX ONLY - Shows or hides the OSX dock icon for this app. */
static void setDockIconVisible (bool isVisible);
#endif
//==============================================================================
#if JUCE_MAC || JUCE_LINUX || JUCE_BSD || DOXYGEN
/** UNIX ONLY - Attempts to use setrlimit to change the maximum number of file
handles that the app can open. Pass 0 or less as the parameter to mean
'infinite'. Returns true if it succeeds.
*/
static bool setMaxNumberOfFileHandles (int maxNumberOfFiles) noexcept;
#endif
private:
Process();
JUCE_DECLARE_NON_COPYABLE (Process)
};
} // namespace juce

View File

@ -0,0 +1,147 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ReadWriteLock::ReadWriteLock() noexcept
{
readerThreads.ensureStorageAllocated (16);
}
ReadWriteLock::~ReadWriteLock() noexcept
{
jassert (readerThreads.size() == 0);
jassert (numWriters == 0);
}
//==============================================================================
void ReadWriteLock::enterRead() const noexcept
{
while (! tryEnterRead())
readWaitEvent.wait (100);
}
bool ReadWriteLock::tryEnterRead() const noexcept
{
auto threadId = Thread::getCurrentThreadId();
const SpinLock::ScopedLockType sl (accessLock);
for (auto& readerThread : readerThreads)
{
if (readerThread.threadID == threadId)
{
readerThread.count++;
return true;
}
}
if (numWriters + numWaitingWriters == 0
|| (threadId == writerThreadId && numWriters > 0))
{
readerThreads.add ({ threadId, 1 });
return true;
}
return false;
}
void ReadWriteLock::exitRead() const noexcept
{
auto threadId = Thread::getCurrentThreadId();
const SpinLock::ScopedLockType sl (accessLock);
for (int i = 0; i < readerThreads.size(); ++i)
{
auto& readerThread = readerThreads.getReference (i);
if (readerThread.threadID == threadId)
{
if (--(readerThread.count) == 0)
{
readerThreads.remove (i);
readWaitEvent.signal();
writeWaitEvent.signal();
}
return;
}
}
jassertfalse; // unlocking a lock that wasn't locked..
}
//==============================================================================
void ReadWriteLock::enterWrite() const noexcept
{
auto threadId = Thread::getCurrentThreadId();
const SpinLock::ScopedLockType sl (accessLock);
while (! tryEnterWriteInternal (threadId))
{
++numWaitingWriters;
accessLock.exit();
writeWaitEvent.wait (100);
accessLock.enter();
--numWaitingWriters;
}
}
bool ReadWriteLock::tryEnterWrite() const noexcept
{
const SpinLock::ScopedLockType sl (accessLock);
return tryEnterWriteInternal (Thread::getCurrentThreadId());
}
bool ReadWriteLock::tryEnterWriteInternal (Thread::ThreadID threadId) const noexcept
{
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..
jassert (numWriters > 0 && writerThreadId == Thread::getCurrentThreadId());
if (--numWriters == 0)
{
writerThreadId = {};
readWaitEvent.signal();
writeWaitEvent.signal();
}
}
} // namespace juce

View File

@ -0,0 +1,146 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API ReadWriteLock
{
public:
//==============================================================================
/**
Creates a ReadWriteLock object.
*/
ReadWriteLock() noexcept;
/** Destructor.
If the object is deleted whilst locked, any subsequent behaviour is undefined.
*/
~ReadWriteLock() noexcept;
//==============================================================================
/** Locks this object for reading.
Multiple threads can simultaneously 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 simultaneously 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 readWaitEvent, writeWaitEvent;
mutable int numWaitingWriters = 0, numWriters = 0;
mutable Thread::ThreadID writerThreadId = {};
struct ThreadRecursionCount
{
Thread::ThreadID threadID;
int count;
};
mutable Array <ThreadRecursionCount> readerThreads;
bool tryEnterWriteInternal (Thread::ThreadID) const noexcept;
JUCE_DECLARE_NON_COPYABLE (ReadWriteLock)
};
} // namespace juce

View File

@ -0,0 +1,244 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
@tags{Core}
*/
template <class LockType>
class GenericScopedLock
{
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_;
JUCE_DECLARE_NON_COPYABLE (GenericScopedLock)
};
//==============================================================================
/**
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
@tags{Core}
*/
template <class LockType>
class GenericScopedUnlock
{
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_;
JUCE_DECLARE_NON_COPYABLE (GenericScopedUnlock)
};
//==============================================================================
/**
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
@tags{Core}
*/
template <class LockType>
class GenericScopedTryLock
{
public:
//==============================================================================
/** Creates a GenericScopedTryLock.
If acquireLockOnInitialisation is true then as soon as this ScopedTryLock
is created, it will attempt to acquire the lock with tryEnter.
You can retry acquiring the lock by calling retryLock.
When 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.
@see retryLock, isLocked
*/
inline explicit GenericScopedTryLock (const LockType& lock, bool acquireLockOnInitialisation = true) noexcept
: lock_ (lock), lockWasSuccessful (acquireLockOnInitialisation && 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; }
/** Retry gaining the lock by calling tryEnter on the underlying lock. */
bool retryLock() const noexcept { lockWasSuccessful = lock_.tryEnter(); return lockWasSuccessful; }
private:
//==============================================================================
const LockType& lock_;
mutable bool lockWasSuccessful;
JUCE_DECLARE_NON_COPYABLE (GenericScopedTryLock)
};
} // namespace juce

View File

@ -0,0 +1,84 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API ScopedReadLock
{
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_;
JUCE_DECLARE_NON_COPYABLE (ScopedReadLock)
};
} // namespace juce

View File

@ -0,0 +1,84 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API ScopedWriteLock
{
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_;
JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock)
};
}

View File

@ -0,0 +1,88 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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 in 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
@tags{Core}
*/
class JUCE_API SpinLock
{
public:
inline SpinLock() = default;
inline ~SpinLock() = default;
/** 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
{
jassert (lock.get() == 1); // Agh! Releasing a lock that isn't currently held!
lock = 0;
}
//==============================================================================
/** Provides the type of scoped lock to use for locking a SpinLock. */
using ScopedLockType = GenericScopedLock<SpinLock>;
/** Provides the type of scoped unlocker to use with a SpinLock. */
using ScopedUnlockType = GenericScopedUnlock<SpinLock>;
/** Provides the type of scoped try-lock to use for locking a SpinLock. */
using ScopedTryLockType = GenericScopedTryLock<SpinLock>;
private:
//==============================================================================
mutable Atomic<int> lock;
JUCE_DECLARE_NON_COPYABLE (SpinLock)
};
} // namespace juce

View File

@ -0,0 +1,539 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Thread::Thread (const String& name, size_t stackSize)
: threadName (name), threadStackSize (stackSize)
{
}
Thread::~Thread()
{
if (deleteOnThreadEnd)
return;
/* 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.
*/
jassert (! isThreadRunning());
stopThread (-1);
}
//==============================================================================
// 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 ReferenceCountedObject
{
CurrentThreadHolder() noexcept {}
using Ptr = ReferenceCountedObjectPtr<CurrentThreadHolder>;
ThreadLocalValue<Thread*> value;
JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder)
};
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))
{
jassert (getCurrentThreadId() == threadId.get());
if (affinityMask != 0)
setCurrentThreadAffinityMask (affinityMask);
try
{
run();
}
catch (...)
{
jassertfalse; // Your run() method mustn't throw any exceptions!
}
}
currentThreadHolder->value.releaseCurrentThreadStorage();
// Once closeThreadHandle is called this class may be deleted by a different
// thread, so we need to store deleteOnThreadEnd in a local variable.
auto shouldDeleteThis = deleteOnThreadEnd;
closeThreadHandle();
if (shouldDeleteThis)
delete this;
}
// used to wrap the incoming call from the platform-specific code
void JUCE_API juce_threadEntryPoint (void* userData)
{
static_cast<Thread*> (userData)->threadEntryPoint();
}
//==============================================================================
void Thread::startThread()
{
const ScopedLock sl (startStopLock);
shouldExit = 0;
if (threadHandle.get() == nullptr)
{
launchThread();
setThreadPriority (threadHandle.get(), threadPriority);
startSuspensionEvent.signal();
}
}
void Thread::startThread (int priority)
{
const ScopedLock sl (startStopLock);
if (threadHandle.get() == nullptr)
{
#if JUCE_ANDROID
isAndroidRealtimeThread = (priority == realtimeAudioPriority);
#endif
threadPriority = getAdjustedPriority (priority);
startThread();
}
else
{
setPriority (priority);
}
}
bool Thread::isThreadRunning() const
{
return threadHandle.get() != nullptr;
}
Thread* JUCE_CALLTYPE Thread::getCurrentThread()
{
return getCurrentThreadHolder()->value.get();
}
Thread::ThreadID Thread::getThreadId() const noexcept
{
return threadId.get();
}
//==============================================================================
void Thread::signalThreadShouldExit()
{
shouldExit = 1;
listeners.call ([] (Listener& l) { l.exitSignalSent(); });
}
bool Thread::threadShouldExit() const
{
return shouldExit.get() != 0;
}
bool Thread::currentThreadShouldExit()
{
if (auto* currentThread = getCurrentThread())
return currentThread->threadShouldExit();
return false;
}
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
{
// Doh! So how exactly do you expect this thread to wait for itself to stop??
jassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == ThreadID());
auto 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)
{
// agh! You can't stop the thread that's calling this method! How on earth
// would that work??
jassert (getCurrentThreadId() != getThreadId());
const ScopedLock sl (startStopLock);
if (isThreadRunning())
{
signalThreadShouldExit();
notify();
if (timeOutMilliseconds != 0)
waitForThreadToExit (timeOutMilliseconds);
if (isThreadRunning())
{
// 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..
jassertfalse;
Logger::writeToLog ("!! killing thread by force !!");
killThread();
threadHandle = nullptr;
threadId = {};
return false;
}
}
return true;
}
void Thread::addListener (Listener* listener)
{
listeners.add (listener);
}
void Thread::removeListener (Listener* listener)
{
listeners.remove (listener);
}
//==============================================================================
bool Thread::setPriority (int newPriority)
{
newPriority = getAdjustedPriority (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 JUCE_ANDROID
bool isRealtime = (newPriority == realtimeAudioPriority);
// you cannot switch from or to an Android realtime thread once the
// thread is already running!
jassert (!isThreadRunning() || (isRealtime == isAndroidRealtimeThread));
isAndroidRealtimeThread = isRealtime;
#endif
if ((! isThreadRunning()) || setThreadPriority (threadHandle.get(), newPriority))
{
threadPriority = newPriority;
return true;
}
return false;
}
bool Thread::setCurrentThreadPriority (const int newPriority)
{
return setThreadPriority ({}, getAdjustedPriority (newPriority));
}
void Thread::setAffinityMask (const uint32 newAffinityMask)
{
affinityMask = newAffinityMask;
}
int Thread::getAdjustedPriority (int newPriority)
{
return jlimit (0, 10, newPriority == realtimeAudioPriority ? 9 : newPriority);
}
//==============================================================================
bool Thread::wait (const int timeOutMilliseconds) const
{
return defaultEvent.wait (timeOutMilliseconds);
}
void Thread::notify() const
{
defaultEvent.signal();
}
//==============================================================================
struct LambdaThread : public Thread
{
LambdaThread (std::function<void()> f) : Thread ("anonymous"), fn (f) {}
void run() override
{
fn();
fn = nullptr; // free any objects that the lambda might contain while the thread is still active
}
std::function<void()> fn;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaThread)
};
void Thread::launch (std::function<void()> functionToRun)
{
auto anon = new LambdaThread (functionToRun);
anon->deleteOnThreadEnd = true;
anon->startThread();
}
//==============================================================================
void SpinLock::enter() const noexcept
{
if (! tryEnter())
{
for (int i = 20; --i >= 0;)
if (tryEnter())
return;
while (! tryEnter())
Thread::yield();
}
}
//==============================================================================
bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept
{
return juce_isRunningUnderDebugger();
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AtomicTests : public UnitTest
{
public:
AtomicTests()
: UnitTest ("Atomics", UnitTestCategories::threads)
{}
void runTest() override
{
beginTest ("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) 0x1122334455667788ULL) == (uint64) 0x8877665544332211LL);
beginTest ("Atomic int");
AtomicTester <int>::testInteger (*this);
beginTest ("Atomic unsigned int");
AtomicTester <unsigned int>::testInteger (*this);
beginTest ("Atomic int32");
AtomicTester <int32>::testInteger (*this);
beginTest ("Atomic uint32");
AtomicTester <uint32>::testInteger (*this);
beginTest ("Atomic long");
AtomicTester <long>::testInteger (*this);
beginTest ("Atomic int*");
AtomicTester <int*>::testInteger (*this);
beginTest ("Atomic float");
AtomicTester <float>::testFloat (*this);
#if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms
beginTest ("Atomic int64");
AtomicTester <int64>::testInteger (*this);
beginTest ("Atomic uint64");
AtomicTester <uint64>::testInteger (*this);
beginTest ("Atomic double");
AtomicTester <double>::testFloat (*this);
#endif
beginTest ("Atomic pointer increment/decrement");
Atomic<int*> a (a2); int* b (a2);
expect (++a == ++b);
{
beginTest ("Atomic void*");
Atomic<void*> atomic;
void* c;
atomic.set ((void*) 10);
c = (void*) 10;
expect (atomic.value == c);
expect (atomic.get() == c);
}
}
template <typename Type>
class AtomicTester
{
public:
AtomicTester() {}
static void testInteger (UnitTest& test)
{
Atomic<Type> a, b;
Type c;
a.set ((Type) 10);
c = (Type) 10;
test.expect (a.value == c);
test.expect (a.get() == c);
a += 15;
c += 15;
test.expect (a.get() == c);
a.memoryBarrier();
a -= 5;
c -= 5;
test.expect (a.get() == c);
test.expect (++a == ++c);
++a;
++c;
test.expect (--a == --c);
test.expect (a.get() == c);
a.memoryBarrier();
testFloat (test);
}
static void testFloat (UnitTest& test)
{
Atomic<Type> a, b;
a = (Type) 101;
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) 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 atomicUnitTests;
//==============================================================================
class ThreadLocalValueUnitTest : public UnitTest,
private Thread
{
public:
ThreadLocalValueUnitTest()
: UnitTest ("ThreadLocalValue", UnitTestCategories::threads),
Thread ("ThreadLocalValue Thread")
{}
void runTest() override
{
beginTest ("values are thread local");
{
ThreadLocalValue<int> threadLocal;
sharedThreadLocal = &threadLocal;
sharedThreadLocal.get()->get() = 1;
startThread();
signalThreadShouldExit();
waitForThreadToExit (-1);
mainThreadResult = sharedThreadLocal.get()->get();
expectEquals (mainThreadResult.get(), 1);
expectEquals (auxThreadResult.get(), 2);
}
beginTest ("values are per-instance");
{
ThreadLocalValue<int> a, b;
a.get() = 1;
b.get() = 2;
expectEquals (a.get(), 1);
expectEquals (b.get(), 2);
}
}
private:
Atomic<int> mainThreadResult, auxThreadResult;
Atomic<ThreadLocalValue<int>*> sharedThreadLocal;
void run() override
{
sharedThreadLocal.get()->get() = 2;
auxThreadResult = sharedThreadLocal.get()->get();
}
};
ThreadLocalValueUnitTest threadLocalValueUnitTest;
#endif
} // namespace juce

View File

@ -0,0 +1,415 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API Thread
{
public:
//==============================================================================
/**
Creates a thread.
When first created, the thread is not running. Use the startThread()
method to start it.
@param threadName The name of the thread which typically appears in
debug logs and profiles.
@param threadStackSize The size of the stack of the thread. If this value
is zero then the default stack size of the OS will
be used.
*/
explicit Thread (const String& threadName, size_t threadStackSize = 0);
/** Destructor.
You must never attempt to delete a Thread object while it's still running -
always call stopThread() and make sure your thread has stopped before deleting
the object. Failing to do so will throw an assertion, and put you firmly into
undefined behaviour territory.
*/
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;
//==============================================================================
/** Starts the thread running.
This will cause the thread's run() method to be called by a new thread.
If this thread is already running, 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, realtimeAudioPriority
*/
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 timeout, 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.
@returns true if the thread was cleanly stopped before the timeout, or false
if it had to be killed by force.
@see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning
*/
bool stopThread (int timeOutMilliseconds);
//==============================================================================
/** Invokes a lambda or function on its own thread.
This will spin up a Thread object which calls the function and then exits.
Bear in mind that starting and stopping a thread can be a fairly heavyweight
operation, so you might prefer to use a ThreadPool if you're kicking off a lot
of short background tasks.
Also note that using an anonymous thread makes it very difficult to interrupt
the function when you need to stop it, e.g. when your app quits. So it's up to
you to deal with situations where the function may fail to stop in time.
*/
static void launch (std::function<void()> functionToRun);
//==============================================================================
/** 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, 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, currentThreadShouldExit
*/
bool threadShouldExit() const;
/** Checks whether the current thread has been told to stop running.
On the message thread, this will always return false, otherwise
it will return threadShouldExit() called on the current thread.
@see threadShouldExit
*/
static bool currentThreadShouldExit();
/** Waits for the thread to stop.
This will wait 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) const;
//==============================================================================
/** Used to receive callbacks for thread exit calls */
class JUCE_API Listener
{
public:
virtual ~Listener() = default;
/** Called if Thread::signalThreadShouldExit was called.
@see Thread::threadShouldExit, Thread::addListener, Thread::removeListener
*/
virtual void exitSignalSent() = 0;
};
/** Add a listener to this thread which will receive a callback when
signalThreadShouldExit was called on this thread.
@see signalThreadShouldExit, removeListener
*/
void addListener (Listener*);
/** Removes a listener added with addListener. */
void removeListener (Listener*);
//==============================================================================
/** Special realtime audio thread priority
This priority will create a high-priority thread which is best suited
for realtime audio processing.
Currently, this priority is identical to priority 9, except when building
for Android with OpenSL/Oboe support.
In this case, JUCE will ask OpenSL/Oboe to construct a super high priority thread
specifically for realtime audio processing.
Note that this priority can only be set **before** the thread has
started. Switching to this priority, or from this priority to a different
priority, is not supported under Android and will assert.
For best performance this thread should yield at regular intervals
and not call any blocking APIs.
@see startThread, setPriority, sleep, WaitableEvent
*/
enum
{
realtimeAudioPriority = -1
};
/** 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.
@see realtimeAudioPriority
*/
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 JUCE_CALLTYPE setCurrentThreadAffinityMask (uint32 affinityMask);
//==============================================================================
/** Suspends the execution of the current thread until the specified timeout period
has elapsed (note that this may not be exact).
The timeout period must not be negative and whilst sleeping the thread cannot
be woken up so it should only be used for short periods of time and when other
methods such as using a WaitableEvent or CriticalSection are not possible.
*/
static void JUCE_CALLTYPE sleep (int milliseconds);
/** Yields the current thread's CPU time-slot and allows a new thread to run.
If there are no other threads of equal or higher priority currently running then
this will return immediately and the current thread will continue to run.
*/
static void JUCE_CALLTYPE yield();
//==============================================================================
/** Suspends the execution of this thread until either the specified timeout period
has elapsed, or another thread calls the notify() method to wake it up.
A negative timeout 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) 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()
*/
using ThreadID = void*;
/** 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 JUCE_CALLTYPE getCurrentThreadId();
/** Finds the thread object that is currently running.
Note that the main UI thread (or other non-JUCE threads) don't have a Thread
object associated with them, so this will return nullptr.
*/
static Thread* JUCE_CALLTYPE 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;
/** Returns the name of the thread. This is the name that gets set in the constructor. */
const String& getThreadName() const noexcept { return threadName; }
/** Changes the name of the caller thread.
Different OSes may place different length or content limits on this name.
*/
static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName);
#if JUCE_ANDROID || DOXYGEN
//==============================================================================
/** Initialises the JUCE subsystem for projects not created by the Projucer
On Android, JUCE needs to be initialised once before it is used. The Projucer
will automatically generate the necessary java code to do this. However, if
you are using JUCE without the Projucer or are creating a library made with
JUCE intended for use in non-JUCE apks, then you must call this method
manually once on apk startup.
You can call this method from C++ or directly from java by calling the
following java method:
@code
com.rmsl.juce.Java.initialiseJUCE (myContext);
@endcode
Note that the above java method is only available in Android Studio projects
created by the Projucer. If you need to call this from another type of project
then you need to add the following java file to
your project:
@code
package com.rmsl.juce;
public class Java
{
static { System.loadLibrary ("juce_jni"); }
public native static void initialiseJUCE (Context context);
}
@endcode
@param jniEnv this is a pointer to JNI's JNIEnv variable. Any callback
from Java into C++ will have this passed in as it's first
parameter.
@param jContext this is a jobject referring to your app/service/receiver/
provider's Context. JUCE needs this for many of it's internal
functions.
*/
static void initialiseJUCE (void* jniEnv, void* jContext);
#endif
private:
//==============================================================================
const String threadName;
Atomic<void*> threadHandle { nullptr };
Atomic<ThreadID> threadId = {};
CriticalSection startStopLock;
WaitableEvent startSuspensionEvent, defaultEvent;
int threadPriority = 5;
size_t threadStackSize;
uint32 affinityMask = 0;
bool deleteOnThreadEnd = false;
Atomic<int32> shouldExit { 0 };
ListenerList<Listener, Array<Listener*, CriticalSection>> listeners;
#if JUCE_ANDROID
bool isAndroidRealtimeThread = false;
#endif
#ifndef DOXYGEN
friend void JUCE_API juce_threadEntryPoint (void*);
#endif
void launchThread();
void closeThreadHandle();
void killThread();
void threadEntryPoint();
static bool setThreadPriority (void*, int);
static int getAdjustedPriority (int);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread)
};
} // namespace juce

View File

@ -0,0 +1,148 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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 must be a primitive type, or a simple POD struct.
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.
@tags{Core}
*/
template <typename Type>
class ThreadLocalValue
{
public:
/** */
ThreadLocalValue() = default;
/** Destructor.
When this object is deleted, all the value objects for all threads will be deleted.
*/
~ThreadLocalValue()
{
for (auto* o = first.get(); o != nullptr;)
{
auto* next = o->next;
delete o;
o = next;
}
}
/** 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
{
auto threadId = Thread::getCurrentThreadId();
ObjectHolder* o = nullptr;
for (o = first.get(); o != nullptr; o = o->next)
if (o->threadId.get() == threadId)
return o->object;
for (o = first.get(); o != nullptr; o = o->next)
if (o->threadId.compareAndSetBool (threadId, nullptr))
break;
if (o != nullptr)
o->object = Type();
else
for (o = new ObjectHolder (threadId, first.get());
! first.compareAndSetBool (o, o->next);
o->next = first.get());
return o->object;
}
/** Called by a thread before it terminates, to allow this class to release
any storage associated with the thread.
*/
void releaseCurrentThreadStorage()
{
auto threadId = Thread::getCurrentThreadId();
for (auto* o = first.get(); o != nullptr; o = o->next)
if (o->threadId.compareAndSetBool (nullptr, threadId))
return;
}
private:
//==============================================================================
struct ObjectHolder
{
ObjectHolder (Thread::ThreadID idToUse, ObjectHolder* n) : threadId (idToUse), next (n), object() {}
Atomic<Thread::ThreadID> threadId;
ObjectHolder* next;
Type object;
JUCE_DECLARE_NON_COPYABLE (ObjectHolder)
};
mutable Atomic<ObjectHolder*> first;
JUCE_DECLARE_NON_COPYABLE (ThreadLocalValue)
};
} // namespace juce

View File

@ -0,0 +1,432 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct ThreadPool::ThreadPoolThread : public Thread
{
ThreadPoolThread (ThreadPool& p, size_t stackSize)
: Thread ("Pool", stackSize), pool (p)
{
}
void run() override
{
while (! threadShouldExit())
if (! pool.runNextJob (*this))
wait (500);
}
std::atomic<ThreadPoolJob*> currentJob { nullptr };
ThreadPool& pool;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
};
//==============================================================================
ThreadPoolJob::ThreadPoolJob (const String& name) : jobName (name)
{
}
ThreadPoolJob::~ThreadPoolJob()
{
// you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
// to remove it first!
jassert (pool == nullptr || ! pool->contains (this));
}
String ThreadPoolJob::getJobName() const
{
return jobName;
}
void ThreadPoolJob::setJobName (const String& newName)
{
jobName = newName;
}
void ThreadPoolJob::signalJobShouldExit()
{
shouldStop = true;
listeners.call ([] (Thread::Listener& l) { l.exitSignalSent(); });
}
void ThreadPoolJob::addListener (Thread::Listener* listener)
{
listeners.add (listener);
}
void ThreadPoolJob::removeListener (Thread::Listener* listener)
{
listeners.remove (listener);
}
ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
{
if (auto* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
return t->currentJob.load();
return nullptr;
}
//==============================================================================
ThreadPool::ThreadPool (int numThreads, size_t threadStackSize)
{
jassert (numThreads > 0); // not much point having a pool without any threads!
createThreads (numThreads, threadStackSize);
}
ThreadPool::ThreadPool()
{
createThreads (SystemStats::getNumCpus());
}
ThreadPool::~ThreadPool()
{
removeAllJobs (true, 5000);
stopThreads();
}
void ThreadPool::createThreads (int numThreads, size_t threadStackSize)
{
for (int i = jmax (1, numThreads); --i >= 0;)
threads.add (new ThreadPoolThread (*this, threadStackSize));
for (auto* t : threads)
t->startThread();
}
void ThreadPool::stopThreads()
{
for (auto* t : threads)
t->signalThreadShouldExit();
for (auto* t : threads)
t->stopThread (500);
}
void ThreadPool::addJob (ThreadPoolJob* job, bool deleteJobWhenFinished)
{
jassert (job != nullptr);
jassert (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 (auto* t : threads)
t->notify();
}
}
void ThreadPool::addJob (std::function<ThreadPoolJob::JobStatus()> jobToRun)
{
struct LambdaJobWrapper : public ThreadPoolJob
{
LambdaJobWrapper (std::function<ThreadPoolJob::JobStatus()> j) : ThreadPoolJob ("lambda"), job (j) {}
JobStatus runJob() override { return job(); }
std::function<ThreadPoolJob::JobStatus()> job;
};
addJob (new LambdaJobWrapper (jobToRun), true);
}
void ThreadPool::addJob (std::function<void()> jobToRun)
{
struct LambdaJobWrapper : public ThreadPoolJob
{
LambdaJobWrapper (std::function<void()> j) : ThreadPoolJob ("lambda"), job (j) {}
JobStatus runJob() override { job(); return ThreadPoolJob::jobHasFinished; }
std::function<void()> job;
};
addJob (new LambdaJobWrapper (jobToRun), true);
}
int ThreadPool::getNumJobs() const noexcept
{
const ScopedLock sl (lock);
return jobs.size();
}
int ThreadPool::getNumThreads() const noexcept
{
return threads.size();
}
ThreadPoolJob* ThreadPool::getJob (int index) const noexcept
{
const ScopedLock sl (lock);
return jobs [index];
}
bool ThreadPool::contains (const ThreadPoolJob* job) const noexcept
{
const ScopedLock sl (lock);
return jobs.contains (const_cast<ThreadPoolJob*> (job));
}
bool ThreadPool::isJobRunning (const ThreadPoolJob* job) const noexcept
{
const ScopedLock sl (lock);
return jobs.contains (const_cast<ThreadPoolJob*> (job)) && job->isActive;
}
void ThreadPool::moveJobToFront (const ThreadPoolJob* job) noexcept
{
const ScopedLock sl (lock);
auto index = jobs.indexOf (const_cast<ThreadPoolJob*> (job));
if (index > 0 && ! job->isActive)
jobs.move (index, 0);
}
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* job, int timeOutMs) const
{
if (job != nullptr)
{
auto 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* job, bool interruptIfRunning, 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 (bool interruptRunningJobs, int timeOutMs,
ThreadPool::JobSelector* selectedJobsToRemove)
{
Array<ThreadPoolJob*> jobsToWaitFor;
{
OwnedArray<ThreadPoolJob> deletionList;
{
const ScopedLock sl (lock);
for (int i = jobs.size(); --i >= 0;)
{
auto* 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);
}
}
}
}
}
auto start = Time::getMillisecondCounter();
for (;;)
{
for (int i = jobsToWaitFor.size(); --i >= 0;)
{
auto* 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 (bool onlyReturnActiveJobs) const
{
StringArray s;
const ScopedLock sl (lock);
for (auto* job : jobs)
if (job->isActive || ! onlyReturnActiveJobs)
s.add (job->getJobName());
return s;
}
bool ThreadPool::setThreadPriorities (int newPriority)
{
bool ok = true;
for (auto* t : threads)
if (! t->setPriority (newPriority))
ok = false;
return ok;
}
ThreadPoolJob* ThreadPool::pickNextJobToRun()
{
OwnedArray<ThreadPoolJob> deletionList;
{
const ScopedLock sl (lock);
for (int i = 0; i < jobs.size(); ++i)
{
if (auto* job = jobs[i])
{
if (! job->isActive)
{
if (job->shouldStop)
{
jobs.remove (i);
addToDeleteList (deletionList, job);
--i;
continue;
}
job->isActive = true;
return job;
}
}
}
}
return nullptr;
}
bool ThreadPool::runNextJob (ThreadPoolThread& thread)
{
if (auto* job = pickNextJobToRun())
{
auto result = ThreadPoolJob::jobHasFinished;
thread.currentJob = job;
try
{
result = job->runJob();
}
catch (...)
{
jassertfalse; // Your runJob() method mustn't throw any exceptions!
}
thread.currentJob = nullptr;
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;
}
return false;
}
void ThreadPool::addToDeleteList (OwnedArray<ThreadPoolJob>& deletionList, ThreadPoolJob* job) const
{
job->shouldStop = true;
job->pool = nullptr;
if (job->shouldBeDeleted)
deletionList.add (job);
}
} // namespace juce

View File

@ -0,0 +1,343 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class ThreadPool;
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API ThreadPoolJob
{
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. */
};
/** Performs 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()
*/
virtual void signalJobShouldExit();
/** Add a listener to this thread job which will receive a callback when
signalJobShouldExit was called on this thread job.
@see signalJobShouldExit, removeListener
*/
void addListener (Thread::Listener*);
/** Removes a listener added with addListener. */
void removeListener (Thread::Listener*);
//==============================================================================
/** If the calling thread is being invoked inside a runJob() method, this will
return the ThreadPoolJob that it belongs to.
*/
static ThreadPoolJob* getCurrentThreadPoolJob();
//==============================================================================
private:
friend class ThreadPool;
String jobName;
ThreadPool* pool = nullptr;
std::atomic<bool> shouldStop { false }, isActive { false }, shouldBeDeleted { false };
ListenerList<Thread::Listener, Array<Thread::Listener*, CriticalSection>> listeners;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob)
};
//==============================================================================
/**
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
@tags{Core}
*/
class JUCE_API ThreadPool
{
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.
@param threadStackSize the size of the stack of each thread. If this value
is zero then the default stack size of the OS will
be used.
*/
ThreadPool (int numberOfThreads, size_t threadStackSize = 0);
/** 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 JUCE_API JobSelector
{
public:
virtual ~JobSelector() = default;
/** 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);
/** Adds a lambda function to be called as a job.
This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
*/
void addJob (std::function<ThreadPoolJob::JobStatus()> job);
/** Adds a lambda function to be called as a job.
This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
*/
void addJob (std::function<void()> job);
/** 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 successfully
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 not a nullptr, the JobSelector object is asked to decide
which jobs should be removed. If it is a nullptr, 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 noexcept;
/** Returns the number of threads assigned to this thread pool. */
int getNumThreads() const noexcept;
/** 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 nullptr if the index is currently out-of-range.
*/
ThreadPoolJob* getJob (int index) const noexcept;
/** Returns true if the given job is currently queued or running.
@see isJobRunning()
*/
bool contains (const ThreadPoolJob* job) const noexcept;
/** Returns true if the given job is currently being run by a thread. */
bool isJobRunning (const ThreadPoolJob* job) const noexcept;
/** 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;
/** If the given job is in the queue, this will move it to the front so that it
is the next one to be executed.
*/
void moveJobToFront (const ThreadPoolJob* jobToMove) noexcept;
/** 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;
struct ThreadPoolThread;
friend class ThreadPoolJob;
OwnedArray<ThreadPoolThread> threads;
CriticalSection lock;
WaitableEvent jobFinishedSignal;
bool runNextJob (ThreadPoolThread&);
ThreadPoolJob* pickNextJobToRun();
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
void createThreads (int numThreads, size_t threadStackSize = 0);
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);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPool)
};
} // namespace juce

View File

@ -0,0 +1,184 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
TimeSliceThread::TimeSliceThread (const String& name) : Thread (name)
{
}
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::removeAllClients()
{
for (;;)
{
if (auto* c = getClient (0))
removeTimeSliceClient (c);
else
break;
}
}
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;)
{
auto* c = clients.getUnchecked ((i + index) % clients.size());
if (c != nullptr && (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;
int numClients = 0;
{
const ScopedLock sl2 (listLock);
numClients = clients.size();
index = numClients > 0 ? ((index + 1) % numClients) : 0;
if (auto* firstClient = getNextClient (index))
nextClientTime = firstClient->nextCallTime;
}
if (numClients > 0)
{
auto now = Time::getCurrentTime();
if (nextClientTime > now)
{
timeToWait = (int) jmin ((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);
}
}
} // namespace juce

View File

@ -0,0 +1,150 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
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
@tags{Core}
*/
class JUCE_API TimeSliceClient
{
public:
/** Destructor. */
virtual ~TimeSliceClient() = default;
/** 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 guarantee, 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
@tags{Core}
*/
class JUCE_API TimeSliceThread : public Thread
{
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() override;
//==============================================================================
/** 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* clientToAdd, int millisecondsBeforeStarting = 0);
/** 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* clientToMove);
/** 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* clientToRemove);
/** Removes all the active and pending clients from the list.
This method will make sure that all callbacks to clients have finished before the
method returns.
*/
void removeAllClients();
/** 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 = nullptr;
TimeSliceClient* getNextClient (int index) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimeSliceThread)
};
} // namespace juce

View File

@ -0,0 +1,70 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
WaitableEvent::WaitableEvent (bool manualReset) noexcept
: useManualReset (manualReset)
{
}
bool WaitableEvent::wait (int timeOutMilliseconds) const
{
std::unique_lock<std::mutex> lock (mutex);
if (! triggered)
{
if (timeOutMilliseconds < 0)
{
condition.wait (lock, [this] { return triggered == true; });
}
else
{
if (! condition.wait_for (lock, std::chrono::milliseconds (timeOutMilliseconds),
[this] { return triggered == true; }))
{
return false;
}
}
}
if (! useManualReset)
reset();
return true;
}
void WaitableEvent::signal() const
{
std::lock_guard<std::mutex> lock (mutex);
triggered = true;
condition.notify_all();
}
void WaitableEvent::reset() const
{
triggered = false;
}
} // namespace juce

View File

@ -0,0 +1,99 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Allows threads to wait for events triggered by other threads.
A thread can call WaitableEvent::wait() to suspend the calling thread until
another thread wakes it up by calling the WaitableEvent::signal() method.
@tags{Core}
*/
class JUCE_API WaitableEvent
{
public:
//==============================================================================
/** Creates a WaitableEvent object.
The object is initially in an unsignalled state.
@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.
*/
explicit WaitableEvent (bool manualReset = false) 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;
/** Wakes up any threads that are currently waiting on this object.
If signal() is called when nothing is waiting, the next thread to call wait()
will return immediately and reset the signal.
If the WaitableEvent is manual reset, all current and future threads that wait upon this
object will be woken, until reset() is explicitly called.
If the WaitableEvent is automatic reset, and one or more threads is waiting upon the object,
then one of them will be woken up. If no threads are currently waiting, then the next thread
to call wait() will be woken up. As soon as a thread is woken, the signal is automatically
reset.
@see wait, reset
*/
void signal() const;
/** Resets the event to an unsignalled state.
If it's not already signalled, this does nothing.
*/
void reset() const;
private:
//==============================================================================
bool useManualReset;
mutable std::mutex mutex;
mutable std::condition_variable condition;
mutable std::atomic<bool> triggered { false };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaitableEvent)
};
} // namespace juce