/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef LOCKED_PTR_H #define LOCKED_PTR_H #include #include namespace base { struct LockedPtrCore { LockedPtrCore(uintptr_t data) : data(data) {} std::atomic refCount{1}; // Atomic so that WeakLockedPtr::get can still read it. std::atomic data; QReadWriteLock lock{QReadWriteLock::Recursive}; }; enum class LockedPtrMode { Weak, Shared, Exclusive }; template class LockedPtr; // A WeakLockedPtr is something like shared_ptr. The T* value can only be // accessed by atomic read. template using WeakLockedPtr = LockedPtr; // A SharedLockedPtr is like WeakLockedPtr, but the T* value is prevented // from changing for the lifetime of the SharedLockedPtr by holding a // shared-exclusive mutex in shared mode. template using SharedLockedPtr = LockedPtr; // An ExclusiveLockedPtr is like SharedLockedPtr, but the mutex is held in // exclusive mode. Only in this mode can the T* value be changed. template using ExclusiveLockedPtr = LockedPtr; template class LockedPtr { template static constexpr bool canConstructFrom = std::is_same::value || std::is_same::value; public: constexpr LockedPtr() {} constexpr LockedPtr(std::nullptr_t) {} LockedPtr(const LockedPtr &that) { m_core = that.m_core; lock(); } LockedPtr &operator=(const LockedPtr &that) { unlock(); m_core = that.m_core; lock(); } LockedPtr(LockedPtr &&that) { m_core = that.m_core; that.m_core = nullptr; } LockedPtr &operator=(LockedPtr &&that) { unlock(); m_core = that.m_core; that.m_core = nullptr; } template>> LockedPtr(const LockedPtr &that) { m_core = that.m_core; lock(); } template>> LockedPtr &operator=(const LockedPtr &that) { unlock(); m_core = that.m_core; lock(); } template>> LockedPtr(LockedPtr &&that) { m_core = that.m_core; that.m_core = nullptr; } template>> LockedPtr &operator=(LockedPtr &&that) { unlock(); m_core = that.m_core; that.m_core = nullptr; } ~LockedPtr() { unlock(); } T *get() const { if (m_core) { if (mode == LockedPtrMode::Weak) return reinterpret_cast(m_core->data.load(std::memory_order_acquire)); else return reinterpret_cast(m_core->data.load(std::memory_order_relaxed)); } return nullptr; } void set(T *value) { static_assert(mode == LockedPtrMode::Exclusive, ""); DCHECK(m_core); m_core->data.store(reinterpret_cast(value), std::memory_order_release); } T &operator*() const { return *get(); } T *operator->() const { return get(); } explicit operator bool() const { return get(); } bool MaybeValid() const { return m_core; } static LockedPtr create(T *value) { return new LockedPtrCore(reinterpret_cast(value)); } private: template friend class LockedPtr; LockedPtr(LockedPtrCore *core) : m_core(core) {} void lock() { if (m_core) { ++m_core->refCount; if (mode == LockedPtrMode::Shared) m_core->lock.lockForRead(); else if (mode == LockedPtrMode::Exclusive) m_core->lock.lockForWrite(); } } void unlock() { if (m_core) { if (mode != LockedPtrMode::Weak) m_core->lock.unlock(); if (--m_core->refCount == 0) delete m_core; } } LockedPtrCore *m_core = nullptr; }; // This makes Bind check the pointer before calling the functor. template struct IsWeakReceiver> : std::true_type {}; // By converting the WeakLockedPtr into a SharedLockedPtr we prevent the // pointed-to object from being destroyed during the base::Callback::Run call. // // Unwrap() is called before checking the pointer, so there's no race condition. template struct BindUnwrapTraits> { static SharedLockedPtr Unwrap(const WeakLockedPtr &o) { return o; } }; // Like base::WeakPtrFactory, but InvalidateWeakPtrs *waits* until all currently // executing base::Callbacks are finished. Queued up base::Callbacks are still // canceled, exactly like with WeakPtrFactory. // // Consider, for example, the function // // void fun() // { // MyClass *myClass = new MyClass; // myClass->scheduleDoStuff(); // delete myClass; // ??? // } // // where // // class MyClass // { // public: // void scheduleDoStuff() // { // content::BrowserThread::PostTask( // content::BrowserThread::IO, FROM_HERE, // base::BindOnce(&MyClass::doStuff, m_weakPtrFactory.GetWeakPtr())); // } // void doStuff(); // private: // //base::WeakPtrFactory m_weakPtrFactory{this}; // base::LockedPtrFactory m_weakPtrFactory{this}; // }; // // What happens if the 'delete myClass' line is executed concurrently with // MyClass::doStuff? // // With WeakPtrs we get a segfault or perhaps memory corruption. // // With LockedPtrs we get no crash and no corruption: LockedPtrFactory's // destructor will wait until doStuff is done before continuing. template class LockedPtrFactory { public: explicit LockedPtrFactory(T *value) : m_ptr(WeakLockedPtr::create(value)) {} ~LockedPtrFactory() { InvalidateWeakPtrs(); } WeakLockedPtr GetWeakPtr() { return m_ptr; } WeakLockedPtr GetWeakPtr() const { return m_ptr; } SharedLockedPtr GetSharedPtr() { return m_ptr; } SharedLockedPtr GetSharedPtr() const { return m_ptr; } ExclusiveLockedPtr GetExclusivePtr() { return m_ptr; } ExclusiveLockedPtr GetExclusivePtr() const { return m_ptr; } void InvalidateWeakPtrs() { if (ExclusiveLockedPtr ptr = m_ptr) ptr.set(nullptr); } private: WeakLockedPtr m_ptr; }; } // namespace base #endif // !LOCKED_PTR_H