You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.8 KiB
169 lines
4.8 KiB
// Copyright 2016 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#pragma once
|
|
|
|
#include "base/Compiler.h"
|
|
|
|
namespace android {
|
|
|
|
// In QEMU2, each virtual CPU runs on its own host threads, but all these
|
|
// threads are synchronized through a global mutex, which allows the virtual
|
|
// device code to not care about them.
|
|
//
|
|
// However, if you have to call, from any other thread, a low-level QEMU
|
|
// function that operate on virtual devices (e.g. some Android pipe-related
|
|
// functions), you must acquire the global mutex before doing so, and release
|
|
// it after that.
|
|
|
|
// This header provides a convenience interface class you can use to do
|
|
// just that, i.e.:
|
|
//
|
|
// 1) To operate on the lock, call VmLock::get() to retrieve the
|
|
// current VmLock instance, then invoke its lock() and unlock()
|
|
// methods.
|
|
//
|
|
// 2) Glue code should call VmLock::set() to inject their own implementation
|
|
// into the process. The default implementation doesn't do anything.
|
|
//
|
|
|
|
class VmLock {
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(VmLock);
|
|
public:
|
|
VmLock() = default;
|
|
virtual ~VmLock();
|
|
|
|
// Lock the VM global mutex.
|
|
virtual void lock() {}
|
|
|
|
// Unlock the VM global mutex.
|
|
virtual void unlock() {}
|
|
|
|
// Returns true iff the lock is held by the current thread, false
|
|
// otherwise. Note that for a correct implementation, that doesn't
|
|
// only depend on the number of times that VmLock::lock() and
|
|
// VmLock::unlock() were called, but also on other QEMU threads that
|
|
// act on the global lock.
|
|
virtual bool isLockedBySelf() const { return true; }
|
|
|
|
// Return current VmLock instance. Cannot return nullptr.
|
|
// NOT thread-safe, but we don't expect multiple threads to call this
|
|
// concurrently at init time, and the worst that can happen is to leak
|
|
// a single instance.
|
|
static VmLock* get();
|
|
|
|
// Returns whether or not there is a VmLock.
|
|
// Does not instantiate a VmLock.
|
|
static bool hasInstance();
|
|
|
|
// Set new VmLock instance. Return old value, which cannot be nullptr and
|
|
// can be deleted by the caller. If |vmLock| is nullptr, a new default
|
|
// instance is created. NOTE: not thread-safe with regards to get().
|
|
static VmLock* set(VmLock* vmLock);
|
|
};
|
|
|
|
// Convenience class to perform scoped VM locking.
|
|
class ScopedVmLock {
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ScopedVmLock);
|
|
public:
|
|
ScopedVmLock(VmLock* vmLock = VmLock::get()) : mVmLock(vmLock) {
|
|
mVmLock->lock();
|
|
}
|
|
|
|
~ScopedVmLock() {
|
|
mVmLock->unlock();
|
|
}
|
|
|
|
private:
|
|
VmLock* const mVmLock;
|
|
};
|
|
|
|
// Convenience class to perform scoped VM locking (but does not try
|
|
// to lock twice).
|
|
class RecursiveScopedVmLock {
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(RecursiveScopedVmLock);
|
|
public:
|
|
RecursiveScopedVmLock(VmLock* vmLock = VmLock::get()) {
|
|
if (vmLock->isLockedBySelf()) {
|
|
mVmLock = nullptr;
|
|
} else {
|
|
mVmLock = vmLock;
|
|
vmLock->lock();
|
|
}
|
|
}
|
|
|
|
~RecursiveScopedVmLock() {
|
|
if (mVmLock) {
|
|
mVmLock->unlock();
|
|
}
|
|
}
|
|
|
|
private:
|
|
VmLock* mVmLock;
|
|
};
|
|
|
|
// Convenience class to perform scoped VM locking (but does not try
|
|
// to lock twice), but no-ops if there is no instance.
|
|
class RecursiveScopedVmLockIfInstance {
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(RecursiveScopedVmLockIfInstance);
|
|
public:
|
|
RecursiveScopedVmLockIfInstance() {
|
|
if (!VmLock::hasInstance()) return;
|
|
|
|
VmLock* vmLock = VmLock::get();
|
|
|
|
if (vmLock->isLockedBySelf()) {
|
|
mVmLock = nullptr;
|
|
} else {
|
|
mVmLock = vmLock;
|
|
vmLock->lock();
|
|
}
|
|
}
|
|
|
|
~RecursiveScopedVmLockIfInstance() {
|
|
if (mVmLock) {
|
|
mVmLock->unlock();
|
|
}
|
|
}
|
|
|
|
private:
|
|
VmLock* mVmLock = nullptr;;
|
|
};
|
|
|
|
// Another convenience class for a code that may run either under a lock or not
|
|
// but needs to ensure that some part of it runs without a VmLock.
|
|
class ScopedVmUnlock {
|
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ScopedVmUnlock);
|
|
public:
|
|
ScopedVmUnlock(VmLock* vmLock = VmLock::get()) {
|
|
if (vmLock->isLockedBySelf()) {
|
|
mVmLock = vmLock;
|
|
vmLock->unlock();
|
|
} else {
|
|
mVmLock = nullptr;
|
|
}
|
|
}
|
|
|
|
~ScopedVmUnlock() {
|
|
if (mVmLock) {
|
|
mVmLock->lock();
|
|
}
|
|
}
|
|
|
|
private:
|
|
VmLock* mVmLock;
|
|
};
|
|
|
|
} // namespace android
|