blob: 8e9b5a68584009a3be9ee88d53254402c90286c6 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/thread_annotations.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <mutex>
#pragma push_macro("LOG_TAG")
#undef LOG_TAG
#define LOG_TAG "audio_utils::mutex"
namespace android::audio_utils {
// Define global capabilities for thread-safety annotation.
//
// These can be manually modified, or
// compile generate_mutex_order.cpp in the tests directory
// to generate this.
// --- Begin generated section
// Lock order
enum class MutexOrder : uint32_t {
kEffectHandle_Mutex = 0,
kEffectBase_PolicyMutex = 1,
kAudioFlinger_Mutex = 2,
kAudioFlinger_HardwareMutex = 3,
kDeviceEffectManager_Mutex = 4,
kPatchCommandThread_Mutex = 5,
kThreadBase_Mutex = 6,
kAudioFlinger_ClientMutex = 7,
kMelReporter_Mutex = 8,
kEffectChain_Mutex = 9,
kDeviceEffectProxy_ProxyMutex = 10,
kEffectBase_Mutex = 11,
kAudioFlinger_UnregisteredWritersMutex = 12,
kAsyncCallbackThread_Mutex = 13,
kConfigEvent_Mutex = 14,
kOutputTrack_TrackMetadataMutex = 15,
kPassthruPatchRecord_ReadMutex = 16,
kPatchCommandThread_ListenerMutex = 17,
kPlaybackThread_AudioTrackCbMutex = 18,
kMediaLogNotifier_Mutex = 19,
kOtherMutex = 20,
kSize = 21,
};
// Lock by name
inline constexpr const char* const gMutexNames[] = {
"EffectHandle_Mutex",
"EffectBase_PolicyMutex",
"AudioFlinger_Mutex",
"AudioFlinger_HardwareMutex",
"DeviceEffectManager_Mutex",
"PatchCommandThread_Mutex",
"ThreadBase_Mutex",
"AudioFlinger_ClientMutex",
"MelReporter_Mutex",
"EffectChain_Mutex",
"DeviceEffectProxy_ProxyMutex",
"EffectBase_Mutex",
"AudioFlinger_UnregisteredWritersMutex",
"AsyncCallbackThread_Mutex",
"ConfigEvent_Mutex",
"OutputTrack_TrackMetadataMutex",
"PassthruPatchRecord_ReadMutex",
"PatchCommandThread_ListenerMutex",
"PlaybackThread_AudioTrackCbMutex",
"MediaLogNotifier_Mutex",
"OtherMutex",
};
// Forward declarations
class AudioMutexAttributes;
template <typename T> class mutex_impl;
using mutex = mutex_impl<AudioMutexAttributes>;
// Capabilities in priority order
// (declaration only, value is nullptr)
inline mutex* EffectHandle_Mutex;
inline mutex* EffectBase_PolicyMutex
ACQUIRED_AFTER(android::audio_utils::EffectHandle_Mutex);
inline mutex* AudioFlinger_Mutex
ACQUIRED_AFTER(android::audio_utils::EffectBase_PolicyMutex);
inline mutex* AudioFlinger_HardwareMutex
ACQUIRED_AFTER(android::audio_utils::AudioFlinger_Mutex);
inline mutex* DeviceEffectManager_Mutex
ACQUIRED_AFTER(android::audio_utils::AudioFlinger_HardwareMutex);
inline mutex* PatchCommandThread_Mutex
ACQUIRED_AFTER(android::audio_utils::DeviceEffectManager_Mutex);
inline mutex* ThreadBase_Mutex
ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_Mutex);
inline mutex* AudioFlinger_ClientMutex
ACQUIRED_AFTER(android::audio_utils::ThreadBase_Mutex);
inline mutex* MelReporter_Mutex
ACQUIRED_AFTER(android::audio_utils::AudioFlinger_ClientMutex);
inline mutex* EffectChain_Mutex
ACQUIRED_AFTER(android::audio_utils::MelReporter_Mutex);
inline mutex* DeviceEffectProxy_ProxyMutex
ACQUIRED_AFTER(android::audio_utils::EffectChain_Mutex);
inline mutex* EffectBase_Mutex
ACQUIRED_AFTER(android::audio_utils::DeviceEffectProxy_ProxyMutex);
inline mutex* AudioFlinger_UnregisteredWritersMutex
ACQUIRED_AFTER(android::audio_utils::EffectBase_Mutex);
inline mutex* AsyncCallbackThread_Mutex
ACQUIRED_AFTER(android::audio_utils::AudioFlinger_UnregisteredWritersMutex);
inline mutex* ConfigEvent_Mutex
ACQUIRED_AFTER(android::audio_utils::AsyncCallbackThread_Mutex);
inline mutex* OutputTrack_TrackMetadataMutex
ACQUIRED_AFTER(android::audio_utils::ConfigEvent_Mutex);
inline mutex* PassthruPatchRecord_ReadMutex
ACQUIRED_AFTER(android::audio_utils::OutputTrack_TrackMetadataMutex);
inline mutex* PatchCommandThread_ListenerMutex
ACQUIRED_AFTER(android::audio_utils::PassthruPatchRecord_ReadMutex);
inline mutex* PlaybackThread_AudioTrackCbMutex
ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_ListenerMutex);
inline mutex* MediaLogNotifier_Mutex
ACQUIRED_AFTER(android::audio_utils::PlaybackThread_AudioTrackCbMutex);
inline mutex* OtherMutex
ACQUIRED_AFTER(android::audio_utils::MediaLogNotifier_Mutex);
// Exclusion by capability
#define EXCLUDES_BELOW_OtherMutex
#define EXCLUDES_OtherMutex \
EXCLUDES(android::audio_utils::OtherMutex) \
EXCLUDES_BELOW_OtherMutex
#define EXCLUDES_BELOW_MediaLogNotifier_Mutex \
EXCLUDES_OtherMutex
#define EXCLUDES_MediaLogNotifier_Mutex \
EXCLUDES(android::audio_utils::MediaLogNotifier_Mutex) \
EXCLUDES_BELOW_MediaLogNotifier_Mutex
#define EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex \
EXCLUDES_MediaLogNotifier_Mutex
#define EXCLUDES_PlaybackThread_AudioTrackCbMutex \
EXCLUDES(android::audio_utils::PlaybackThread_AudioTrackCbMutex) \
EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex
#define EXCLUDES_BELOW_PatchCommandThread_ListenerMutex \
EXCLUDES_PlaybackThread_AudioTrackCbMutex
#define EXCLUDES_PatchCommandThread_ListenerMutex \
EXCLUDES(android::audio_utils::PatchCommandThread_ListenerMutex) \
EXCLUDES_BELOW_PatchCommandThread_ListenerMutex
#define EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex \
EXCLUDES_PatchCommandThread_ListenerMutex
#define EXCLUDES_PassthruPatchRecord_ReadMutex \
EXCLUDES(android::audio_utils::PassthruPatchRecord_ReadMutex) \
EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex
#define EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex \
EXCLUDES_PassthruPatchRecord_ReadMutex
#define EXCLUDES_OutputTrack_TrackMetadataMutex \
EXCLUDES(android::audio_utils::OutputTrack_TrackMetadataMutex) \
EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex
#define EXCLUDES_BELOW_ConfigEvent_Mutex \
EXCLUDES_OutputTrack_TrackMetadataMutex
#define EXCLUDES_ConfigEvent_Mutex \
EXCLUDES(android::audio_utils::ConfigEvent_Mutex) \
EXCLUDES_BELOW_ConfigEvent_Mutex
#define EXCLUDES_BELOW_AsyncCallbackThread_Mutex \
EXCLUDES_ConfigEvent_Mutex
#define EXCLUDES_AsyncCallbackThread_Mutex \
EXCLUDES(android::audio_utils::AsyncCallbackThread_Mutex) \
EXCLUDES_BELOW_AsyncCallbackThread_Mutex
#define EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex \
EXCLUDES_AsyncCallbackThread_Mutex
#define EXCLUDES_AudioFlinger_UnregisteredWritersMutex \
EXCLUDES(android::audio_utils::AudioFlinger_UnregisteredWritersMutex) \
EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex
#define EXCLUDES_BELOW_EffectBase_Mutex \
EXCLUDES_AudioFlinger_UnregisteredWritersMutex
#define EXCLUDES_EffectBase_Mutex \
EXCLUDES(android::audio_utils::EffectBase_Mutex) \
EXCLUDES_BELOW_EffectBase_Mutex
#define EXCLUDES_BELOW_DeviceEffectProxy_ProxyMutex \
EXCLUDES_EffectBase_Mutex
#define EXCLUDES_DeviceEffectProxy_ProxyMutex \
EXCLUDES(android::audio_utils::DeviceEffectProxy_ProxyMutex) \
EXCLUDES_BELOW_DeviceEffectProxy_ProxyMutex
#define EXCLUDES_BELOW_EffectChain_Mutex \
EXCLUDES_DeviceEffectProxy_ProxyMutex
#define EXCLUDES_EffectChain_Mutex \
EXCLUDES(android::audio_utils::EffectChain_Mutex) \
EXCLUDES_BELOW_EffectChain_Mutex
#define EXCLUDES_BELOW_MelReporter_Mutex \
EXCLUDES_EffectChain_Mutex
#define EXCLUDES_MelReporter_Mutex \
EXCLUDES(android::audio_utils::MelReporter_Mutex) \
EXCLUDES_BELOW_MelReporter_Mutex
#define EXCLUDES_BELOW_AudioFlinger_ClientMutex \
EXCLUDES_MelReporter_Mutex
#define EXCLUDES_AudioFlinger_ClientMutex \
EXCLUDES(android::audio_utils::AudioFlinger_ClientMutex) \
EXCLUDES_BELOW_AudioFlinger_ClientMutex
#define EXCLUDES_BELOW_ThreadBase_Mutex \
EXCLUDES_AudioFlinger_ClientMutex
#define EXCLUDES_ThreadBase_Mutex \
EXCLUDES(android::audio_utils::ThreadBase_Mutex) \
EXCLUDES_BELOW_ThreadBase_Mutex
#define EXCLUDES_BELOW_PatchCommandThread_Mutex \
EXCLUDES_ThreadBase_Mutex
#define EXCLUDES_PatchCommandThread_Mutex \
EXCLUDES(android::audio_utils::PatchCommandThread_Mutex) \
EXCLUDES_BELOW_PatchCommandThread_Mutex
#define EXCLUDES_BELOW_DeviceEffectManager_Mutex \
EXCLUDES_PatchCommandThread_Mutex
#define EXCLUDES_DeviceEffectManager_Mutex \
EXCLUDES(android::audio_utils::DeviceEffectManager_Mutex) \
EXCLUDES_BELOW_DeviceEffectManager_Mutex
#define EXCLUDES_BELOW_AudioFlinger_HardwareMutex \
EXCLUDES_DeviceEffectManager_Mutex
#define EXCLUDES_AudioFlinger_HardwareMutex \
EXCLUDES(android::audio_utils::AudioFlinger_HardwareMutex) \
EXCLUDES_BELOW_AudioFlinger_HardwareMutex
#define EXCLUDES_BELOW_AudioFlinger_Mutex \
EXCLUDES_AudioFlinger_HardwareMutex
#define EXCLUDES_AudioFlinger_Mutex \
EXCLUDES(android::audio_utils::AudioFlinger_Mutex) \
EXCLUDES_BELOW_AudioFlinger_Mutex
#define EXCLUDES_BELOW_EffectBase_PolicyMutex \
EXCLUDES_AudioFlinger_Mutex
#define EXCLUDES_EffectBase_PolicyMutex \
EXCLUDES(android::audio_utils::EffectBase_PolicyMutex) \
EXCLUDES_BELOW_EffectBase_PolicyMutex
#define EXCLUDES_BELOW_EffectHandle_Mutex \
EXCLUDES_EffectBase_PolicyMutex
#define EXCLUDES_EffectHandle_Mutex \
EXCLUDES(android::audio_utils::EffectHandle_Mutex) \
EXCLUDES_BELOW_EffectHandle_Mutex
#define EXCLUDES_AUDIO_ALL \
EXCLUDES_EffectHandle_Mutex
// --- End generated section
/**
* AudioMutexAttributes is a collection of types and constexpr configuration
* used for the Android audio mutex.
*
* A different AudioMutexAttributes configuration will instantiate a completely
* independent set of mutex strategies, statics and thread locals,
* for a different type of mutexes.
*/
class AudioMutexAttributes {
public:
// Order types, name arrays.
using order_t = MutexOrder;
static constexpr auto& order_names_ = gMutexNames;
static constexpr size_t order_size_ = (size_t)MutexOrder::kSize;
static constexpr order_t order_default_ = MutexOrder::kOtherMutex;
// verify order information
static_assert(std::size(order_names_) == order_size_);
static_assert((size_t)order_default_ < order_size_);
};
// audio_utils::mutex, audio_utils::lock_guard, audio_utils::unique_lock,
// and audio_utils::condition_variable are method compatible versions
// of std::mutex, std::lock_guard, std::unique_lock, and std::condition_variable
// for optimizing audio thread performance and debugging.
//
// Note: we do not use std::timed_mutex as its Clang library implementation
// is inefficient. One is better off making a custom timed implementation using
// pthread_mutex_timedlock() on the mutex::native_handle().
extern bool mutex_get_enable_flag();
template <typename Attributes>
class CAPABILITY("mutex") mutex_impl {
public:
// We use composition here.
// No copy/move ctors as the member std::mutex has it deleted.
mutex_impl(typename Attributes::order_t order = Attributes::order_default_)
: order_(order)
{
LOG_ALWAYS_FATAL_IF((size_t)order >= Attributes::order_size_,
"mutex order %u is equal to or greater than order limit:%zu",
order, Attributes::order_size_);
if (!mutex_get_enable_flag()) return;
pthread_mutexattr_t attr;
int ret = pthread_mutexattr_init(&attr);
if (ret != 0) {
ALOGW("%s, pthread_mutexattr_init returned %d", __func__, ret);
return;
}
ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
if (ret != 0) {
ALOGW("%s, pthread_mutexattr_setprotocol returned %d", __func__, ret);
return;
}
// use of the native_handle() is implementation defined.
const auto handle = m_.native_handle();
ret = pthread_mutex_init(handle, &attr);
if (ret != 0) {
ALOGW("%s, pthread_mutex_init returned %d", __func__, ret);
}
ALOGV("%s: audio_mutex initialized: ret:%d order:%u", __func__, ret, order_);
}
~mutex_impl() {
// Note: std::mutex behavior is undefined if released holding ownership.
}
auto native_handle() {
return m_.native_handle();
}
void lock() ACQUIRE() {
m_.lock();
}
void unlock() RELEASE() {
m_.unlock();
}
bool try_lock(int64_t timeout_ns = 0) TRY_ACQUIRE(true) {
if (timeout_ns <= 0) {
if (!m_.try_lock()) return false;
} else {
const int64_t deadline_ns = timeout_ns + systemTime(SYSTEM_TIME_REALTIME);
const struct timespec ts = {
.tv_sec = static_cast<time_t>(deadline_ns / 1'000'000'000),
.tv_nsec = static_cast<long>(deadline_ns % 1'000'000'000),
};
if (pthread_mutex_timedlock(m_.native_handle(), &ts) != 0) return false;
}
return true;
}
// additional method to obtain the underlying std::mutex.
std::mutex& std_mutex() {
return m_;
}
private:
std::mutex m_;
const typename Attributes::order_t order_;
};
// audio_utils::lock_guard only works with the defined mutex.
using lock_guard = std::lock_guard<mutex>;
// audio_utils::unique_lock is based on std::unique_lock<std::mutex>
// because std::condition_variable is optimized for std::unique_lock<std::mutex>
//
// Note: std::unique_lock **does not** have thread safety annotations.
// We annotate correctly here. Essentially, this is the same as an annotated
// using unique_lock = std::unique_lock<std::mutex>;
//
// We omit swap(), release() and move methods which don't have thread
// safety annotations.
class SCOPED_CAPABILITY unique_lock {
public:
explicit unique_lock(mutex& m) ACQUIRE(m)
: ul_(m.std_mutex()) {}
~unique_lock() RELEASE() = default;
void lock() ACQUIRE() {
ul_.lock();
}
void unlock() RELEASE() {
ul_.unlock();
}
bool try_lock() TRY_ACQUIRE(true) {
return ul_.try_lock();
}
template<class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration)
TRY_ACQUIRE(true) {
return ul_.try_lock_for(timeout_duration);
}
template<class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
TRY_ACQUIRE(true) {
return ul_.try_lock_until(timeout_time);
}
// additional method to obtain the underlying std::unique_lock
std::unique_lock<std::mutex>& std_unique_lock() {
return ul_;
}
private:
std::unique_lock<std::mutex> ul_;
};
// audio_utils::condition_variable uses the optimized version of
// std::condition_variable for std::unique_lock<std::mutex>
// It is possible to use std::condition_variable_any for a generic mutex type,
// but it is less efficient.
class condition_variable {
public:
void notify_one() noexcept {
cv_.notify_one();
}
void notify_all() noexcept {
cv_.notify_all();
}
void wait(unique_lock& lock) {
cv_.wait(lock.std_unique_lock());
}
template<typename Predicate>
void wait(unique_lock& lock, Predicate stop_waiting) {
cv_.wait(lock.std_unique_lock(), std::move(stop_waiting));
}
template<typename Rep, typename Period>
std::cv_status wait_for(unique_lock& lock,
const std::chrono::duration<Rep, Period>& rel_time) {
return cv_.wait_for(lock.std_unique_lock(), rel_time);
}
template<typename Rep, typename Period, typename Predicate>
bool wait_for(unique_lock& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate stop_waiting) {
return cv_.wait_for(lock.std_unique_lock(), rel_time, std::move(stop_waiting));
}
template<typename Clock, typename Duration>
std::cv_status wait_until(unique_lock& lock,
const std::chrono::time_point<Clock, Duration>& timeout_time) {
return cv_.wait_until(lock.std_unique_lock(), timeout_time);
}
template<typename Clock, typename Duration, typename Predicate>
bool wait_until(unique_lock& lock,
const std::chrono::time_point<Clock, Duration>& timeout_time,
Predicate stop_waiting) {
return cv_.wait_until(lock.std_unique_lock(), timeout_time, std::move(stop_waiting));
}
auto native_handle() {
return cv_.native_handle();
}
private:
std::condition_variable cv_;
};
// audio_utils::scoped_lock has proper thread safety annotations.
// std::scoped_lock does not have thread safety annotations for greater than 1 lock
// since the variadic template doesn't convert to the variadic macro def.
template <typename ...Mutexes>
class scoped_lock;
template <typename Mutex1>
class SCOPED_CAPABILITY scoped_lock<Mutex1>
: private std::scoped_lock<Mutex1> {
public:
explicit scoped_lock(Mutex1& m) ACQUIRE(m) : std::scoped_lock<Mutex1>(m) {}
~scoped_lock() RELEASE() = default;
};
template <typename Mutex1, typename Mutex2>
class SCOPED_CAPABILITY scoped_lock<Mutex1, Mutex2>
: private std::scoped_lock<Mutex1, Mutex2> {
public:
scoped_lock(Mutex1& m1, Mutex2& m2) ACQUIRE(m1, m2)
: std::scoped_lock<Mutex1, Mutex2>(m1, m2) {}
~scoped_lock() RELEASE() = default;
};
template <typename Mutex1, typename Mutex2, typename Mutex3>
class SCOPED_CAPABILITY scoped_lock<Mutex1, Mutex2, Mutex3>
: private std::scoped_lock<Mutex1, Mutex2, Mutex3> {
public:
scoped_lock(Mutex1& m1, Mutex2& m2, Mutex3& m3) ACQUIRE(m1, m2, m3)
: std::scoped_lock<Mutex1, Mutex2, Mutex3>(m1, m2, m3) {}
~scoped_lock() RELEASE() = default;
};
template <typename ...Mutexes>
class scoped_lock : private std::scoped_lock<Mutexes...> {
public:
scoped_lock(Mutexes&... mutexes)
: std::scoped_lock<Mutexes...>(mutexes...) {}
};
// audio_utils::lock_guard_no_thread_safety_analysis is used to lock
// the second mutex when the same global capability is aliased
// to 2 (or more) different mutexes.
//
// Example usage:
//
// // Suppose the interface IAfThreadBase::mutex() returns a global capability
// // ThreadBase_Mutex.
//
// sp<IAfThreadBase> srcThread, dstThread;
//
// lock_guard(srcThread->mutex()); // acquires global capability ThreadBase_Mutex;
// ...
// lock_guard_no_thread_safety_analysis( // lock_guard would fail here as
// dstThread->mutex()); // the same global capability is assigned to
// // dstThread->mutex().
// // lock_guard_no_thread_safety_analysis
// // prevents a thread safety error.
template<typename Mutex1>
class lock_guard_no_thread_safety_analysis : private std::lock_guard<Mutex1> {
public:
lock_guard_no_thread_safety_analysis(Mutex1& m) : std::lock_guard<Mutex1>(m) {}
};
} // namespace android::audio_utils
#pragma pop_macro("LOG_TAG")