blob: aef496780aec483773f679426353f93544e2eb44 [file] [log] [blame]
Andy Hung328d6772021-01-12 12:32:21 -08001/*
2 * Copyright 2021, 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#include <mutex>
19#include <utils/RefBase.h>
20
21namespace android::mediautils {
22
23/**
24 * The LockItem class introduces a simple template which mimics atomic<T>
25 * for non-trivially copyable types. For trivially copyable types,
26 * the LockItem will statically assert that an atomic<T> should be used instead.
27 *
28 * The default lock mutex is std::mutex which is suitable for all but rare cases
29 * e.g. recursive constructors that might be found in tree construction,
30 * setters that might recurse onto the same object.
31 */
32
33template <typename T, typename L = std::mutex, int FLAGS = 0>
34class LockItem {
35protected:
36 mutable L mLock;
37 mutable T mT;
38
39public:
40 enum {
41 // Best practices for smart pointers and complex containers is to move to a temp
42 // and invoke destructor outside of lock. This reduces time under lock and in
43 // some cases eliminates deadlock.
44 FLAG_DTOR_OUT_OF_LOCK = 1,
45 };
46
47 // Check type, suggest std::atomic if possible.
48 static_assert(!std::is_trivially_copyable_v<T>,
49 "type is trivially copyable, please use std::atomic instead");
50
51 // Allow implicit conversions as expected for some types, e.g. sp -> wp.
52 template <typename... Args>
53 LockItem(Args&&... args) : mT(std::forward<Args>(args)...) {
54 }
55
56 // NOT copy or move / assignable or constructible.
57
58 // Do not enable this because it may lead to confusion because it returns
59 // a copy-value not a reference.
60 // operator T() const { return load(); }
61
62 // any conversion done under lock.
63 template <typename U>
64 void operator=(U&& u) {
65 store(std::forward<U>(u));
66 }
67
68 // returns a copy-value not a reference.
69 T load() const {
70 std::lock_guard lock(mLock);
71 return mT;
72 }
73
74 // any conversion done under lock.
75 template <typename U>
76 void store(U&& u) {
77 if constexpr ((FLAGS & FLAG_DTOR_OUT_OF_LOCK) != 0) {
78 std::unique_lock lock(mLock);
79 T temp = std::move(mT);
80 mT = std::forward<U>(u);
81 lock.unlock();
82 } else {
83 std::lock_guard lock(mLock);
84 mT = std::forward<U>(u);
85 }
86 }
87};
88
89/**
90 * atomic_wp<> and atomic_sp<> are used for concurrent access to Android
91 * sp<> and wp<> smart pointers, including their modifiers. We
92 * return a copy of the smart pointer with load().
93 *
94 * Historical: The importance of an atomic<std::shared_ptr<T>> class is described
95 * by Herb Sutter in the following ISO document https://isocpp.org/files/papers/N4162.pdf
96 * and is part of C++20. Lock free versions of atomic smart pointers are available
97 * publicly but usually require specialized smart pointer structs.
98 * See also https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
99 * and https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2
100 *
101 * We offer lock based atomic_wp<> and atomic_sp<> objects here. This is useful to
102 * copy the Android smart pointer to a different variable for subsequent local access,
103 * where the change of the original object after copy is acceptable.
104 *
105 * Note: Instead of atomics, it is often preferrable to create an explicit visible lock to
106 * ensure complete transaction consistency. For example, one might want to ensure
107 * that the method called from the smart pointer is also done under lock.
108 * This may not be possible for callbacks due to inverted lock ordering.
109 */
110
111template <typename T>
112using atomic_wp = LockItem<::android::wp<T>>;
113
114template <typename T>
115using atomic_sp = LockItem<
116 ::android::sp<T>, std::mutex, LockItem<::android::sp<T>>::FLAG_DTOR_OUT_OF_LOCK>;
117
Andy Hung638f45b2021-01-18 20:02:56 -0800118/**
119 * Defers a function to run in the RAII destructor.
120 * A C++ implementation of Go _defer_ https://golangr.com/defer/.
121 */
122class Defer {
123public:
124 template <typename U>
125 explicit Defer(U &&f) : mThunk(std::forward<U>(f)) {}
126 ~Defer() { mThunk(); }
127
128private:
129 const std::function<void()> mThunk;
130};
131
Andy Hung328d6772021-01-12 12:32:21 -0800132} // namespace android::mediautils
133