The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. class ThreadPool;
  20. //==============================================================================
  21. /**
  22. A task that is executed by a ThreadPool object.
  23. A ThreadPool keeps a list of ThreadPoolJob objects which are executed by
  24. its threads.
  25. The runJob() method needs to be implemented to do the task, and if the code that
  26. does the work takes a significant time to run, it must keep checking the shouldExit()
  27. method to see if something is trying to interrupt the job. If shouldExit() returns
  28. true, the runJob() method must return immediately.
  29. @see ThreadPool, Thread
  30. @tags{Core}
  31. */
  32. class JUCE_API ThreadPoolJob
  33. {
  34. public:
  35. //==============================================================================
  36. /** Creates a thread pool job object.
  37. After creating your job, add it to a thread pool with ThreadPool::addJob().
  38. */
  39. explicit ThreadPoolJob (const String& name);
  40. /** Destructor. */
  41. virtual ~ThreadPoolJob();
  42. //==============================================================================
  43. /** Returns the name of this job.
  44. @see setJobName
  45. */
  46. String getJobName() const;
  47. /** Changes the job's name.
  48. @see getJobName
  49. */
  50. void setJobName (const String& newName);
  51. //==============================================================================
  52. /** These are the values that can be returned by the runJob() method.
  53. */
  54. enum JobStatus
  55. {
  56. jobHasFinished = 0, /**< indicates that the job has finished and can be
  57. removed from the pool. */
  58. jobNeedsRunningAgain /**< indicates that the job would like to be called
  59. again when a thread is free. */
  60. };
  61. /** Performs the actual work that this job needs to do.
  62. Your subclass must implement this method, in which is does its work.
  63. If the code in this method takes a significant time to run, it must repeatedly check
  64. the shouldExit() method to see if something is trying to interrupt the job.
  65. If shouldExit() ever returns true, the runJob() method must return immediately.
  66. If this method returns jobHasFinished, then the job will be removed from the pool
  67. immediately. If it returns jobNeedsRunningAgain, then the job will be left in the
  68. pool and will get a chance to run again as soon as a thread is free.
  69. @see shouldExit()
  70. */
  71. virtual JobStatus runJob() = 0;
  72. //==============================================================================
  73. /** Returns true if this job is currently running its runJob() method. */
  74. bool isRunning() const noexcept { return isActive; }
  75. /** Returns true if something is trying to interrupt this job and make it stop.
  76. Your runJob() method must call this whenever it gets a chance, and if it ever
  77. returns true, the runJob() method must return immediately.
  78. @see signalJobShouldExit()
  79. */
  80. bool shouldExit() const noexcept { return shouldStop; }
  81. /** Calling this will cause the shouldExit() method to return true, and the job
  82. should (if it's been implemented correctly) stop as soon as possible.
  83. @see shouldExit()
  84. */
  85. void signalJobShouldExit();
  86. /** Add a listener to this thread job which will receive a callback when
  87. signalJobShouldExit was called on this thread job.
  88. @see signalJobShouldExit, removeListener
  89. */
  90. void addListener (Thread::Listener*);
  91. /** Removes a listener added with addListener. */
  92. void removeListener (Thread::Listener*);
  93. //==============================================================================
  94. /** If the calling thread is being invoked inside a runJob() method, this will
  95. return the ThreadPoolJob that it belongs to.
  96. */
  97. static ThreadPoolJob* getCurrentThreadPoolJob();
  98. //==============================================================================
  99. private:
  100. friend class ThreadPool;
  101. String jobName;
  102. ThreadPool* pool = nullptr;
  103. std::atomic<bool> shouldStop { false }, isActive { false }, shouldBeDeleted { false };
  104. ListenerList<Thread::Listener, Array<Thread::Listener*, CriticalSection>> listeners;
  105. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob)
  106. };
  107. //==============================================================================
  108. /**
  109. A set of threads that will run a list of jobs.
  110. When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method
  111. will be called by the next pooled thread that becomes free.
  112. @see ThreadPoolJob, Thread
  113. @tags{Core}
  114. */
  115. struct ThreadPoolOptions
  116. {
  117. /** The name to give each thread in the pool. */
  118. [[nodiscard]] ThreadPoolOptions withThreadName (String newThreadName) const
  119. {
  120. return withMember (*this, &ThreadPoolOptions::threadName, newThreadName);
  121. }
  122. /** The number of threads to run.
  123. These will be started when a pool is created, and run until the pool is destroyed.
  124. */
  125. [[nodiscard]] ThreadPoolOptions withNumberOfThreads (int newNumberOfThreads) const
  126. {
  127. return withMember (*this, &ThreadPoolOptions::numberOfThreads, newNumberOfThreads);
  128. }
  129. /** The size of the stack of each thread in the pool. */
  130. [[nodiscard]] ThreadPoolOptions withThreadStackSizeBytes (size_t newThreadStackSizeBytes) const
  131. {
  132. return withMember (*this, &ThreadPoolOptions::threadStackSizeBytes, newThreadStackSizeBytes);
  133. }
  134. /** The desired priority of each thread in the pool. */
  135. [[nodiscard]] ThreadPoolOptions withDesiredThreadPriority (Thread::Priority newDesiredThreadPriority) const
  136. {
  137. return withMember (*this, &ThreadPoolOptions::desiredThreadPriority, newDesiredThreadPriority);
  138. }
  139. String threadName { "Pool" };
  140. int numberOfThreads { SystemStats::getNumCpus() };
  141. size_t threadStackSizeBytes { Thread::osDefaultStackSize };
  142. Thread::Priority desiredThreadPriority { Thread::Priority::normal };
  143. };
  144. //==============================================================================
  145. /**
  146. A set of threads that will run a list of jobs.
  147. When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method
  148. will be called by the next pooled thread that becomes free.
  149. @see ThreadPoolJob, Thread
  150. @tags{Core}
  151. */
  152. class JUCE_API ThreadPool
  153. {
  154. public:
  155. using Options = ThreadPoolOptions;
  156. //==============================================================================
  157. /** Creates a thread pool based on the provided options.
  158. Once you've created a pool, you can give it some jobs by calling addJob().
  159. @see ThreadPool::ThreadPoolOptions
  160. */
  161. explicit ThreadPool (const Options& options);
  162. /** Creates a thread pool based using the default arguments provided by
  163. ThreadPoolOptions.
  164. Once you've created a pool, you can give it some jobs by calling addJob().
  165. @see ThreadPoolOptions
  166. */
  167. ThreadPool() : ThreadPool { Options{} } {}
  168. /** Creates a thread pool.
  169. Once you've created a pool, you can give it some jobs by calling addJob().
  170. @param numberOfThreads the number of threads to run. These will be started
  171. immediately, and will run until the pool is deleted.
  172. @param threadStackSizeBytes the size of the stack of each thread. If this value
  173. is zero then the default stack size of the OS will
  174. be used.
  175. @param desiredThreadPriority the desired priority of each thread in the pool.
  176. */
  177. ThreadPool (int numberOfThreads,
  178. size_t threadStackSizeBytes = Thread::osDefaultStackSize,
  179. Thread::Priority desiredThreadPriority = Thread::Priority::normal);
  180. /** Destructor.
  181. This will attempt to remove all the jobs before deleting, but if you want to
  182. specify a timeout, you should call removeAllJobs() explicitly before deleting
  183. the pool.
  184. */
  185. ~ThreadPool();
  186. //==============================================================================
  187. /** A callback class used when you need to select which ThreadPoolJob objects are suitable
  188. for some kind of operation.
  189. @see ThreadPool::removeAllJobs
  190. */
  191. class JUCE_API JobSelector
  192. {
  193. public:
  194. virtual ~JobSelector() = default;
  195. /** Should return true if the specified thread matches your criteria for whatever
  196. operation that this object is being used for.
  197. Any implementation of this method must be extremely fast and thread-safe!
  198. */
  199. virtual bool isJobSuitable (ThreadPoolJob* job) = 0;
  200. };
  201. //==============================================================================
  202. /** Adds a job to the queue.
  203. Once a job has been added, then the next time a thread is free, it will run
  204. the job's ThreadPoolJob::runJob() method. Depending on the return value of the
  205. runJob() method, the pool will either remove the job from the pool or add it to
  206. the back of the queue to be run again.
  207. If deleteJobWhenFinished is true, then the job object will be owned and deleted by
  208. the pool when not needed - if you do this, make sure that your object's destructor
  209. is thread-safe.
  210. If deleteJobWhenFinished is false, the pointer will be used but not deleted, and
  211. the caller is responsible for making sure the object is not deleted before it has
  212. been removed from the pool.
  213. */
  214. void addJob (ThreadPoolJob* job,
  215. bool deleteJobWhenFinished);
  216. /** Adds a lambda function to be called as a job.
  217. This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
  218. */
  219. void addJob (std::function<ThreadPoolJob::JobStatus()> job);
  220. /** Adds a lambda function to be called as a job.
  221. This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
  222. */
  223. void addJob (std::function<void()> job);
  224. /** Tries to remove a job from the pool.
  225. If the job isn't yet running, this will simply remove it. If it is running, it
  226. will wait for it to finish.
  227. If the timeout period expires before the job finishes running, then the job will be
  228. left in the pool and this will return false. It returns true if the job is successfully
  229. stopped and removed.
  230. @param job the job to remove
  231. @param interruptIfRunning if true, then if the job is currently busy, its
  232. ThreadPoolJob::signalJobShouldExit() method will be called to try
  233. to interrupt it. If false, then if the job will be allowed to run
  234. until it stops normally (or the timeout expires)
  235. @param timeOutMilliseconds the length of time this method should wait for the job to finish
  236. before giving up and returning false
  237. */
  238. bool removeJob (ThreadPoolJob* job,
  239. bool interruptIfRunning,
  240. int timeOutMilliseconds);
  241. /** Tries to remove all jobs from the pool.
  242. @param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit()
  243. methods called to try to interrupt them
  244. @param timeOutMilliseconds the length of time this method should wait for all the jobs to finish
  245. before giving up and returning false
  246. @param selectedJobsToRemove if this is not a nullptr, the JobSelector object is asked to decide
  247. which jobs should be removed. If it is a nullptr, all jobs are removed
  248. @returns true if all jobs are successfully stopped and removed; false if the timeout period
  249. expires while waiting for one or more jobs to stop
  250. */
  251. bool removeAllJobs (bool interruptRunningJobs,
  252. int timeOutMilliseconds,
  253. JobSelector* selectedJobsToRemove = nullptr);
  254. /** Returns the number of jobs currently running or queued. */
  255. int getNumJobs() const noexcept;
  256. /** Returns the number of threads assigned to this thread pool. */
  257. int getNumThreads() const noexcept;
  258. /** Returns one of the jobs in the queue.
  259. Note that this can be a very volatile list as jobs might be continuously getting shifted
  260. around in the list, and this method may return nullptr if the index is currently out-of-range.
  261. */
  262. ThreadPoolJob* getJob (int index) const noexcept;
  263. /** Returns true if the given job is currently queued or running.
  264. @see isJobRunning()
  265. */
  266. bool contains (const ThreadPoolJob* job) const noexcept;
  267. /** Returns true if the given job is currently being run by a thread. */
  268. bool isJobRunning (const ThreadPoolJob* job) const noexcept;
  269. /** Waits until a job has finished running and has been removed from the pool.
  270. This will wait until the job is no longer in the pool - i.e. until its
  271. runJob() method returns ThreadPoolJob::jobHasFinished.
  272. If the timeout period expires before the job finishes, this will return false;
  273. it returns true if the job has finished successfully.
  274. */
  275. bool waitForJobToFinish (const ThreadPoolJob* job,
  276. int timeOutMilliseconds) const;
  277. /** If the given job is in the queue, this will move it to the front so that it
  278. is the next one to be executed.
  279. */
  280. void moveJobToFront (const ThreadPoolJob* jobToMove) noexcept;
  281. /** Returns a list of the names of all the jobs currently running or queued.
  282. If onlyReturnActiveJobs is true, only the ones currently running are returned.
  283. */
  284. StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
  285. private:
  286. //==============================================================================
  287. Array<ThreadPoolJob*> jobs;
  288. struct ThreadPoolThread;
  289. friend class ThreadPoolJob;
  290. OwnedArray<ThreadPoolThread> threads;
  291. CriticalSection lock;
  292. WaitableEvent jobFinishedSignal;
  293. bool runNextJob (ThreadPoolThread&);
  294. ThreadPoolJob* pickNextJobToRun();
  295. void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
  296. void stopThreads();
  297. // Note that this method has changed, and no longer has a parameter to indicate
  298. // whether the jobs should be deleted - see the new method for details.
  299. void removeAllJobs (bool, int, bool);
  300. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPool)
  301. };
  302. } // namespace juce