91 lines
3.3 KiB
C++
91 lines
3.3 KiB
C++
/*
|
|
* Copyright 2022 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
|
|
|
|
#define FTL_ATTRIBUTE(a) __attribute__((a))
|
|
|
|
namespace android::ftl {
|
|
|
|
// Granular alternative to [[clang::no_thread_safety_analysis]]. Given a std::mutex-like object,
|
|
// FakeGuard suppresses enforcement of thread-safe access to guarded variables within its scope.
|
|
// While FakeGuard is scoped to a block, there are macro shorthands for a single expression, as
|
|
// well as function/lambda scope (though calls must be indirect, e.g. virtual or std::function):
|
|
//
|
|
// struct {
|
|
// std::mutex mutex;
|
|
// int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1;
|
|
//
|
|
// int f() {
|
|
// {
|
|
// ftl::FakeGuard guard(mutex);
|
|
// x = 0;
|
|
// }
|
|
//
|
|
// return FTL_FAKE_GUARD(mutex, x + 1);
|
|
// }
|
|
//
|
|
// std::function<int()> g() const {
|
|
// return [this]() FTL_FAKE_GUARD(mutex) { return x; };
|
|
// }
|
|
// } s;
|
|
//
|
|
// assert(s.f() == 1);
|
|
// assert(s.g()() == 0);
|
|
//
|
|
// An example of a situation where FakeGuard helps is a mutex that guards writes on Thread 1, and
|
|
// reads on Thread 2. Reads on Thread 1, which is the only writer, need not be under lock, so can
|
|
// use FakeGuard to appease the thread safety analyzer. Another example is enforcing and documenting
|
|
// exclusive access by a single thread. This is done by defining a global constant that represents a
|
|
// thread context, and annotating guarded variables as if it were a mutex (though without any effect
|
|
// at run time):
|
|
//
|
|
// constexpr class [[clang::capability("mutex")]] {
|
|
// } kMainThreadContext;
|
|
//
|
|
template <typename Mutex>
|
|
struct [[clang::scoped_lockable]] FakeGuard final {
|
|
explicit FakeGuard(const Mutex& mutex) FTL_ATTRIBUTE(acquire_capability(mutex)) {}
|
|
[[clang::release_capability()]] ~FakeGuard() {}
|
|
|
|
FakeGuard(const FakeGuard&) = delete;
|
|
FakeGuard& operator=(const FakeGuard&) = delete;
|
|
};
|
|
|
|
} // namespace android::ftl
|
|
|
|
// TODO: Enable in C++23 once standard attributes can be used on lambdas.
|
|
#if 0
|
|
#define FTL_FAKE_GUARD1(mutex) [[using clang: acquire_capability(mutex), release_capability(mutex)]]
|
|
#else
|
|
#define FTL_FAKE_GUARD1(mutex) \
|
|
FTL_ATTRIBUTE(acquire_capability(mutex)) \
|
|
FTL_ATTRIBUTE(release_capability(mutex))
|
|
#endif
|
|
|
|
// The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
|
|
#define FTL_FAKE_GUARD2(mutex, expr) \
|
|
[&]() -> decltype(auto) { \
|
|
const android::ftl::FakeGuard guard(mutex); \
|
|
return (expr); \
|
|
}()
|
|
|
|
#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard
|
|
|
|
// The void argument suppresses a warning about zero variadic macro arguments.
|
|
#define FTL_FAKE_GUARD(...) \
|
|
FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, void)(__VA_ARGS__)
|