Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43da3d54 authored by Andy Hung's avatar Andy Hung
Browse files

SoundPool: Improve single stream SoundPool handling

By design, the StreamManager ramps down volume
on a Stream stop to prevent pops and glitches.
When the SoundPool is configured only with a single stream,
there may be a short period of unavailability of that stream
while stop is called by the worker thread; an immediate
play after a stop may return 0 (failure).

To allow immediate play after stop for a *single* Stream configured
SoundPool, we lock the StreamManager worker thread so that the
stop call is processed and the stream is visible to the client for use.

We prefer not to keep this lock for the multiple Stream case as it
prevents concurrent initiation of sounds from multiple StreamManager
worker threads and such blocking is not appropriate for games.

Test: SoundPoolAacTest SoundPoolHapticTest
Test: SoundPoolMidiTest SoundPoolOggTest
Bug: 175097719
Bug: 177287876
Merged-In: Iec777d6319d5ed76000d4c5b12336b106dacede4
Change-Id: Iec777d6319d5ed76000d4c5b12336b106dacede4
parent 59437812
Loading
Loading
Loading
Loading
+14 −4
Original line number Original line Diff line number Diff line
@@ -43,6 +43,14 @@ static constexpr bool kPlayOnCallingThread = true;
// Amount of time for a StreamManager thread to wait before closing.
// Amount of time for a StreamManager thread to wait before closing.
static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;


// Debug flag:
// kForceLockStreamManagerStop is set to true to force lock the StreamManager
// worker thread during stop. This limits concurrency of Stream processing.
// Normally we lock the StreamManager worker thread during stop ONLY
// for SoundPools configured with a single Stream.
//
static constexpr bool kForceLockStreamManagerStop = false;

////////////
////////////


StreamMap::StreamMap(int32_t streams) {
StreamMap::StreamMap(int32_t streams) {
@@ -103,6 +111,7 @@ StreamManager::StreamManager(
    : StreamMap(streams)
    : StreamMap(streams)
    , mAttributes(*attributes)
    , mAttributes(*attributes)
    , mOpPackageName(std::move(opPackageName))
    , mOpPackageName(std::move(opPackageName))
    , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{
{
    ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
    ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
    forEach([this](Stream *stream) {
    forEach([this](Stream *stream) {
@@ -113,7 +122,8 @@ StreamManager::StreamManager(
    });
    });


    mThreadPool = std::make_unique<ThreadPool>(
    mThreadPool = std::make_unique<ThreadPool>(
            std::min(threads, (size_t)std::thread::hardware_concurrency()),
            std::min((size_t)streams,  // do not make more threads than streams to play
                    std::min(threads, (size_t)std::thread::hardware_concurrency())),
            "SoundPool_");
            "SoundPool_");
}
}


@@ -375,12 +385,12 @@ void StreamManager::run(int32_t id)
            }
            }
            mRestartStreams.erase(it);
            mRestartStreams.erase(it);
            mProcessingStreams.emplace(stream);
            mProcessingStreams.emplace(stream);
            lock.unlock();
            if (!mLockStreamManagerStop) lock.unlock();
            stream->stop();
            stream->stop();
            ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
            ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
            if (Stream* nextStream = stream->playPairStream()) {
            if (Stream* nextStream = stream->playPairStream()) {
                ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
                ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
                lock.lock();
                if (!mLockStreamManagerStop) lock.lock();
                if (nextStream->getStopTimeNs() > 0) {
                if (nextStream->getStopTimeNs() > 0) {
                    // the next stream was stopped before we can move it to the active queue.
                    // the next stream was stopped before we can move it to the active queue.
                    ALOGV("%s(%d) stopping started streamID:%d",
                    ALOGV("%s(%d) stopping started streamID:%d",
@@ -390,7 +400,7 @@ void StreamManager::run(int32_t id)
                    addToActiveQueue_l(nextStream);
                    addToActiveQueue_l(nextStream);
                }
                }
            } else {
            } else {
                lock.lock();
                if (!mLockStreamManagerStop) lock.lock();
                mAvailableStreams.insert(stream);
                mAvailableStreams.insert(stream);
            }
            }
            mProcessingStreams.erase(stream);
            mProcessingStreams.erase(stream);
+8 −2
Original line number Original line Diff line number Diff line
@@ -437,6 +437,14 @@ private:
    void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock);
    void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock);


    const audio_attributes_t mAttributes;
    const audio_attributes_t mAttributes;
    const std::string mOpPackageName;

   // For legacy compatibility, we lock the stream manager on stop when
   // there is only one stream.  This allows a play to be called immediately
   // after stopping, otherwise it is possible that the play might be discarded
   // (returns 0) because that stream may be in the worker thread call to stop.
    const bool mLockStreamManagerStop;

    std::unique_ptr<ThreadPool> mThreadPool;                  // locked internally
    std::unique_ptr<ThreadPool> mThreadPool;                  // locked internally


    // mStreamManagerLock is used to lock access for transitions between the
    // mStreamManagerLock is used to lock access for transitions between the
@@ -477,8 +485,6 @@ private:
    // The paired stream may be active or restarting.
    // The paired stream may be active or restarting.
    // No particular order.
    // No particular order.
    std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock);
    std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock);

    const std::string           mOpPackageName;
};
};


} // namespace android::soundpool
} // namespace android::soundpool