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.

222 lines
7.4KB

  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. //==============================================================================
  20. /**
  21. Created by AudioWorkgroup to join the calling thread to a workgroup.
  22. To leave the workgroup again, destroy the WorkgroupToken.
  23. @see AudioWorkgroup
  24. @tags{Audio}
  25. */
  26. class WorkgroupToken
  27. {
  28. public:
  29. /** @internal */
  30. class TokenProvider;
  31. /** @internal */
  32. using Erased = FixedSizeFunction<64, const TokenProvider*()>;
  33. /** @internal
  34. Creates a WorkgroupToken from a function returning a TokenProvider.
  35. */
  36. explicit WorkgroupToken (Erased e) : erased (std::move (e)) {}
  37. /** @internal
  38. Creates a disengaged WorkgroupToken, i.e. create a token without joining the thread to a
  39. workgroup.
  40. */
  41. WorkgroupToken() = default;
  42. /** If the token joined the calling thread to a workgroup during construction, the destructor
  43. will cause the calling thread to leave that workgroup.
  44. */
  45. ~WorkgroupToken() = default;
  46. /** @internal */
  47. WorkgroupToken (const WorkgroupToken&) = delete;
  48. WorkgroupToken (WorkgroupToken&&) noexcept = default;
  49. /** @internal */
  50. WorkgroupToken& operator= (const WorkgroupToken&) = delete;
  51. WorkgroupToken& operator= (WorkgroupToken&&) = default;
  52. /** Returns true if and only if getTokenProvider() returns non-null. */
  53. explicit operator bool() const { return getTokenProvider() != nullptr; }
  54. /** The result of this function can be compared to nullptr to check whether the token
  55. successfully joined the calling thread to a workgroup.
  56. Used in the implementation to provide platform-specific information about this token.
  57. */
  58. [[nodiscard]] const TokenProvider* getTokenProvider() const { return erased != nullptr ? erased() : nullptr; }
  59. /** If this token was engaged by joining a workgroup, leaves that workgroup and disengages the token.
  60. After this call, getTokenProvider() will return nullptr.
  61. */
  62. void reset() { erased = nullptr; }
  63. private:
  64. Erased erased;
  65. };
  66. //==============================================================================
  67. /**
  68. A handle to an audio workgroup, which is a collection of realtime threads
  69. working together to produce audio by a common deadline.
  70. You can use this class to join a real-time worker thread to a workgroup.
  71. Rather than constructing instances of this class directly, you should use
  72. functions like AudioProcessor::audioWorkgroupContextChanged() and
  73. AudioIODevice::getWorkgroup() to fetch an engaged workgroup from the system.
  74. The class contains a single method, join(). Call this from your real-time
  75. thread to with register this workgroup.
  76. Here's an example of how you might use this class:
  77. @code
  78. Constructor()
  79. {
  80. startRealtimeThread (RealtimeThreadOptions{}.withApproximateAudioProcessingTime (samplesPerFrame, sampleRate));
  81. or
  82. startRealtimeThread (RealtimeThreadOptions{}.withProcessingTimeMs (10));
  83. }
  84. void Thread::run() override
  85. {
  86. WorkgroupToken token;
  87. getWorkgroup().join (token);
  88. while (wait (-1) && ! threadShouldExit())
  89. {
  90. // If the workgroup has changed, rejoin the workgroup with the same token.
  91. if (workgroupChanged())
  92. getWorkgroup().join (token);
  93. // Perform the work here
  94. }
  95. }
  96. void AudioProcessor::processBlock()
  97. {
  98. workerThread->notify();
  99. }
  100. @endcode
  101. @see Thread, AudioProcessor, WorkgroupToken
  102. @tags{Audio}
  103. */
  104. class AudioWorkgroup
  105. {
  106. public:
  107. /** @internal */
  108. class WorkgroupProvider;
  109. /** @internal */
  110. using Erased = FixedSizeFunction<64, const WorkgroupProvider*()>;
  111. /** @internal
  112. Creates an AudioWorkgroup from a function returning a WorkgroupProvider.
  113. */
  114. explicit AudioWorkgroup (Erased e) : erased (std::move (e)) {}
  115. /** Move constructor. */
  116. AudioWorkgroup (AudioWorkgroup&&) = default;
  117. /** Move assignment operator. */
  118. AudioWorkgroup& operator= (AudioWorkgroup&&) = default;
  119. /** Copy constructor. */
  120. AudioWorkgroup (const AudioWorkgroup&);
  121. /** Copy assignment operator. */
  122. AudioWorkgroup& operator= (const AudioWorkgroup& other)
  123. {
  124. AudioWorkgroup { other }.swap (*this);
  125. return *this;
  126. }
  127. /** Constructs a disengaged handle that does not represent any workgroup. */
  128. AudioWorkgroup() = default;
  129. /**
  130. This method attempts to join the calling thread to this workgroup.
  131. If the join operation is successful, the token will be engaged, i.e. its
  132. getTokenProvider() function will return non-null.
  133. If the token is already engaged and represents a join to another workgroup,
  134. the thread will leave that workgroup before joining the workgroup represented by this
  135. object. If the 'token' is already engaged and is passed to the same workgroup, the method
  136. will not perform any action.
  137. It's important to note that the lifetime of the token should not exceed the lifetime
  138. of the associated thread and must be destroyed on the same thread.
  139. */
  140. void join (WorkgroupToken& token) const;
  141. /** Equality operator. */
  142. bool operator== (const AudioWorkgroup& other) const;
  143. /** Inequality operator. */
  144. bool operator!= (const AudioWorkgroup& other) const { return ! operator== (other); }
  145. /** Returns true if and only if this object represents a workgroup. */
  146. explicit operator bool() const;
  147. /** Disengages this instance so that it no longer represents a workgroup.
  148. After this call, operator bool() will return false.
  149. */
  150. void reset() { erased = nullptr; }
  151. /** Returns the recommended maximum number of parallel threads that should join this workgroup.
  152. This recommendation is based on the workgroup attributes and current hardware, but not on
  153. system load. On a very busy system, it may be more effective to use fewer parallel threads.
  154. */
  155. size_t getMaxParallelThreadCount() const;
  156. private:
  157. const WorkgroupProvider* getWorkgroupProvider() const { return erased != nullptr ? erased() : nullptr; }
  158. void swap (AudioWorkgroup& other) noexcept { std::swap (other.erased, erased); }
  159. Erased erased;
  160. };
  161. } // namespace juce