/* * Copyright (C) 2019 Igalia, S.L. * Copyright (C) 2019 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * aint with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #pragma once #include #include namespace WTF { // By default invalid access checks are only done in Debug builds. #if !defined(ENABLE_DATA_MUTEX_CHECKS) #if defined(NDEBUG) #define ENABLE_DATA_MUTEX_CHECKS 0 #else #define ENABLE_DATA_MUTEX_CHECKS 1 #endif #endif #if ENABLE_DATA_MUTEX_CHECKS #define DATA_MUTEX_CHECK(expr) RELEASE_ASSERT(expr) #else #define DATA_MUTEX_CHECK(expr) #endif template class OwnerAwareLockAdapter { public: void lock() { DATA_MUTEX_CHECK(m_owner != &Thread::current()); // Thread attempted recursive lock (unsupported). m_lock.lock(); #if ENABLE_DATA_MUTEX_CHECKS ASSERT(!m_owner); m_owner = &Thread::current(); #endif } void unlock() { #if ENABLE_DATA_MUTEX_CHECKS m_owner = nullptr; #endif m_lock.unlock(); } bool tryLock() { DATA_MUTEX_CHECK(m_owner != &Thread::current()); // Thread attempted recursive lock (unsupported). if (!m_lock.tryLock()) return false; #if ENABLE_DATA_MUTEX_CHECKS ASSERT(!m_owner); m_owner = &Thread::current(); #endif return true; } bool isLocked() const { return m_lock.isLocked(); } private: #if ENABLE_DATA_MUTEX_CHECKS Thread* m_owner { nullptr }; // Use Thread* instead of RefPtr since m_owner thread is always alive while m_owner is set. #endif LockType m_lock; }; using OwnerAwareLock = OwnerAwareLockAdapter; template class DataMutex { WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(DataMutex); public: template explicit DataMutex(Args&&... args) : m_data(std::forward(args)...) { } class LockedWrapper { public: explicit LockedWrapper(DataMutex& dataMutex) : m_mutex(dataMutex.m_mutex) , m_lockHolder(dataMutex.m_mutex) , m_data(dataMutex.m_data) { } T* operator->() { DATA_MUTEX_CHECK(m_mutex.isLocked()); return &m_data; } T& operator*() { DATA_MUTEX_CHECK(m_mutex.isLocked()); return m_data; } LockType& mutex() { return m_mutex; } Locker& lockHolder() { return m_lockHolder; } // Used to avoid excessive brace scoping when only small parts of the code need to be run unlocked. // Please be mindful that accessing the wrapped data from the callback is unsafe and will fail on assertions. // It's helpful to use a minimal lambda capture to be conscious of what data you're having access to in these sections. void runUnlocked(WTF::Function callback) { m_mutex.unlock(); callback(); m_mutex.lock(); } private: LockType& m_mutex; Locker m_lockHolder; T& m_data; }; private: LockType m_mutex; T m_data; }; } // namespace WTF