blob: e6012516fc6f8e8bcbc6d451c9c565ed12c358a2 [file] [log] [blame]
Dominik Laskowskibd006782022-02-15 22:20:16 -08001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#define FTL_ATTRIBUTE(a) __attribute__((a))
20
21namespace android::ftl {
22
23// Granular alternative to [[clang::no_thread_safety_analysis]]. Given a std::mutex-like object,
24// FakeGuard suppresses enforcement of thread-safe access to guarded variables within its scope.
25// While FakeGuard is scoped to a block, there are macro shorthands for a single expression, as
26// well as function/lambda scope (though calls must be indirect, e.g. virtual or std::function):
27//
28// struct {
29// std::mutex mutex;
30// int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1;
31//
32// int f() {
33// {
34// ftl::FakeGuard guard(mutex);
35// x = 0;
36// }
37//
38// return FTL_FAKE_GUARD(mutex, x + 1);
39// }
40//
41// std::function<int()> g() const {
42// return [this]() FTL_FAKE_GUARD(mutex) { return x; };
43// }
44// } s;
45//
46// assert(s.f() == 1);
47// assert(s.g()() == 0);
48//
49// An example of a situation where FakeGuard helps is a mutex that guards writes on Thread 1, and
50// reads on Thread 2. Reads on Thread 1, which is the only writer, need not be under lock, so can
51// use FakeGuard to appease the thread safety analyzer. Another example is enforcing and documenting
52// exclusive access by a single thread. This is done by defining a global constant that represents a
53// thread context, and annotating guarded variables as if it were a mutex (though without any effect
54// at run time):
55//
56// constexpr class [[clang::capability("mutex")]] {
57// } kMainThreadContext;
58//
59template <typename Mutex>
60struct [[clang::scoped_lockable]] FakeGuard final {
61 explicit FakeGuard(const Mutex& mutex) FTL_ATTRIBUTE(acquire_capability(mutex)) {}
62 [[clang::release_capability()]] ~FakeGuard() {}
63
64 FakeGuard(const FakeGuard&) = delete;
65 FakeGuard& operator=(const FakeGuard&) = delete;
66};
67
68} // namespace android::ftl
69
70// TODO: Enable in C++23 once standard attributes can be used on lambdas.
71#if 0
72#define FTL_FAKE_GUARD1(mutex) [[using clang: acquire_capability(mutex), release_capability(mutex)]]
73#else
74#define FTL_FAKE_GUARD1(mutex) \
75 FTL_ATTRIBUTE(acquire_capability(mutex)) \
76 FTL_ATTRIBUTE(release_capability(mutex))
77#endif
78
79// The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
80#define FTL_FAKE_GUARD2(mutex, expr) \
81 [&]() -> decltype(auto) { \
82 const android::ftl::FakeGuard guard(mutex); \
83 return (expr); \
84 }()
85
86#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard
87
Dominik Laskowskibd006782022-02-15 22:20:16 -080088#define FTL_FAKE_GUARD(...) \
Dominik Laskowskid7629e72022-04-18 08:25:04 -070089 FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, )(__VA_ARGS__)