2015-02-05 08:53:25 -08:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2015-06-20 15:36:19 -07:00
|
|
|
#include <algorithm>
|
2015-02-05 08:53:25 -08:00
|
|
|
#include <mutex>
|
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
|
|
|
template <typename T>
|
2017-02-18 18:27:37 -08:00
|
|
|
class SynchronizedWrapper;
|
2015-02-05 08:53:25 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
|
|
|
|
* greatly reduces the chance that someone will access the wrapped resource without locking the
|
|
|
|
* mutex.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
class SynchronizedRef {
|
|
|
|
public:
|
|
|
|
SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
|
|
|
|
wrapper.mutex.lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
SynchronizedRef(SynchronizedRef&) = delete;
|
|
|
|
SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
|
|
|
|
o.wrapper = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
~SynchronizedRef() {
|
|
|
|
if (wrapper)
|
|
|
|
wrapper->mutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
SynchronizedRef& operator=(SynchronizedRef&) = delete;
|
|
|
|
SynchronizedRef& operator=(SynchronizedRef&& o) {
|
|
|
|
std::swap(wrapper, o.wrapper);
|
2015-09-11 19:00:21 -07:00
|
|
|
return *this;
|
2015-02-05 08:53:25 -08:00
|
|
|
}
|
|
|
|
|
2016-09-17 17:38:01 -07:00
|
|
|
T& operator*() {
|
|
|
|
return wrapper->data;
|
|
|
|
}
|
|
|
|
const T& operator*() const {
|
|
|
|
return wrapper->data;
|
|
|
|
}
|
2015-02-05 08:53:25 -08:00
|
|
|
|
2016-09-17 17:38:01 -07:00
|
|
|
T* operator->() {
|
|
|
|
return &wrapper->data;
|
|
|
|
}
|
|
|
|
const T* operator->() const {
|
|
|
|
return &wrapper->data;
|
|
|
|
}
|
2015-02-05 08:53:25 -08:00
|
|
|
|
|
|
|
private:
|
|
|
|
SynchronizedWrapper<T>* wrapper;
|
|
|
|
};
|
|
|
|
|
2017-02-18 18:27:37 -08:00
|
|
|
/**
|
|
|
|
* Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
|
|
|
|
* one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
|
|
|
|
* SyncronizedRef on this wrapper. Inspired by Rust's Mutex type
|
|
|
|
* (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
class SynchronizedWrapper {
|
|
|
|
public:
|
|
|
|
template <typename... Args>
|
|
|
|
SynchronizedWrapper(Args&&... args) : data(std::forward<Args>(args)...) {}
|
|
|
|
|
|
|
|
SynchronizedRef<T> Lock() {
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename U>
|
|
|
|
friend class SynchronizedRef;
|
|
|
|
|
|
|
|
std::mutex mutex;
|
|
|
|
T data;
|
|
|
|
};
|
|
|
|
|
2015-02-05 08:53:25 -08:00
|
|
|
} // namespace Common
|