Andy Hung | 328d677 | 2021-01-12 12:32:21 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 21 | namespace 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 | |
| 33 | template <typename T, typename L = std::mutex, int FLAGS = 0> |
| 34 | class LockItem { |
| 35 | protected: |
| 36 | mutable L mLock; |
| 37 | mutable T mT; |
| 38 | |
| 39 | public: |
| 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 | |
| 111 | template <typename T> |
| 112 | using atomic_wp = LockItem<::android::wp<T>>; |
| 113 | |
| 114 | template <typename T> |
| 115 | using atomic_sp = LockItem< |
| 116 | ::android::sp<T>, std::mutex, LockItem<::android::sp<T>>::FLAG_DTOR_OUT_OF_LOCK>; |
| 117 | |
Andy Hung | 638f45b | 2021-01-18 20:02:56 -0800 | [diff] [blame^] | 118 | /** |
| 119 | * Defers a function to run in the RAII destructor. |
| 120 | * A C++ implementation of Go _defer_ https://golangr.com/defer/. |
| 121 | */ |
| 122 | class Defer { |
| 123 | public: |
| 124 | template <typename U> |
| 125 | explicit Defer(U &&f) : mThunk(std::forward<U>(f)) {} |
| 126 | ~Defer() { mThunk(); } |
| 127 | |
| 128 | private: |
| 129 | const std::function<void()> mThunk; |
| 130 | }; |
| 131 | |
Andy Hung | 328d677 | 2021-01-12 12:32:21 -0800 | [diff] [blame] | 132 | } // namespace android::mediautils |
| 133 | |