Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2026 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "audioworkgroup.h"
24 :
25 : #include <memory>
26 : #include <utility>
27 :
28 : #ifdef __APPLE__
29 : #include <os/object.h>
30 : #include <os/workgroup.h>
31 : #else
32 : #include <thread>
33 : #endif
34 :
35 : namespace muse::audio {
36 : #ifdef __APPLE__
37 : class AudioWorkgroupTokenProvider
38 : {
39 : public:
40 : AudioWorkgroupTokenProvider(os_workgroup_t workgroup)
41 : : m_workgroup(workgroup)
42 : {
43 : if (!m_workgroup) {
44 : return;
45 : }
46 :
47 : if (__builtin_available(macOS 11.0, *)) {
48 : os_retain(m_workgroup);
49 : auto status = os_workgroup_join(m_workgroup, &m_joinToken);
50 : if (status == 0) {
51 : return;
52 : }
53 :
54 : os_release(m_workgroup);
55 : m_workgroup = nullptr;
56 : m_joinToken = {};
57 : return;
58 : }
59 :
60 : m_workgroup = nullptr;
61 : }
62 :
63 : bool isAttachedTo(os_workgroup_t wg) const { return m_workgroup == wg; }
64 : bool isValid() const { return m_workgroup != nullptr; }
65 :
66 : AudioWorkgroupTokenProvider(const AudioWorkgroupTokenProvider& other) = delete;
67 :
68 : AudioWorkgroupTokenProvider& operator=(const AudioWorkgroupTokenProvider&) = delete;
69 :
70 : AudioWorkgroupTokenProvider(AudioWorkgroupTokenProvider&& other) noexcept
71 : : m_workgroup(other.m_workgroup), m_joinToken(other.m_joinToken)
72 : {
73 : other.m_workgroup = nullptr;
74 : other.m_joinToken = {};
75 : }
76 :
77 : ~AudioWorkgroupTokenProvider()
78 : {
79 : leave();
80 : }
81 :
82 : private:
83 : void leave() noexcept
84 : {
85 : if (!m_workgroup) {
86 : return;
87 : }
88 :
89 : if (__builtin_available(macOS 11.0, *)) {
90 : os_workgroup_leave(m_workgroup, &m_joinToken);
91 : }
92 : os_release(m_workgroup);
93 : m_workgroup = nullptr;
94 : m_joinToken = {};
95 : }
96 :
97 : os_workgroup_t m_workgroup;
98 : os_workgroup_join_token_s m_joinToken;
99 : };
100 :
101 : class AudioWorkgroupProvider
102 : {
103 : public:
104 : explicit AudioWorkgroupProvider(os_workgroup_t wg)
105 : : m_workgroup(wg)
106 : {
107 : os_retain(m_workgroup);
108 : }
109 :
110 : ~AudioWorkgroupProvider()
111 : {
112 : if (m_workgroup) {
113 : os_release(m_workgroup);
114 : }
115 : }
116 :
117 : AudioWorkgroupProvider(const AudioWorkgroupProvider& other)
118 : : m_workgroup(other.m_workgroup)
119 : {
120 : os_retain(m_workgroup);
121 : }
122 :
123 : AudioWorkgroupProvider& operator=(const AudioWorkgroupProvider& other)
124 : {
125 : if (this != &other) {
126 : os_retain(other.m_workgroup);
127 : if (m_workgroup) {
128 : os_release(m_workgroup);
129 : }
130 : m_workgroup = other.m_workgroup;
131 : }
132 : return *this;
133 : }
134 :
135 : AudioWorkgroupProvider(AudioWorkgroupProvider&& other) noexcept
136 : : m_workgroup(other.m_workgroup)
137 : {
138 : other.m_workgroup = nullptr;
139 : }
140 :
141 : bool join(AudioWorkgroupToken& tokenProvider) const
142 : {
143 : if (auto existingProvider = AudioWorkGroup::providerFor(tokenProvider);
144 : existingProvider != nullptr && existingProvider->isAttachedTo(m_workgroup)) {
145 : return true;
146 : }
147 : AudioWorkGroup::resetProviderFor(tokenProvider);
148 : AudioWorkgroupTokenProvider provider(m_workgroup);
149 : if (!provider.isValid()) {
150 : return false;
151 : }
152 :
153 : AudioWorkGroup::setProviderFor(tokenProvider, [provider = std::move(provider)]() {
154 : return &provider;
155 : });
156 : return true;
157 : }
158 :
159 : size_t getMaxParallelThreadCount() const
160 : {
161 : if (__builtin_available(macOS 11.0, *)) {
162 : return (size_t)os_workgroup_max_parallel_threads(m_workgroup, nullptr);
163 : }
164 : return 0;
165 : }
166 :
167 : private:
168 :
169 : os_workgroup_t m_workgroup;
170 : };
171 :
172 : bool AudioWorkGroup::join(AudioWorkgroupToken& token)
173 : {
174 : auto provider = getProvider();
175 : if (!provider) {
176 : return false;
177 : }
178 : return provider->join(token);
179 : }
180 :
181 : AudioWorkGroup makeAudioWorkgroup(void* opaqueHandle)
182 : {
183 : if (opaqueHandle == nullptr) {
184 : return {};
185 : }
186 :
187 : os_workgroup_t handle = reinterpret_cast<os_workgroup_t>(opaqueHandle);
188 :
189 : return AudioWorkGroup { std::make_unique<AudioWorkgroupProvider>(handle) };
190 : }
191 :
192 : size_t AudioWorkGroup::getMaxParallelThreadCount() const
193 : {
194 : if (auto provider = getProvider(); provider) {
195 : return provider->getMaxParallelThreadCount();
196 : }
197 : return 0;
198 : }
199 :
200 : #else
201 :
202 : class AudioWorkgroupProvider
203 : {
204 : };
205 :
206 0 : size_t AudioWorkGroup::getMaxParallelThreadCount() const
207 : {
208 0 : return std::thread::hardware_concurrency();
209 : }
210 :
211 47 : bool AudioWorkGroup::join(AudioWorkgroupToken&)
212 : {
213 47 : return false;
214 : }
215 :
216 0 : AudioWorkGroup makeAudioWorkgroup(void*)
217 : {
218 0 : return {};
219 : }
220 :
221 : #endif
222 :
223 99 : AudioWorkGroup::AudioWorkGroup() = default;
224 :
225 0 : AudioWorkGroup::AudioWorkGroup(std::unique_ptr<AudioWorkgroupProvider> provider)
226 0 : : m_provider(std::move(provider)) {}
227 :
228 153 : AudioWorkGroup::~AudioWorkGroup() = default;
229 :
230 54 : AudioWorkGroup::AudioWorkGroup(const AudioWorkGroup& other)
231 54 : : m_provider(other.m_provider ? std::make_unique<AudioWorkgroupProvider>(*other.m_provider) : nullptr) {}
232 :
233 0 : AudioWorkGroup::AudioWorkGroup(AudioWorkGroup&& other) noexcept = default;
234 :
235 54 : AudioWorkGroup& AudioWorkGroup::operator=(const AudioWorkGroup& other)
236 : {
237 54 : if (this != &other) {
238 54 : m_provider = other.m_provider ? std::make_unique<AudioWorkgroupProvider>(*other.m_provider) : nullptr;
239 : }
240 54 : return *this;
241 : }
242 :
243 0 : AudioWorkGroup& AudioWorkGroup::operator=(AudioWorkGroup&& other) noexcept = default;
244 : } // namespace muse::audio
|