Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "SoundPool::StreamManager" |
| 19 | #include <utils/Log.h> |
| 20 | |
| 21 | #include "StreamManager.h" |
| 22 | |
| 23 | #include <audio_utils/clock.h> |
| 24 | #include <audio_utils/roundup.h> |
| 25 | |
| 26 | namespace android::soundpool { |
| 27 | |
| 28 | // kMaxStreams is number that should be less than the current AudioTrack max per UID of 40. |
| 29 | // It is the maximum number of AudioTrack resources allowed in the SoundPool. |
| 30 | // We suggest a value at least 4 or greater to allow CTS tests to pass. |
| 31 | static constexpr int32_t kMaxStreams = 32; |
| 32 | |
| 33 | // kStealActiveStream_OldestFirst = false historically (Q and earlier) |
| 34 | // Changing to true could break app expectations but could change behavior beneficially. |
| 35 | // In R, we change this to true, as it is the correct way per SoundPool documentation. |
| 36 | static constexpr bool kStealActiveStream_OldestFirst = true; |
| 37 | |
| 38 | // kPlayOnCallingThread = true prior to R. |
| 39 | // Changing to false means calls to play() are almost instantaneous instead of taking around |
| 40 | // ~10ms to launch the AudioTrack. It is perhaps 100x faster. |
Andy Hung | 8823c98 | 2019-12-12 19:43:12 +0000 | [diff] [blame] | 41 | static constexpr bool kPlayOnCallingThread = true; |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 42 | |
| 43 | // Amount of time for a StreamManager thread to wait before closing. |
| 44 | static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND; |
| 45 | |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 46 | // Debug flag: |
| 47 | // kForceLockStreamManagerStop is set to true to force lock the StreamManager |
| 48 | // worker thread during stop. This limits concurrency of Stream processing. |
| 49 | // Normally we lock the StreamManager worker thread during stop ONLY |
| 50 | // for SoundPools configured with a single Stream. |
| 51 | // |
| 52 | static constexpr bool kForceLockStreamManagerStop = false; |
| 53 | |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 54 | //////////// |
| 55 | |
| 56 | StreamMap::StreamMap(int32_t streams) { |
| 57 | ALOGV("%s(%d)", __func__, streams); |
| 58 | if (streams > kMaxStreams) { |
| 59 | ALOGW("%s: requested %d streams, clamping to %d", __func__, streams, kMaxStreams); |
| 60 | streams = kMaxStreams; |
| 61 | } else if (streams < 1) { |
| 62 | ALOGW("%s: requested %d streams, clamping to 1", __func__, streams); |
| 63 | streams = 1; |
| 64 | } |
| 65 | mStreamPoolSize = streams * 2; |
Andy Hung | 77eb2bd | 2020-05-19 10:42:09 -0700 | [diff] [blame] | 66 | mStreamPool = std::make_unique<Stream[]>(mStreamPoolSize); // create array of streams. |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 67 | // we use a perfect hash table with 2x size to map StreamIDs to Stream pointers. |
| 68 | mPerfectHash = std::make_unique<PerfectHash<int32_t, Stream *>>(roundup(mStreamPoolSize * 2)); |
| 69 | } |
| 70 | |
| 71 | Stream* StreamMap::findStream(int32_t streamID) const |
| 72 | { |
| 73 | Stream *stream = lookupStreamFromId(streamID); |
| 74 | return stream != nullptr && stream->getStreamID() == streamID ? stream : nullptr; |
| 75 | } |
| 76 | |
| 77 | size_t StreamMap::streamPosition(const Stream* stream) const |
| 78 | { |
| 79 | ptrdiff_t index = stream - mStreamPool.get(); |
Andy Hung | 77eb2bd | 2020-05-19 10:42:09 -0700 | [diff] [blame] | 80 | LOG_ALWAYS_FATAL_IF(index < 0 || (size_t)index >= mStreamPoolSize, |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 81 | "%s: stream position out of range: %td", __func__, index); |
| 82 | return (size_t)index; |
| 83 | } |
| 84 | |
| 85 | Stream* StreamMap::lookupStreamFromId(int32_t streamID) const |
| 86 | { |
| 87 | return streamID > 0 ? mPerfectHash->getValue(streamID).load() : nullptr; |
| 88 | } |
| 89 | |
| 90 | int32_t StreamMap::getNextIdForStream(Stream* stream) const { |
| 91 | // even though it is const, it mutates the internal hash table. |
| 92 | const int32_t id = mPerfectHash->generateKey( |
| 93 | stream, |
| 94 | [] (Stream *stream) { |
| 95 | return stream == nullptr ? 0 : stream->getStreamID(); |
| 96 | }, /* getKforV() */ |
| 97 | stream->getStreamID() /* oldID */); |
| 98 | return id; |
| 99 | } |
| 100 | |
| 101 | //////////// |
| 102 | |
Andy Hung | 77eb2bd | 2020-05-19 10:42:09 -0700 | [diff] [blame] | 103 | // Thread safety analysis is supposed to be disabled for constructors and destructors |
| 104 | // but clang in R seems to have a bug. We use pragma to disable. |
| 105 | #pragma clang diagnostic push |
| 106 | #pragma clang diagnostic ignored "-Wthread-safety-analysis" |
| 107 | |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 108 | StreamManager::StreamManager( |
jiabin | 181d26b | 2020-12-14 21:13:30 -0800 | [diff] [blame] | 109 | int32_t streams, size_t threads, const audio_attributes_t* attributes, |
| 110 | std::string opPackageName) |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 111 | : StreamMap(streams) |
| 112 | , mAttributes(*attributes) |
jiabin | 181d26b | 2020-12-14 21:13:30 -0800 | [diff] [blame] | 113 | , mOpPackageName(std::move(opPackageName)) |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 114 | , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop) |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 115 | { |
| 116 | ALOGV("%s(%d, %zu, ...)", __func__, streams, threads); |
| 117 | forEach([this](Stream *stream) { |
| 118 | stream->setStreamManager(this); |
| 119 | if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available. |
| 120 | mAvailableStreams.insert(stream); |
| 121 | } |
| 122 | }); |
| 123 | |
| 124 | mThreadPool = std::make_unique<ThreadPool>( |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 125 | std::min((size_t)streams, // do not make more threads than streams to play |
| 126 | std::min(threads, (size_t)std::thread::hardware_concurrency())), |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 127 | "SoundPool_"); |
| 128 | } |
| 129 | |
Andy Hung | 77eb2bd | 2020-05-19 10:42:09 -0700 | [diff] [blame] | 130 | #pragma clang diagnostic pop |
| 131 | |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 132 | StreamManager::~StreamManager() |
| 133 | { |
| 134 | ALOGV("%s", __func__); |
| 135 | { |
| 136 | std::unique_lock lock(mStreamManagerLock); |
| 137 | mQuit = true; |
| 138 | mStreamManagerCondition.notify_all(); |
| 139 | } |
| 140 | mThreadPool->quit(); |
| 141 | |
| 142 | // call stop on the stream pool |
| 143 | forEach([](Stream *stream) { stream->stop(); }); |
| 144 | |
| 145 | // This invokes the destructor on the AudioTracks - |
| 146 | // we do it here to ensure that AudioTrack callbacks will not occur |
| 147 | // afterwards. |
| 148 | forEach([](Stream *stream) { stream->clearAudioTrack(); }); |
| 149 | } |
| 150 | |
| 151 | |
| 152 | int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound, |
| 153 | int32_t soundID, float leftVolume, float rightVolume, |
| 154 | int32_t priority, int32_t loop, float rate) |
| 155 | { |
| 156 | ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)", |
| 157 | __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate); |
| 158 | bool launchThread = false; |
| 159 | int32_t streamID = 0; |
| 160 | |
| 161 | { // for lock |
| 162 | std::unique_lock lock(mStreamManagerLock); |
| 163 | Stream *newStream = nullptr; |
| 164 | bool fromAvailableQueue = false; |
| 165 | ALOGV("%s: mStreamManagerLock lock acquired", __func__); |
| 166 | |
| 167 | sanityCheckQueue_l(); |
| 168 | // find an available stream, prefer one that has matching sound id. |
| 169 | if (mAvailableStreams.size() > 0) { |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 170 | for (auto stream : mAvailableStreams) { |
| 171 | if (stream->getSoundID() == soundID) { |
| 172 | newStream = stream; |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 173 | ALOGV("%s: found soundID %d in available queue", __func__, soundID); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 174 | break; |
| 175 | } |
| 176 | } |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 177 | if (newStream == nullptr) { |
| 178 | ALOGV("%s: found stream in available queue", __func__); |
| 179 | newStream = *mAvailableStreams.begin(); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 180 | } |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 181 | newStream->setStopTimeNs(systemTime()); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 182 | fromAvailableQueue = true; |
| 183 | } |
| 184 | |
| 185 | // also look in the streams restarting (if the paired stream doesn't have a pending play) |
| 186 | if (newStream == nullptr || newStream->getSoundID() != soundID) { |
| 187 | for (auto [unused , stream] : mRestartStreams) { |
| 188 | if (!stream->getPairStream()->hasSound()) { |
| 189 | if (stream->getSoundID() == soundID) { |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 190 | ALOGV("%s: found soundID %d in restart queue", __func__, soundID); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 191 | newStream = stream; |
Andy Hung | 8823c98 | 2019-12-12 19:43:12 +0000 | [diff] [blame] | 192 | fromAvailableQueue = false; |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 193 | break; |
| 194 | } else if (newStream == nullptr) { |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 195 | ALOGV("%s: found stream in restart queue", __func__); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 196 | newStream = stream; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // no available streams, look for one to steal from the active list |
| 203 | if (newStream == nullptr) { |
| 204 | for (auto stream : mActiveStreams) { |
| 205 | if (stream->getPriority() <= priority) { |
| 206 | if (newStream == nullptr |
| 207 | || newStream->getPriority() > stream->getPriority()) { |
| 208 | newStream = stream; |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 209 | ALOGV("%s: found stream in active queue", __func__); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | } |
| 213 | if (newStream != nullptr) { // we need to mute as it is still playing. |
| 214 | (void)newStream->requestStop(newStream->getStreamID()); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | // none found, look for a stream that is restarting, evict one. |
| 219 | if (newStream == nullptr) { |
| 220 | for (auto [unused, stream] : mRestartStreams) { |
| 221 | if (stream->getPairPriority() <= priority) { |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 222 | ALOGV("%s: evict stream from restart queue", __func__); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 223 | newStream = stream; |
| 224 | break; |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | // DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads. |
| 230 | |
| 231 | if (newStream == nullptr) { |
| 232 | ALOGD("%s: unable to find stream, returning 0", __func__); |
| 233 | return 0; // unable to find available stream |
| 234 | } |
| 235 | |
| 236 | Stream *pairStream = newStream->getPairStream(); |
| 237 | streamID = getNextIdForStream(pairStream); |
Andy Hung | 457ed3a | 2019-11-19 16:44:13 -0800 | [diff] [blame] | 238 | ALOGV("%s: newStream:%p pairStream:%p, streamID:%d", |
| 239 | __func__, newStream, pairStream, streamID); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 240 | pairStream->setPlay( |
| 241 | streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate); |
| 242 | if (fromAvailableQueue && kPlayOnCallingThread) { |
| 243 | removeFromQueues_l(newStream); |
| 244 | mProcessingStreams.emplace(newStream); |
| 245 | lock.unlock(); |
| 246 | if (Stream* nextStream = newStream->playPairStream()) { |
| 247 | lock.lock(); |
| 248 | ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID()); |
| 249 | addToActiveQueue_l(nextStream); |
| 250 | } else { |
| 251 | lock.lock(); |
| 252 | mAvailableStreams.insert(newStream); |
| 253 | streamID = 0; |
| 254 | } |
| 255 | mProcessingStreams.erase(newStream); |
| 256 | } else { |
| 257 | launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l(); |
| 258 | } |
| 259 | sanityCheckQueue_l(); |
| 260 | ALOGV("%s: mStreamManagerLock released", __func__); |
| 261 | } // lock |
| 262 | |
| 263 | if (launchThread) { |
Andy Hung | ce8e6da | 2020-06-01 09:46:03 -0700 | [diff] [blame] | 264 | const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); }); |
| 265 | (void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 266 | ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id); |
| 267 | } |
| 268 | ALOGV("%s: returning %d", __func__, streamID); |
| 269 | return streamID; |
| 270 | } |
| 271 | |
| 272 | void StreamManager::moveToRestartQueue( |
| 273 | Stream* stream, int32_t activeStreamIDToMatch) |
| 274 | { |
| 275 | ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)", |
| 276 | __func__, stream->getStreamID(), activeStreamIDToMatch); |
| 277 | bool restart; |
| 278 | { |
| 279 | std::lock_guard lock(mStreamManagerLock); |
| 280 | sanityCheckQueue_l(); |
| 281 | if (mProcessingStreams.count(stream) > 0 || |
| 282 | mProcessingStreams.count(stream->getPairStream()) > 0) { |
| 283 | ALOGD("%s: attempting to restart processing stream(%d)", |
| 284 | __func__, stream->getStreamID()); |
| 285 | restart = false; |
| 286 | } else { |
| 287 | moveToRestartQueue_l(stream, activeStreamIDToMatch); |
| 288 | restart = needMoreThreads_l(); |
| 289 | } |
| 290 | sanityCheckQueue_l(); |
| 291 | } |
| 292 | if (restart) { |
Andy Hung | ce8e6da | 2020-06-01 09:46:03 -0700 | [diff] [blame] | 293 | const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); }); |
| 294 | (void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 295 | ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | bool StreamManager::moveToRestartQueue_l( |
| 300 | Stream* stream, int32_t activeStreamIDToMatch) |
| 301 | { |
| 302 | ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)", |
| 303 | __func__, stream->getStreamID(), activeStreamIDToMatch); |
| 304 | if (activeStreamIDToMatch > 0 && stream->getStreamID() != activeStreamIDToMatch) { |
| 305 | return false; |
| 306 | } |
| 307 | const ssize_t found = removeFromQueues_l(stream, activeStreamIDToMatch); |
| 308 | if (found < 0) return false; |
| 309 | |
| 310 | LOG_ALWAYS_FATAL_IF(found > 1, "stream on %zd > 1 stream lists", found); |
| 311 | |
| 312 | addToRestartQueue_l(stream); |
| 313 | mStreamManagerCondition.notify_one(); |
| 314 | return true; |
| 315 | } |
| 316 | |
| 317 | ssize_t StreamManager::removeFromQueues_l( |
| 318 | Stream* stream, int32_t activeStreamIDToMatch) { |
| 319 | size_t found = 0; |
| 320 | for (auto it = mActiveStreams.begin(); it != mActiveStreams.end(); ++it) { |
| 321 | if (*it == stream) { |
| 322 | mActiveStreams.erase(it); // we erase the iterator and break (otherwise it not safe). |
| 323 | ++found; |
| 324 | break; |
| 325 | } |
| 326 | } |
| 327 | // activeStreamIDToMatch is nonzero indicates we proceed only if found. |
| 328 | if (found == 0 && activeStreamIDToMatch > 0) { |
| 329 | return -1; // special code: not present on active streams, ignore restart request |
| 330 | } |
| 331 | |
| 332 | for (auto it = mRestartStreams.begin(); it != mRestartStreams.end(); ++it) { |
| 333 | if (it->second == stream) { |
| 334 | mRestartStreams.erase(it); |
| 335 | ++found; |
| 336 | break; |
| 337 | } |
| 338 | } |
| 339 | found += mAvailableStreams.erase(stream); |
| 340 | |
| 341 | // streams on mProcessingStreams are undergoing processing by the StreamManager thread |
| 342 | // and do not participate in normal stream migration. |
Andy Hung | a5daa17 | 2021-03-10 17:07:17 -0800 | [diff] [blame] | 343 | return (ssize_t)found; |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 344 | } |
| 345 | |
| 346 | void StreamManager::addToRestartQueue_l(Stream *stream) { |
| 347 | mRestartStreams.emplace(stream->getStopTimeNs(), stream); |
| 348 | } |
| 349 | |
| 350 | void StreamManager::addToActiveQueue_l(Stream *stream) { |
| 351 | if (kStealActiveStream_OldestFirst) { |
| 352 | mActiveStreams.push_back(stream); // oldest to newest |
| 353 | } else { |
| 354 | mActiveStreams.push_front(stream); // newest to oldest |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | void StreamManager::run(int32_t id) |
| 359 | { |
| 360 | ALOGV("%s(%d) entering", __func__, id); |
Andy Hung | daa60c2 | 2021-03-15 19:01:51 -0700 | [diff] [blame] | 361 | int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty. |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 362 | std::unique_lock lock(mStreamManagerLock); |
| 363 | while (!mQuit) { |
Andy Hung | daa60c2 | 2021-03-15 19:01:51 -0700 | [diff] [blame] | 364 | if (waitTimeNs > 0) { |
Andy Hung | ba04dbe | 2020-03-19 21:32:53 -0700 | [diff] [blame] | 365 | mStreamManagerCondition.wait_for( |
| 366 | lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); |
| 367 | } |
Andy Hung | daa60c2 | 2021-03-15 19:01:51 -0700 | [diff] [blame] | 368 | ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 369 | |
| 370 | sanityCheckQueue_l(); |
| 371 | |
| 372 | if (mQuit || (mRestartStreams.empty() && waitTimeNs == kWaitTimeBeforeCloseNs)) { |
| 373 | break; // end the thread |
| 374 | } |
| 375 | |
| 376 | waitTimeNs = kWaitTimeBeforeCloseNs; |
| 377 | while (!mQuit && !mRestartStreams.empty()) { |
| 378 | const nsecs_t nowNs = systemTime(); |
| 379 | auto it = mRestartStreams.begin(); |
| 380 | Stream* const stream = it->second; |
| 381 | const int64_t diffNs = stream->getStopTimeNs() - nowNs; |
| 382 | if (diffNs > 0) { |
| 383 | waitTimeNs = std::min(waitTimeNs, diffNs); |
| 384 | break; |
| 385 | } |
| 386 | mRestartStreams.erase(it); |
| 387 | mProcessingStreams.emplace(stream); |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 388 | if (!mLockStreamManagerStop) lock.unlock(); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 389 | stream->stop(); |
| 390 | ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID()); |
| 391 | if (Stream* nextStream = stream->playPairStream()) { |
| 392 | ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID()); |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 393 | if (!mLockStreamManagerStop) lock.lock(); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 394 | if (nextStream->getStopTimeNs() > 0) { |
| 395 | // the next stream was stopped before we can move it to the active queue. |
| 396 | ALOGV("%s(%d) stopping started streamID:%d", |
| 397 | __func__, id, nextStream->getStreamID()); |
| 398 | moveToRestartQueue_l(nextStream); |
| 399 | } else { |
| 400 | addToActiveQueue_l(nextStream); |
| 401 | } |
| 402 | } else { |
Andy Hung | 43da3d5 | 2021-03-15 11:31:45 -0700 | [diff] [blame] | 403 | if (!mLockStreamManagerStop) lock.lock(); |
Andy Hung | e7937b9 | 2019-08-28 21:02:23 -0700 | [diff] [blame] | 404 | mAvailableStreams.insert(stream); |
| 405 | } |
| 406 | mProcessingStreams.erase(stream); |
| 407 | sanityCheckQueue_l(); |
| 408 | } |
| 409 | } |
| 410 | ALOGV("%s(%d) exiting", __func__, id); |
| 411 | } |
| 412 | |
| 413 | void StreamManager::dump() const |
| 414 | { |
| 415 | forEach([](const Stream *stream) { stream->dump(); }); |
| 416 | } |
| 417 | |
| 418 | void StreamManager::sanityCheckQueue_l() const |
| 419 | { |
| 420 | // We want to preserve the invariant that each stream pair is exactly on one of the queues. |
| 421 | const size_t availableStreams = mAvailableStreams.size(); |
| 422 | const size_t restartStreams = mRestartStreams.size(); |
| 423 | const size_t activeStreams = mActiveStreams.size(); |
| 424 | const size_t processingStreams = mProcessingStreams.size(); |
| 425 | const size_t managedStreams = availableStreams + restartStreams + activeStreams |
| 426 | + processingStreams; |
| 427 | const size_t totalStreams = getStreamMapSize() >> 1; |
| 428 | LOG_ALWAYS_FATAL_IF(managedStreams != totalStreams, |
| 429 | "%s: mAvailableStreams:%zu + mRestartStreams:%zu + " |
| 430 | "mActiveStreams:%zu + mProcessingStreams:%zu = %zu != total streams %zu", |
| 431 | __func__, availableStreams, restartStreams, activeStreams, processingStreams, |
| 432 | managedStreams, totalStreams); |
| 433 | ALOGV("%s: mAvailableStreams:%zu + mRestartStreams:%zu + " |
| 434 | "mActiveStreams:%zu + mProcessingStreams:%zu = %zu (total streams: %zu)", |
| 435 | __func__, availableStreams, restartStreams, activeStreams, processingStreams, |
| 436 | managedStreams, totalStreams); |
| 437 | } |
| 438 | |
| 439 | } // namespace android::soundpool |