/* * \brief Utilities for object life-time management * \author Norman Feske * \date 2013-03-09 */ /* * Copyright (C) 2013-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _INCLUDE__BASE__WEAK_PTR_H_ #define _INCLUDE__BASE__WEAK_PTR_H_ #include #include #include namespace Genode { class Weak_object_base; class Weak_ptr_base; class Locked_ptr_base; template struct Weak_object; template struct Weak_ptr; template struct Locked_ptr; } /** * Type-agnostic base class of a weak pointer * * This class implements the mechanics of the the 'Weak_ptr' class template. * It is not used directly. */ class Genode::Weak_ptr_base : public Genode::List::Element { private: friend class Weak_object_base; friend class Locked_ptr_base; Lock mutable _lock { }; Weak_object_base *_obj { nullptr }; /* * This lock is used to synchronize destruction of a weak pointer * and its corresponding weak object that happen simultanously */ Lock mutable _destruct_lock { Lock::LOCKED }; inline void _adopt(Weak_object_base *obj); inline void _disassociate(); Weak_ptr_base(Weak_ptr_base const &); protected: explicit inline Weak_ptr_base(Weak_object_base *obj); public: /** * Default constructor, produces invalid pointer */ Weak_ptr_base() { } inline ~Weak_ptr_base(); /** * Assignment operator */ inline Weak_ptr_base &operator = (Weak_ptr_base const &other); /** * Test for equality */ inline bool operator == (Weak_ptr_base const &other) const; /** * Return pointer to object if it exists, or 0 if object vanished * * \noapi */ Weak_object_base *obj() const { return _obj; } /** * Inspection hook for unit test * * \noapi */ void debug_info() const; }; /** * Type-agnostic base class of a weak object */ class Genode::Weak_object_base { private: friend class Weak_ptr_base; friend class Locked_ptr_base; /** * List of weak pointers currently pointing to the object */ Lock _list_lock { }; List _list { }; /** * Buffers dequeued weak pointer that get invalidated currently */ Weak_ptr_base *_ptr_in_destruction = nullptr; /** * Lock to synchronize access to object */ Lock _lock { }; protected: /** * To be called from 'Weak_object' only * * \noapi */ template Weak_ptr _weak_ptr(); public: /** * This exception signals a weak pointer that the object * is in destruction progress */ class In_destruction : Exception {}; ~Weak_object_base() { if (_list.first()) error("Weak object ", this, " not destructed properly " "there are still dangling pointers to it"); } void disassociate(Weak_ptr_base *ptr) { if (!ptr) return; { Lock::Guard guard(_list_lock); /* * If the weak pointer that tries to disassociate is currently * removed to invalidate it by the weak object's destructor, * signal that fact to the pointer, so it can free it's lock, * and block until invalidation is finished. */ if (_ptr_in_destruction == ptr) throw In_destruction(); _list.remove(ptr); } } /** * Mark object as safe to be destructed * * This method must be called by the destructor of a weak object to * defer the destruction until no 'Locked_ptr' is held to the object. */ void lock_for_destruction() { /* * Loop through the list of weak pointers and invalidate them */ while (true) { /* * To prevent dead-locks we always have to hold * the order of lock access, therefore we first * dequeue one weak pointer and free the list lock again */ { Lock::Guard guard(_list_lock); _ptr_in_destruction = _list.first(); /* if the list is empty we're done */ if (!_ptr_in_destruction) break; _list.remove(_ptr_in_destruction); } { Lock::Guard guard(_ptr_in_destruction->_lock); _ptr_in_destruction->_obj = nullptr; /* * unblock a weak pointer that tried to disassociate * in the meantime */ _ptr_in_destruction->_destruct_lock.unlock(); } } /* * synchronize with locked pointers that already aquired * the lock before the corresponding weak pointer got invalidated */ _lock.lock(); } /** * Inspection hook for unit test * * \noapi */ void debug_info() const; }; class Genode::Locked_ptr_base { private: /* * Noncopyable */ Locked_ptr_base(Locked_ptr_base const &); Locked_ptr_base &operator = (Locked_ptr_base const &); protected: Weak_object_base *curr = nullptr; /** * Constructor * * \noapi */ inline Locked_ptr_base(Weak_ptr_base &weak_ptr); /** * Destructor * * \noapi */ inline ~Locked_ptr_base(); }; /** * Weak pointer to a given type * * A weak pointer can be obtained from a weak object (an object that inherits * the 'Weak_object' class template) and safely survives the lifetime of the * associated weak object. If the weak object disappears, all * weak pointers referring to the object are automatically invalidated. * To avoid race conditions between the destruction and use of a weak object, * a weak pointer cannot be de-reference directly. To access the object, a * weak pointer must be turned into a locked pointer ('Locked_ptr'). */ template struct Genode::Weak_ptr : Genode::Weak_ptr_base { /** * Default constructor creates invalid pointer */ Weak_ptr() { } /** * Copy constructor */ Weak_ptr(Weak_ptr const &other) : Weak_ptr_base(other.obj()) { } /** * Assignment operator */ inline Weak_ptr &operator = (Weak_ptr const &other) { *static_cast(this) = other; return *this; } }; /** * Weak object * * This class template must be inherited in order to equip an object with * the weak-pointer mechanism. * * \param T type of the derived class */ template struct Genode::Weak_object : Genode::Weak_object_base { /** * Obtain a weak pointer referring to the weak object */ Weak_ptr weak_ptr() { return _weak_ptr(); } /** * Const version of 'weak_ptr' * * This function is useful in cases where the returned weak pointer is * merely used for comparison operations. */ Weak_ptr const weak_ptr_const() const { /* * We strip off the constness of 'this' to reuse the internal non-const * code of the weak object. The executed operations are known to not * alter the state of the weak object. */ return const_cast(this)->_weak_ptr(); } }; /** * Locked pointer * * A locked pointer is constructed from a weak pointer. After construction, * its validity can (and should) be checked by calling the 'valid' * method. If the locked pointer is valid, the pointed-to object is known to * be locked until the locked pointer is destroyed. During this time, the * locked pointer can safely be de-referenced. * * The typical pattern of using a locked pointer is to declare it as a * local variable. Once the execution leaves the scope of the variable, the * locked pointer is destructed, which unlocks the pointed-to weak object. * It effectively serves as a lock guard. */ template struct Genode::Locked_ptr : Genode::Locked_ptr_base { Locked_ptr(Weak_ptr &weak_ptr) : Locked_ptr_base(weak_ptr) { } T *operator -> () { return static_cast(curr); } T &operator * () { return *static_cast(curr); } /** * Returns true if the locked pointer is valid * * Only if valid, the locked pointer can be de-referenced. Otherwise, * the attempt will result in a null-pointer access. */ bool valid() const { return curr != nullptr; } }; /******************** ** Implementation ** ********************/ void Genode::Weak_ptr_base::_adopt(Genode::Weak_object_base *obj) { _obj = obj; if (_obj) { Lock::Guard guard(_obj->_list_lock); _obj->_list.insert(this); } } void Genode::Weak_ptr_base::_disassociate() { /* defer destruction of object */ try { Lock::Guard guard(_lock); if (_obj) _obj->disassociate(this); } catch(Weak_object_base::In_destruction&) { _destruct_lock.lock(); } } Genode::Weak_ptr_base::Weak_ptr_base(Genode::Weak_object_base *obj) { _adopt(obj); } Genode::Weak_ptr_base & Genode::Weak_ptr_base::operator = (Weak_ptr_base const &other) { /* self assignment */ if (&other == this) return *this; _disassociate(); { Lock::Guard guard(other._lock); _adopt(other._obj); } return *this; } bool Genode::Weak_ptr_base::operator == (Weak_ptr_base const &other) const { if (&other == this) return true; Lock::Guard guard_this(_lock), guard_other(other._lock); return (_obj == other._obj); } Genode::Weak_ptr_base::~Weak_ptr_base() { _disassociate(); } template Genode::Weak_ptr Genode::Weak_object_base::_weak_ptr() { Weak_ptr_base result(this); return *static_cast *>(&result); } Genode::Locked_ptr_base::Locked_ptr_base(Weak_ptr_base &weak_ptr) : curr(nullptr) { Lock::Guard guard(weak_ptr._lock); if (!weak_ptr.obj()) return; curr = weak_ptr.obj(); curr->_lock.lock(); } Genode::Locked_ptr_base::~Locked_ptr_base() { if (curr) curr->_lock.unlock(); } #endif /* _INCLUDE__BASE__WEAK_PTR_H_ */