From b3da4ae94641025c477e5e5d1f191dde52f54f81 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Mon, 19 Jun 2023 12:24:45 +0000 Subject: [PATCH] ThreadPool: Add support for a user specified thread name --- examples/Utilities/MultithreadingDemo.h | 3 +- .../Source/UI/GraphEditorPanel.cpp | 2 +- .../UserAccount/jucer_LicenseQueryThread.h | 2 +- .../scanning/juce_PluginListComponent.cpp | 2 +- modules/juce_core/misc/juce_Functional.h | 2 +- modules/juce_core/threads/juce_Thread.h | 5 +- modules/juce_core/threads/juce_ThreadPool.cpp | 23 +++-- modules/juce_core/threads/juce_ThreadPool.h | 85 +++++++++++++++---- .../native/juce_DragAndDrop_windows.cpp | 2 +- 9 files changed, 96 insertions(+), 30 deletions(-) diff --git a/examples/Utilities/MultithreadingDemo.h b/examples/Utilities/MultithreadingDemo.h index 9b329253ff..d674e39028 100644 --- a/examples/Utilities/MultithreadingDemo.h +++ b/examples/Utilities/MultithreadingDemo.h @@ -317,7 +317,8 @@ private: } //============================================================================== - ThreadPool pool { 3 }; + ThreadPool pool { ThreadPoolOptions{}.withThreadName ("Demo thread pool") + .withNumberOfThreads (3) }; TextButton controlButton { "Thread type" }; bool isUsingPool = false; diff --git a/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp b/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp index 1f523f3c1a..a324abc518 100644 --- a/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp +++ b/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp @@ -50,7 +50,7 @@ FileSearchPath paths; static constexpr auto numJobs = 5; - ThreadPool pool { numJobs }; + ThreadPool pool { ThreadPoolOptions{}.withNumberOfThreads (numJobs) }; void startScan() { diff --git a/extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h b/extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h index f60c26f370..08b96574f4 100644 --- a/extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h +++ b/extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h @@ -363,7 +363,7 @@ private: } //============================================================================== - ThreadPool jobPool { 1 }; + ThreadPool jobPool { ThreadPoolOptions{}.withNumberOfThreads (1) }; //============================================================================== JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread) diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 455393fb85..af4e0013fc 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -553,7 +553,7 @@ private: if (numThreads > 0) { - pool.reset (new ThreadPool (numThreads)); + pool.reset (new ThreadPool (ThreadPoolOptions{}.withNumberOfThreads (numThreads))); for (int i = numThreads; --i >= 0;) pool->addJob (new ScanJob (*this), true); diff --git a/modules/juce_core/misc/juce_Functional.h b/modules/juce_core/misc/juce_Functional.h index e2a8bbfd1f..7f20239a61 100644 --- a/modules/juce_core/misc/juce_Functional.h +++ b/modules/juce_core/misc/juce_Functional.h @@ -80,7 +80,7 @@ using DisableIfSameOrDerived = std::enable_if_t -Object withMember (Object copy, Member OtherObject::* member, Other&& value) +[[nodiscard]] Object withMember (Object copy, Member OtherObject::* member, Other&& value) { copy.*member = std::forward (value); return copy; diff --git a/modules/juce_core/threads/juce_Thread.h b/modules/juce_core/threads/juce_Thread.h index 97b66ff6fb..03a3ad97cc 100644 --- a/modules/juce_core/threads/juce_Thread.h +++ b/modules/juce_core/threads/juce_Thread.h @@ -42,6 +42,9 @@ namespace juce class JUCE_API Thread { public: + //============================================================================== + static constexpr size_t osDefaultStackSize { 0 }; + //============================================================================== /** The different runtime priorities of non-realtime threads. @@ -95,7 +98,7 @@ public: is zero then the default stack size of the OS will be used. */ - explicit Thread (const String& threadName, size_t threadStackSize = 0); + explicit Thread (const String& threadName, size_t threadStackSize = osDefaultStackSize); /** Destructor. diff --git a/modules/juce_core/threads/juce_ThreadPool.cpp b/modules/juce_core/threads/juce_ThreadPool.cpp index 8639481de6..bfccce215c 100644 --- a/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/modules/juce_core/threads/juce_ThreadPool.cpp @@ -25,8 +25,9 @@ namespace juce struct ThreadPool::ThreadPoolThread : public Thread { - ThreadPoolThread (ThreadPool& p, size_t stackSize) - : Thread ("Pool", stackSize), pool (p) + ThreadPoolThread (ThreadPool& p, const ThreadPoolOptions& options) + : Thread { options.threadName, options.threadStackSizeBytes }, + pool { p } { } @@ -93,18 +94,24 @@ ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob() } //============================================================================== -ThreadPool::ThreadPool (int numThreads, size_t threadStackSize, Thread::Priority priority) +ThreadPool::ThreadPool (const ThreadPoolOptions& options) { - jassert (numThreads > 0); // not much point having a pool without any threads! + // not much point having a pool without any threads! + jassert (options.numberOfThreads > 0); - for (int i = jmax (1, numThreads); --i >= 0;) - threads.add (new ThreadPoolThread (*this, threadStackSize)); + for (int i = jmax (1, options.numberOfThreads); --i >= 0;) + threads.add (new ThreadPoolThread (*this, options)); for (auto* t : threads) - t->startThread (priority); + t->startThread (options.desiredThreadPriority); } -ThreadPool::ThreadPool() : ThreadPool (SystemStats::getNumCpus(), 0, Thread::Priority::normal) +ThreadPool::ThreadPool (int numberOfThreads, + size_t threadStackSizeBytes, + Thread::Priority desiredThreadPriority) + : ThreadPool { ThreadPoolOptions{}.withNumberOfThreads (numberOfThreads) + .withThreadStackSizeBytes (threadStackSizeBytes) + .withDesiredThreadPriority (desiredThreadPriority) } { } diff --git a/modules/juce_core/threads/juce_ThreadPool.h b/modules/juce_core/threads/juce_ThreadPool.h index 779b1d02fe..c1909e6691 100644 --- a/modules/juce_core/threads/juce_ThreadPool.h +++ b/modules/juce_core/threads/juce_ThreadPool.h @@ -140,6 +140,51 @@ private: 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} +*/ +struct ThreadPoolOptions +{ + /** The name to give each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withThreadName (String newThreadName) const + { + return withMember (*this, &ThreadPoolOptions::threadName, newThreadName); + } + + /** The number of threads to run. + These will be started when a pool is created, and run until the pool is destroyed. + */ + [[nodiscard]] ThreadPoolOptions withNumberOfThreads (int newNumberOfThreads) const + { + return withMember (*this, &ThreadPoolOptions::numberOfThreads, newNumberOfThreads); + } + + /** The size of the stack of each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withThreadStackSizeBytes (size_t newThreadStackSizeBytes) const + { + return withMember (*this, &ThreadPoolOptions::threadStackSizeBytes, newThreadStackSizeBytes); + } + + /** The desired priority of each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withDesiredThreadPriority (Thread::Priority newDesiredThreadPriority) const + { + return withMember (*this, &ThreadPoolOptions::desiredThreadPriority, newDesiredThreadPriority); + } + + String threadName { "Pool" }; + int numberOfThreads { SystemStats::getNumCpus() }; + size_t threadStackSizeBytes { Thread::osDefaultStackSize }; + Thread::Priority desiredThreadPriority { Thread::Priority::normal }; +}; + //============================================================================== /** @@ -156,25 +201,35 @@ class JUCE_API ThreadPool { public: //============================================================================== + /** Creates a thread pool based on the provided options. + Once you've created a pool, you can give it some jobs by calling addJob(). + + @see ThreadPool::ThreadPoolOptions + */ + explicit ThreadPool (const ThreadPoolOptions& options); + + /** Creates a thread pool based using the default arguments provided by + ThreadPoolOptions. + + Once you've created a pool, you can give it some jobs by calling addJob(). + + @see ThreadPoolOptions + */ + ThreadPool() : ThreadPool { ThreadPoolOptions{} } {} + /** 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. - @param priority the desired priority of each thread in the pool. + @param numberOfThreads the number of threads to run. These will be started + immediately, and will run until the pool is deleted. + @param threadStackSizeBytes the size of the stack of each thread. If this value + is zero then the default stack size of the OS will + be used. + @param priority the desired priority of each thread in the pool. */ - ThreadPool (int numberOfThreads, size_t threadStackSize = 0, Thread::Priority priority = Thread::Priority::normal); - - /** 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(); + ThreadPool (int numberOfThreads, + size_t threadStackSizeBytes = Thread::osDefaultStackSize, + Thread::Priority desiredThreadPriority = Thread::Priority::normal); /** Destructor. diff --git a/modules/juce_gui_basics/native/juce_DragAndDrop_windows.cpp b/modules/juce_gui_basics/native/juce_DragAndDrop_windows.cpp index 2243262799..e636f99752 100644 --- a/modules/juce_gui_basics/native/juce_DragAndDrop_windows.cpp +++ b/modules/juce_gui_basics/native/juce_DragAndDrop_windows.cpp @@ -306,7 +306,7 @@ namespace DragAndDropHelpers // We need to make sure we don't do simultaneous text and file drag and drops, // so use a pool that can only run a single job. - ThreadPool pool { 1 }; + ThreadPool pool { ThreadPoolOptions{}.withNumberOfThreads (1) }; }; JUCE_IMPLEMENT_SINGLETON (ThreadPoolHolder)