SoundPool: Implement generic garbage collection
Move audio tracks to a gc mechanism for release outside of lock.
Test: soundpool_stress
Test: atest SoundPoolAacTest
Test: atest SoundPoolHapticTest
Test: atest SoundPoolMidiTest
Test: atest SoundPoolOggTest
Bug: 203193643
Change-Id: I5babd4f3fa73cca25579fde2194123bf62cf53b4
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index bbbef38..773cdc9 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -228,10 +228,9 @@
return mStreamManager->getPairStream(this);
}
-Stream* Stream::playPairStream() {
+Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
Stream* pairStream = getPairStream();
LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
- sp<AudioTrack> releaseTracks[2];
{
ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
// TODO: Do we really want to force a simultaneous synchronization between
@@ -260,7 +259,7 @@
const int pairState = pairStream->mState;
pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
- pairStream->mLoop, pairStream->mRate, releaseTracks);
+ pairStream->mLoop, pairStream->mRate, garbage);
if (pairStream->mState == IDLE) {
return nullptr; // AudioTrack error
}
@@ -269,17 +268,16 @@
pairStream->mAudioTrack->pause();
}
}
- // release tracks outside of Stream lock
return pairStream;
}
void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
- sp<AudioTrack> releaseTracks[2])
+ std::vector<std::any>& garbage)
{
- // These tracks are released without the lock.
- sp<AudioTrack> &oldTrack = releaseTracks[0];
- sp<AudioTrack> &newTrack = releaseTracks[1];
+ // oldTrack and newTrack are placeholders to be released by garbage without the lock.
+ sp<AudioTrack> oldTrack;
+ sp<AudioTrack> newTrack;
status_t status = NO_ERROR;
{
@@ -383,8 +381,12 @@
mState = IDLE;
mSoundID = 0;
mSound.reset();
- mAudioTrack.clear(); // actual release from releaseTracks[]
+ mAudioTrack.clear(); // actual release from garbage
}
+
+ // move tracks to garbage to be released later outside of lock.
+ if (newTrack) garbage.emplace_back(std::move(newTrack));
+ if (oldTrack) garbage.emplace_back(std::move(oldTrack));
}
/* static */
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
index d4e5c9f..aa0eef5 100644
--- a/media/jni/soundpool/Stream.h
+++ b/media/jni/soundpool/Stream.h
@@ -18,6 +18,7 @@
#include "Sound.h"
+#include <any>
#include <android-base/thread_annotations.h>
#include <audio_utils/clock.h>
#include <media/AudioTrack.h>
@@ -90,8 +91,9 @@
void mute(bool muting);
void dump() const NO_THREAD_SAFETY_ANALYSIS; // disable for ALOGV (see func for details).
- // returns the pair stream if successful, nullptr otherwise
- Stream* playPairStream();
+ // returns the pair stream if successful, nullptr otherwise.
+ // garbage is used to release tracks and data outside of any lock.
+ Stream* playPairStream(std::vector<std::any>& garbage);
// These parameters are explicitly checked in the SoundPool class
// so never deviate from the Java API specified values.
@@ -123,9 +125,10 @@
Stream* getPairStream() const;
private:
+ // garbage is used to release tracks and data outside of any lock.
void play_l(const std::shared_ptr<Sound>& sound, int streamID,
float leftVolume, float rightVolume, int priority, int loop, float rate,
- sp<AudioTrack> releaseTracks[2]) REQUIRES(mLock);
+ std::vector<std::any>& garbage) REQUIRES(mLock);
void stop_l() REQUIRES(mLock);
void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 309d71c..7f987e3 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -157,6 +157,7 @@
__func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
bool launchThread = false;
int32_t streamID = 0;
+ std::vector<std::any> garbage;
{ // for lock
std::unique_lock lock(mStreamManagerLock);
@@ -243,7 +244,7 @@
removeFromQueues_l(newStream);
mProcessingStreams.emplace(newStream);
lock.unlock();
- if (Stream* nextStream = newStream->playPairStream()) {
+ if (Stream* nextStream = newStream->playPairStream(garbage)) {
lock.lock();
ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
addToActiveQueue_l(nextStream);
@@ -266,6 +267,7 @@
ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
}
ALOGV("%s: returning %d", __func__, streamID);
+ // garbage is cleared here outside mStreamManagerLock.
return streamID;
}
@@ -359,6 +361,7 @@
{
ALOGV("%s(%d) entering", __func__, id);
int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty.
+ std::vector<std::any> garbage; // used for garbage collection
std::unique_lock lock(mStreamManagerLock);
while (!mQuit) {
if (waitTimeNs > 0) {
@@ -388,7 +391,7 @@
if (!mLockStreamManagerStop) lock.unlock();
stream->stop();
ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
- if (Stream* nextStream = stream->playPairStream()) {
+ if (Stream* nextStream = stream->playPairStream(garbage)) {
ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
if (!mLockStreamManagerStop) lock.lock();
if (nextStream->getStopTimeNs() > 0) {
@@ -405,6 +408,12 @@
}
mProcessingStreams.erase(stream);
sanityCheckQueue_l();
+ if (!garbage.empty()) {
+ lock.unlock();
+ // garbage audio tracks (etc) are cleared here outside mStreamManagerLock.
+ garbage.clear();
+ lock.lock();
+ }
}
}
ALOGV("%s(%d) exiting", __func__, id);