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:
118
deps/juce/modules/juce_core/threads/juce_ChildProcess.cpp
vendored
Normal file
118
deps/juce/modules/juce_core/threads/juce_ChildProcess.cpp
vendored
Normal 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
|
112
deps/juce/modules/juce_core/threads/juce_ChildProcess.h
vendored
Normal file
112
deps/juce/modules/juce_core/threads/juce_ChildProcess.h
vendored
Normal 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
|
262
deps/juce/modules/juce_core/threads/juce_CriticalSection.h
vendored
Normal file
262
deps/juce/modules/juce_core/threads/juce_CriticalSection.h
vendored
Normal 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
|
86
deps/juce/modules/juce_core/threads/juce_DynamicLibrary.h
vendored
Normal file
86
deps/juce/modules/juce_core/threads/juce_DynamicLibrary.h
vendored
Normal 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
|
35
deps/juce/modules/juce_core/threads/juce_HighResolutionTimer.cpp
vendored
Normal file
35
deps/juce/modules/juce_core/threads/juce_HighResolutionTimer.cpp
vendored
Normal 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
|
102
deps/juce/modules/juce_core/threads/juce_HighResolutionTimer.h
vendored
Normal file
102
deps/juce/modules/juce_core/threads/juce_HighResolutionTimer.h
vendored
Normal 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
|
121
deps/juce/modules/juce_core/threads/juce_InterProcessLock.h
vendored
Normal file
121
deps/juce/modules/juce_core/threads/juce_InterProcessLock.h
vendored
Normal 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
|
156
deps/juce/modules/juce_core/threads/juce_Process.h
vendored
Normal file
156
deps/juce/modules/juce_core/threads/juce_Process.h
vendored
Normal 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
|
147
deps/juce/modules/juce_core/threads/juce_ReadWriteLock.cpp
vendored
Normal file
147
deps/juce/modules/juce_core/threads/juce_ReadWriteLock.cpp
vendored
Normal 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
|
146
deps/juce/modules/juce_core/threads/juce_ReadWriteLock.h
vendored
Normal file
146
deps/juce/modules/juce_core/threads/juce_ReadWriteLock.h
vendored
Normal 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
|
244
deps/juce/modules/juce_core/threads/juce_ScopedLock.h
vendored
Normal file
244
deps/juce/modules/juce_core/threads/juce_ScopedLock.h
vendored
Normal 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
|
84
deps/juce/modules/juce_core/threads/juce_ScopedReadLock.h
vendored
Normal file
84
deps/juce/modules/juce_core/threads/juce_ScopedReadLock.h
vendored
Normal 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
|
84
deps/juce/modules/juce_core/threads/juce_ScopedWriteLock.h
vendored
Normal file
84
deps/juce/modules/juce_core/threads/juce_ScopedWriteLock.h
vendored
Normal 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)
|
||||
};
|
||||
|
||||
}
|
88
deps/juce/modules/juce_core/threads/juce_SpinLock.h
vendored
Normal file
88
deps/juce/modules/juce_core/threads/juce_SpinLock.h
vendored
Normal 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
|
539
deps/juce/modules/juce_core/threads/juce_Thread.cpp
vendored
Normal file
539
deps/juce/modules/juce_core/threads/juce_Thread.cpp
vendored
Normal 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
|
415
deps/juce/modules/juce_core/threads/juce_Thread.h
vendored
Normal file
415
deps/juce/modules/juce_core/threads/juce_Thread.h
vendored
Normal 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
|
148
deps/juce/modules/juce_core/threads/juce_ThreadLocalValue.h
vendored
Normal file
148
deps/juce/modules/juce_core/threads/juce_ThreadLocalValue.h
vendored
Normal 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
|
432
deps/juce/modules/juce_core/threads/juce_ThreadPool.cpp
vendored
Normal file
432
deps/juce/modules/juce_core/threads/juce_ThreadPool.cpp
vendored
Normal 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
|
343
deps/juce/modules/juce_core/threads/juce_ThreadPool.h
vendored
Normal file
343
deps/juce/modules/juce_core/threads/juce_ThreadPool.h
vendored
Normal 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
|
184
deps/juce/modules/juce_core/threads/juce_TimeSliceThread.cpp
vendored
Normal file
184
deps/juce/modules/juce_core/threads/juce_TimeSliceThread.cpp
vendored
Normal 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
|
150
deps/juce/modules/juce_core/threads/juce_TimeSliceThread.h
vendored
Normal file
150
deps/juce/modules/juce_core/threads/juce_TimeSliceThread.h
vendored
Normal 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
|
70
deps/juce/modules/juce_core/threads/juce_WaitableEvent.cpp
vendored
Normal file
70
deps/juce/modules/juce_core/threads/juce_WaitableEvent.cpp
vendored
Normal 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
|
99
deps/juce/modules/juce_core/threads/juce_WaitableEvent.h
vendored
Normal file
99
deps/juce/modules/juce_core/threads/juce_WaitableEvent.h
vendored
Normal 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
|
Reference in New Issue
Block a user