Merge "aaudio: prevent busy loop on shared stream standby" into main
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 4e46bbf..20a5ea8 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -398,6 +398,7 @@
}
// implement Runnable, periodically send timestamps to client and process commands from queue.
+// Enter standby mode if idle for a while.
__attribute__((no_sanitize("integer")))
void AAudioServiceStreamBase::run() {
ALOGD("%s() %s entering >>>>>>>>>>>>>> COMMANDS", __func__, getTypeText());
@@ -406,6 +407,7 @@
TimestampScheduler timestampScheduler;
int64_t nextTimestampReportTime;
int64_t nextDataReportTime;
+ // When to try to enter standby.
int64_t standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
// Balance the incStrong from when the thread was launched.
holdStream->decStrong(nullptr);
@@ -417,28 +419,26 @@
int32_t loopCount = 0;
while (mThreadEnabled.load()) {
loopCount++;
- int64_t timeoutNanos = -1;
- if (isDisconnected_l()) {
- if (!isStandby_l()) {
- // If the stream is disconnected but not in standby mode, wait until standby time.
+ int64_t timeoutNanos = -1; // wait forever
+ if (isDisconnected_l() || isIdle_l()) {
+ if (isStandbyImplemented() && !isStandby_l()) {
+ // If not in standby mode, wait until standby time.
timeoutNanos = standbyTime - AudioClock::getNanoseconds();
timeoutNanos = std::max<int64_t>(0, timeoutNanos);
- } // else {
- // If the stream is disconnected and in standby mode, keep `timeoutNanos` as
- // -1 to wait forever until next command as the stream can only be closed.
- // }
- } else if (isRunning() || (isIdle_l() && !isStandby_l())) {
- timeoutNanos = (isRunning() ? std::min(nextTimestampReportTime, nextDataReportTime)
- : standbyTime) - AudioClock::getNanoseconds();
+ }
+ // Otherwise, keep `timeoutNanos` as -1 to wait forever until next command.
+ } else if (isRunning()) {
+ timeoutNanos = std::min(nextTimestampReportTime, nextDataReportTime)
+ - AudioClock::getNanoseconds();
timeoutNanos = std::max<int64_t>(0, timeoutNanos);
}
-
auto command = mCommandQueue.waitForCommand(timeoutNanos);
if (!mThreadEnabled) {
// Break the loop if the thread is disabled.
break;
}
+ // Is it time to send timestamps?
if (isRunning() && !isDisconnected_l()) {
auto currentTimestamp = AudioClock::getNanoseconds();
if (currentTimestamp >= nextDataReportTime) {
@@ -454,19 +454,24 @@
nextTimestampReportTime = timestampScheduler.nextAbsoluteTime();
}
}
- if ((isIdle_l() || isDisconnected_l()) && AudioClock::getNanoseconds() >= standbyTime) {
+
+ // Is it time to enter standby?
+ if ((isIdle_l() || isDisconnected_l())
+ && isStandbyImplemented()
+ && !isStandby_l()
+ && (AudioClock::getNanoseconds() >= standbyTime)) {
+ ALOGD("%s() call standby_l(), %d loops", __func__, loopCount);
aaudio_result_t result = standby_l();
if (result != AAUDIO_OK) {
- // If standby failed because of the function is not implemented, there is no
- // need to retry. Otherwise, retry standby later.
- ALOGW("Failed to enter standby, error=%d", result);
- standbyTime = result == AAUDIO_ERROR_UNIMPLEMENTED
- ? std::numeric_limits<int64_t>::max()
- : AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
+ ALOGW("Failed to enter standby, error = %d", result);
+ // Try again later.
+ standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
}
}
if (command != nullptr) {
+ ALOGD("%s() got COMMAND opcode %d after %d loops",
+ __func__, command->operationCode, loopCount);
std::scoped_lock<std::mutex> _commandLock(command->lock);
switch (command->operationCode) {
case START:
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 8057f87..20737bc 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -316,9 +316,14 @@
mDisconnected = flag;
}
+ // If you implemented this method then please also override isStandbyImplemented().
virtual aaudio_result_t standby_l() REQUIRES(mLock) {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
+ virtual bool isStandbyImplemented() {
+ return false;
+ }
+
class ExitStandbyParam : public AAudioCommandParam {
public:
explicit ExitStandbyParam(AudioEndpointParcelable* parcelable)
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f4ce83d..f20ea10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -73,6 +73,9 @@
aaudio_result_t stop_l() REQUIRES(mLock) override;
aaudio_result_t standby_l() REQUIRES(mLock) override;
+ bool isStandbyImplemented() override {
+ return true;
+ }
aaudio_result_t exitStandby_l(AudioEndpointParcelable* parcelable) REQUIRES(mLock) override;