1 // Copyright 2020 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #pragma once 16 17 #include "aemu/base/synchronization/Lock.h" 18 #include "host-common/VmLock.h" 19 20 #include <algorithm> 21 #include <functional> 22 #include <memory> 23 #include <vector> 24 25 namespace android { 26 // All operations that change the global VM state (e.g. 27 // virtual device operations) should happen in a thread 28 // that holds the global VM lock. 29 // 30 // DeviceContextRunner is a helper template class used 31 // to ensure that a given operation is always performed 32 // in such a thread. more specifically: 33 // - If the current thread already owns the lock, 34 // the operation is performed as-is. 35 // - Otherwise, it is queued and will be run in the 36 // main-loop thread as soon as possible. 37 // 38 // Usage is the following: 39 // 40 // - Define a custom type |OP| corresponding to 41 // the state of each operation. It must be copyable. 42 // 43 // - Define a derived class of 44 // |DeviceContextRunner<OP>| that must implement the 45 // abstract performDeviceOperation(const OP& op) method. 46 // 47 // - Create a DeviceContextRunner<OP> instance, and call 48 // its init() method, passing a valid android::VmLock 49 // instance to it. NOTE: This should be called from 50 // the main loop thread, during emulation setup time!! 51 // 52 // - Whenever you want to perform an operation, call 53 // queueDeviceOperation(<op>) on it. If the current 54 // thread holds the lock, it will be 55 // performed immediately. Otherwise, it will be queued 56 // and run later. Hence the void return type of 57 // queueDeviceOperation; it is run asynchronously, so 58 // you cannot expect a return value. 59 60 enum class ContextRunMode { 61 DeferIfNotLocked, 62 DeferAlways 63 }; 64 65 template <typename T> 66 class DeviceContextRunner { 67 public: 68 struct TimerInterface { 69 std::function<void(DeviceContextRunner*, std::function<void()>)> installFunc; 70 std::function<void(DeviceContextRunner*)> uninstallFunc; 71 std::function<void(DeviceContextRunner*, uint64_t)> startWithTimeoutFunc; 72 }; 73 74 using AutoLock = android::base::AutoLock; 75 using Lock = android::base::Lock; 76 using VmLock = android::VmLock; 77 using PendingList = std::vector<T>; 78 79 // Looper parameter is for unit testing purposes. init(VmLock * vmLock,TimerInterface timerInterface)80 void init(VmLock* vmLock, TimerInterface timerInterface) { 81 mVmLock = vmLock; 82 mTimerInterface = timerInterface; 83 // TODO(digit): Find a better event abstraction. 84 // 85 // Operating on Looper::Timer objects is not supposed to be 86 // thread-safe, but it appears that their QEMU1 and QEMU2 specific 87 // implementation *is* (see qemu-timer.c:timer_mod()). 88 // 89 // This means that in practice the code below is safe when used 90 // in the context of an Android emulation engine. However, we probably 91 // need a better abstraction that also works with the generic 92 // Looper implementation (for unit-testing) or any other kind of 93 // runtime environment, should we one day link AndroidEmu to a 94 // different emulation engine. 95 // 96 // Solution: Feed a callback interface that acts depending on the timer object 97 mTimerInterface.installFunc(this, [this]() { this->onTimerEvent(); }); 98 } 99 setContextRunMode(ContextRunMode mode)100 void setContextRunMode(ContextRunMode mode) { 101 AutoLock lock(mLock); 102 mContextRunMode = mode; 103 } 104 105 protected: 106 // Disable delete-through-interface. ~DeviceContextRunner()107 ~DeviceContextRunner() { 108 mTimerInterface.uninstallFunc(this); 109 } 110 111 // To be implemented by the class that derives DeviceContextRunner: 112 // the method that actually touches the virtual device. 113 virtual void performDeviceOperation(const T& op) = 0; 114 115 // queueDeviceOperation: If the VM lock is currently held, 116 // we are OK to actually perform device operations. 117 // Otherwise, we need to add the request to a pending 118 // set of requests, to be finished later when we do have the VM lock. queueDeviceOperation(const T & op)119 void queueDeviceOperation(const T& op) { 120 if (mContextRunMode == ContextRunMode::DeferIfNotLocked && 121 mVmLock->isLockedBySelf()) { 122 // Perform the operation correctly since the current thread 123 // already holds the lock that protects the global VM state. 124 performDeviceOperation(op); 125 } else { 126 // Queue the operation in the mPendingMap structure, then 127 // restart the timer. 128 AutoLock lock(mLock); 129 mPending.push_back(op); 130 lock.unlock(); 131 132 // NOTE: See TODO above why this is thread-safe when used with 133 // QEMU1 and QEMU2. 134 mTimerInterface.startWithTimeoutFunc(this, 0); 135 } 136 } 137 138 // Remove all pending operations that match the passed predicate |op|. 139 template <class Predicate> removeAllPendingOperations(const Predicate & op)140 void removeAllPendingOperations(const Predicate& op) { 141 AutoLock lock(mLock); 142 mPending.erase(std::remove_if(mPending.begin(), mPending.end(), op), 143 mPending.end()); 144 } 145 146 // Run the passed functor |op| for all pending operations. 147 template <class Func> forEachPendingOperation(const Func & op)148 void forEachPendingOperation(const Func& op) const { 149 AutoLock lock(mLock); 150 for (const auto& p : mPending) { op(p); } 151 } 152 153 protected: numPending()154 size_t numPending() const { 155 AutoLock lock(mLock); 156 return mPending.size(); 157 } 158 159 private: onTimerEvent()160 void onTimerEvent() { 161 AutoLock lock(mLock); 162 for (const auto& elt : mPending) { 163 performDeviceOperation(elt); 164 } 165 mPending.clear(); 166 } 167 168 VmLock* mVmLock = nullptr; 169 ContextRunMode mContextRunMode = ContextRunMode::DeferIfNotLocked; 170 171 mutable Lock mLock; 172 PendingList mPending; 173 TimerInterface mTimerInterface; 174 }; 175 176 } // namespace android 177