Merge "audio: Ensure proper priority and scheduler for service threads" into main am: 41243dc213
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2696137 Change-Id: Iaad0376514ff74a8daa1b6bb20956ba7aa9b0acd Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
67869eb176
7 changed files with 67 additions and 8 deletions
|
@ -106,6 +106,9 @@ void ThreadController::workerThread() {
|
||||||
std::lock_guard<std::mutex> lock(mWorkerLock);
|
std::lock_guard<std::mutex> lock(mWorkerLock);
|
||||||
mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
|
mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
|
||||||
mError = error;
|
mError = error;
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
mTid = pthread_gettid_np(pthread_self());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
mWorkerCv.notify_one();
|
mWorkerCv.notify_one();
|
||||||
if (!error.empty()) return;
|
if (!error.empty()) return;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -52,6 +54,10 @@ class ThreadController {
|
||||||
std::lock_guard<std::mutex> lock(mWorkerLock);
|
std::lock_guard<std::mutex> lock(mWorkerLock);
|
||||||
return mError;
|
return mError;
|
||||||
}
|
}
|
||||||
|
pid_t getTid() {
|
||||||
|
std::lock_guard<std::mutex> lock(mWorkerLock);
|
||||||
|
return mTid;
|
||||||
|
}
|
||||||
void stop();
|
void stop();
|
||||||
// Direct use of 'join' assumes that the StreamLogic is not intended
|
// Direct use of 'join' assumes that the StreamLogic is not intended
|
||||||
// to run forever, and is guaranteed to exit by itself. This normally
|
// to run forever, and is guaranteed to exit by itself. This normally
|
||||||
|
@ -78,6 +84,7 @@ class ThreadController {
|
||||||
std::condition_variable mWorkerCv;
|
std::condition_variable mWorkerCv;
|
||||||
WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::INITIAL;
|
WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::INITIAL;
|
||||||
std::string mError GUARDED_BY(mWorkerLock);
|
std::string mError GUARDED_BY(mWorkerLock);
|
||||||
|
pid_t mTid GUARDED_BY(mWorkerLock) = -1;
|
||||||
// The atomic lock-free variable is used to prevent priority inversions
|
// The atomic lock-free variable is used to prevent priority inversions
|
||||||
// that can occur when a high priority worker tries to acquire the lock
|
// that can occur when a high priority worker tries to acquire the lock
|
||||||
// which has been taken by a lower priority control thread which in its turn
|
// which has been taken by a lower priority control thread which in its turn
|
||||||
|
@ -143,6 +150,7 @@ class StreamWorker : public LogicImpl {
|
||||||
void resume() { mThread.resume(); }
|
void resume() { mThread.resume(); }
|
||||||
bool hasError() { return mThread.hasError(); }
|
bool hasError() { return mThread.hasError(); }
|
||||||
std::string getError() { return mThread.getError(); }
|
std::string getError() { return mThread.getError(); }
|
||||||
|
pid_t getTid() { return mThread.getTid(); }
|
||||||
void stop() { mThread.stop(); }
|
void stop() { mThread.stop(); }
|
||||||
void join() { mThread.join(); }
|
void join() { mThread.join(); }
|
||||||
bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
|
bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
|
||||||
|
|
|
@ -87,6 +87,7 @@ class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
|
||||||
TEST_P(StreamWorkerInvalidTest, Uninitialized) {
|
TEST_P(StreamWorkerInvalidTest, Uninitialized) {
|
||||||
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
||||||
EXPECT_FALSE(worker.hasError());
|
EXPECT_FALSE(worker.hasError());
|
||||||
|
EXPECT_LE(worker.getTid(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
|
TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
|
||||||
|
@ -105,6 +106,9 @@ TEST_P(StreamWorkerInvalidTest, Start) {
|
||||||
EXPECT_FALSE(worker.start());
|
EXPECT_FALSE(worker.start());
|
||||||
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
||||||
EXPECT_TRUE(worker.hasError());
|
EXPECT_TRUE(worker.hasError());
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
EXPECT_GT(worker.getTid(), 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
|
TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
|
||||||
|
@ -136,12 +140,16 @@ static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
|
||||||
TEST_P(StreamWorkerTest, Uninitialized) {
|
TEST_P(StreamWorkerTest, Uninitialized) {
|
||||||
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
EXPECT_FALSE(worker.hasWorkerCycleCalled());
|
||||||
EXPECT_FALSE(worker.hasError());
|
EXPECT_FALSE(worker.hasError());
|
||||||
|
EXPECT_LE(worker.getTid(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(StreamWorkerTest, Start) {
|
TEST_P(StreamWorkerTest, Start) {
|
||||||
ASSERT_TRUE(worker.start());
|
ASSERT_TRUE(worker.start());
|
||||||
EXPECT_TRUE(worker.waitForAtLeastOneCycle());
|
EXPECT_TRUE(worker.waitForAtLeastOneCycle());
|
||||||
EXPECT_FALSE(worker.hasError());
|
EXPECT_FALSE(worker.hasError());
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
EXPECT_GT(worker.getTid(), 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(StreamWorkerTest, StartStop) {
|
TEST_P(StreamWorkerTest, StartStop) {
|
||||||
|
|
|
@ -479,14 +479,17 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
||||||
// Mix ports:
|
// Mix ports:
|
||||||
// * "test output", 1 max open, 1 max active stream
|
// * "test output", 1 max open, 1 max active stream
|
||||||
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||||
// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
|
// * "test fast output", 1 max open, 1 max active stream
|
||||||
|
// - profile PCM 24-bit; STEREO; 44100, 48000
|
||||||
|
// * "test compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active
|
||||||
|
// stream
|
||||||
// - profile MP3; MONO, STEREO; 44100, 48000
|
// - profile MP3; MONO, STEREO; 44100, 48000
|
||||||
// * "test input", 2 max open, 2 max active streams
|
// * "test input", 2 max open, 2 max active streams
|
||||||
// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
|
// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
|
||||||
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||||
//
|
//
|
||||||
// Routes:
|
// Routes:
|
||||||
// "test output", "compressed offload" -> "Test Out"
|
// "test output", "test fast output", "test compressed offload" -> "Test Out"
|
||||||
// "Test In" -> "test input"
|
// "Test In" -> "test input"
|
||||||
//
|
//
|
||||||
// Initial port configs:
|
// Initial port configs:
|
||||||
|
@ -525,8 +528,15 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
||||||
{8000, 11025, 16000, 32000, 44100, 48000}));
|
{8000, 11025, 16000, 32000, 44100, 48000}));
|
||||||
c.ports.push_back(testOutMix);
|
c.ports.push_back(testOutMix);
|
||||||
|
|
||||||
|
AudioPort testFastOutMix = createPort(c.nextPortId++, "test fast output",
|
||||||
|
makeBitPositionFlagMask({AudioOutputFlags::FAST}),
|
||||||
|
false, createPortMixExt(1, 1));
|
||||||
|
testFastOutMix.profiles.push_back(createProfile(
|
||||||
|
PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {44100, 48000}));
|
||||||
|
c.ports.push_back(testFastOutMix);
|
||||||
|
|
||||||
AudioPort compressedOffloadOutMix =
|
AudioPort compressedOffloadOutMix =
|
||||||
createPort(c.nextPortId++, "compressed offload",
|
createPort(c.nextPortId++, "test compressed offload",
|
||||||
makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
|
makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
|
||||||
AudioOutputFlags::COMPRESS_OFFLOAD,
|
AudioOutputFlags::COMPRESS_OFFLOAD,
|
||||||
AudioOutputFlags::NON_BLOCKING}),
|
AudioOutputFlags::NON_BLOCKING}),
|
||||||
|
@ -551,7 +561,8 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
||||||
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
|
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
|
||||||
c.ports.push_back(testInMIx);
|
c.ports.push_back(testInMIx);
|
||||||
|
|
||||||
c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
|
c.routes.push_back(
|
||||||
|
createRoute({testOutMix, testFastOutMix, compressedOffloadOutMix}, testOutDevice));
|
||||||
c.routes.push_back(createRoute({testInDevice}, testInMIx));
|
c.routes.push_back(createRoute({testInDevice}, testInMIx));
|
||||||
|
|
||||||
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
|
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
|
||||||
|
|
|
@ -27,12 +27,16 @@
|
||||||
using aidl::android::hardware::audio::common::AudioOffloadMetadata;
|
using aidl::android::hardware::audio::common::AudioOffloadMetadata;
|
||||||
using aidl::android::hardware::audio::common::getChannelCount;
|
using aidl::android::hardware::audio::common::getChannelCount;
|
||||||
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
|
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
|
||||||
|
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
|
||||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||||
using aidl::android::media::audio::common::AudioDevice;
|
using aidl::android::media::audio::common::AudioDevice;
|
||||||
using aidl::android::media::audio::common::AudioDualMonoMode;
|
using aidl::android::media::audio::common::AudioDualMonoMode;
|
||||||
|
using aidl::android::media::audio::common::AudioInputFlags;
|
||||||
|
using aidl::android::media::audio::common::AudioIoFlags;
|
||||||
using aidl::android::media::audio::common::AudioLatencyMode;
|
using aidl::android::media::audio::common::AudioLatencyMode;
|
||||||
using aidl::android::media::audio::common::AudioOffloadInfo;
|
using aidl::android::media::audio::common::AudioOffloadInfo;
|
||||||
|
using aidl::android::media::audio::common::AudioOutputFlags;
|
||||||
using aidl::android::media::audio::common::AudioPlaybackRate;
|
using aidl::android::media::audio::common::AudioPlaybackRate;
|
||||||
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
|
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
|
||||||
using aidl::android::media::audio::common::MicrophoneInfo;
|
using aidl::android::media::audio::common::MicrophoneInfo;
|
||||||
|
@ -610,8 +614,30 @@ StreamCommonImpl::~StreamCommonImpl() {
|
||||||
ndk::ScopedAStatus StreamCommonImpl::initInstance(
|
ndk::ScopedAStatus StreamCommonImpl::initInstance(
|
||||||
const std::shared_ptr<StreamCommonInterface>& delegate) {
|
const std::shared_ptr<StreamCommonInterface>& delegate) {
|
||||||
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
|
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
|
||||||
return mWorker->start() ? ndk::ScopedAStatus::ok()
|
if (!mWorker->start()) {
|
||||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||||
|
}
|
||||||
|
if (auto flags = getContext().getFlags();
|
||||||
|
(flags.getTag() == AudioIoFlags::Tag::input &&
|
||||||
|
isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::input>(),
|
||||||
|
AudioInputFlags::FAST)) ||
|
||||||
|
(flags.getTag() == AudioIoFlags::Tag::output &&
|
||||||
|
isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
|
||||||
|
AudioOutputFlags::FAST))) {
|
||||||
|
// FAST workers should be run with a SCHED_FIFO scheduler, however the host process
|
||||||
|
// might be lacking the capability to request it, thus a failure to set is not an error.
|
||||||
|
pid_t workerTid = mWorker->getTid();
|
||||||
|
if (workerTid > 0) {
|
||||||
|
struct sched_param param;
|
||||||
|
param.sched_priority = 3; // Must match SchedulingPolicyService.PRIORITY_MAX (Java).
|
||||||
|
if (sched_setscheduler(workerTid, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) {
|
||||||
|
PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler for a fast thread";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(WARNING) << __func__ << ": invalid worker tid: " << workerTid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
|
ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
|
||||||
|
|
|
@ -3,7 +3,7 @@ service vendor.audio-hal-aidl /vendor/bin/hw/android.hardware.audio.service-aidl
|
||||||
user audioserver
|
user audioserver
|
||||||
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
|
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
|
||||||
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
|
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
|
||||||
capabilities BLOCK_SUSPEND
|
capabilities BLOCK_SUSPEND SYS_NICE
|
||||||
# setting RLIMIT_RTPRIO allows binder RT priority inheritance
|
# setting RLIMIT_RTPRIO allows binder RT priority inheritance
|
||||||
rlimit rtprio 10 10
|
rlimit rtprio 10 10
|
||||||
ioprio rt 4
|
ioprio rt 4
|
||||||
|
|
|
@ -262,6 +262,7 @@ struct StreamWorkerInterface {
|
||||||
virtual void setIsConnected(bool isConnected) = 0;
|
virtual void setIsConnected(bool isConnected) = 0;
|
||||||
virtual StreamDescriptor::State setClosed() = 0;
|
virtual StreamDescriptor::State setClosed() = 0;
|
||||||
virtual bool start() = 0;
|
virtual bool start() = 0;
|
||||||
|
virtual pid_t getTid() = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,8 +278,10 @@ class StreamWorkerImpl : public StreamWorkerInterface,
|
||||||
void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
|
void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
|
||||||
StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
|
StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
|
||||||
bool start() override {
|
bool start() override {
|
||||||
return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_AUDIO);
|
// This is an "audio service thread," must have elevated priority.
|
||||||
|
return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_URGENT_AUDIO);
|
||||||
}
|
}
|
||||||
|
pid_t getTid() override { return WorkerImpl::getTid(); }
|
||||||
void stop() override { return WorkerImpl::stop(); }
|
void stop() override { return WorkerImpl::stop(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue