blob: b2dfc8acc73674ac621a663777ad8a05c8756a94 [file] [log] [blame] [edit]
// Copyright 2023 The Dawn Authors
//
// 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.
#ifndef SRC_DAWN_COMMON_MUTEXPROTECTED_H_
#define SRC_DAWN_COMMON_MUTEXPROTECTED_H_
#include <mutex>
#include <utility>
#include "dawn/common/Mutex.h"
#include "dawn/common/Ref.h"
namespace dawn {
template <typename T>
class MutexProtected;
namespace detail {
template <typename T>
struct MutexProtectedTraits {
using MutexType = std::mutex;
using LockType = std::lock_guard<std::mutex>;
using ObjectType = T;
static MutexType CreateMutex() { return std::mutex(); }
static std::mutex& GetMutex(MutexType& m) { return m; }
static ObjectType* GetObj(T* const obj) { return obj; }
static const ObjectType* GetObj(const T* const obj) { return obj; }
};
template <typename T>
struct MutexProtectedTraits<Ref<T>> {
using MutexType = Ref<Mutex>;
using LockType = Mutex::AutoLock;
using ObjectType = T;
static MutexType CreateMutex() { return AcquireRef(new Mutex()); }
static Mutex* GetMutex(MutexType& m) { return m.Get(); }
static ObjectType* GetObj(Ref<T>* const obj) { return obj->Get(); }
static const ObjectType* GetObj(const Ref<T>* const obj) { return obj->Get(); }
};
// Guard class is a wrapping class that gives access to a protected resource after acquiring the
// lock related to it. For the lifetime of this class, the lock is held.
template <typename T, typename Traits>
class Guard {
public:
using ReturnType = typename UnwrapRef<T>::type;
// It's the programmer's burden to not save the pointer/reference and reuse it without the lock.
ReturnType* operator->() { return Traits::GetObj(mObj); }
ReturnType& operator*() { return *Traits::GetObj(mObj); }
const ReturnType* operator->() const { return Traits::GetObj(mObj); }
const ReturnType& operator*() const { return *Traits::GetObj(mObj); }
private:
friend class MutexProtected<T>;
Guard(T* obj, typename Traits::MutexType& mutex) : mLock(Traits::GetMutex(mutex)), mObj(obj) {}
typename Traits::LockType mLock;
T* const mObj;
};
} // namespace detail
// Wrapping class used for object members to ensure usage of the resource is protected with a mutex.
// Example usage:
// class Allocator {
// public:
// Allocation Allocate();
// void Deallocate(Allocation&);
// };
// class AllocatorUser {
// public:
// void OnlyAllocate() {
// auto allocation = mAllocator->Allocate();
// }
// void AtomicAllocateDeallocate() {
// // Operations:
// // - acquire lock
// // - Allocate, Deallocate
// // - release lock
// mAllocator.Use([](auto allocator) {
// auto allocation = allocator->Allocate();
// allocator->Deallocate(allocation);
// });
// }
// void NonAtomicAllocateDeallocate() {
// // Operations:
// // - acquire lock, Allocate, release lock
// // - acquire lock, Deallocate, release lock
// auto allocation = mAllocator->Allocate();
// mAllocator->Deallocate(allocation);
// }
// private:
// MutexProtected<Allocator> mAllocator;
// };
template <typename T>
class MutexProtected {
public:
using Traits = detail::MutexProtectedTraits<T>;
using Usage = detail::Guard<T, Traits>;
using ConstUsage = detail::Guard<const T, Traits>;
MutexProtected() : mMutex(Traits::CreateMutex()) {}
template <typename... Args>
// NOLINTNEXTLINE(runtime/explicit) allow implicit construction
MutexProtected(Args&&... args)
: mMutex(Traits::CreateMutex()), mObj(std::forward<Args>(args)...) {}
Usage operator->() { return Use(); }
ConstUsage operator->() const { return Use(); }
template <typename Fn>
auto Use(Fn&& fn) {
return fn(Use());
}
private:
Usage Use() { return Usage(&mObj, mMutex); }
ConstUsage Use() const { return ConstUsage(&mObj, mMutex); }
typename Traits::MutexType mMutex;
T mObj;
};
} // namespace dawn
#endif // SRC_DAWN_COMMON_MUTEXPROTECTED_H_