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> LockItem(Args &&...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. load()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> store(U && 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 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> Defer(U && f)125 explicit Defer(U &&f) : mThunk(std::forward<U>(f)) {} ~Defer()126 ~Defer() { mThunk(); } 127 128 private: 129 const std::function<void()> mThunk; 130 }; 131 132 } // namespace android::mediautils 133 134