• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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