mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-07 03:40:15 +00:00
base: resolve dead lock in weak pointer templates
This commit eliminates the mutual interlaced taking of destruction lock, list lock and weak pointer locks that could lead to a dead-lock situation when a lock pointer was tried to construct while a weak object is in destruction progress. Now, all weak pointers are invalidated and dequeued at the very beginning of the weak object's destruction. Moreover, before a weak pointer gets invalidated during destruction of a weak object, it gets dequeued, and the list lock is freed again to avoid the former dead-lock. Fix #1607
This commit is contained in:
parent
64831c63c9
commit
0f05fa6fd4
@ -123,5 +123,8 @@ Platform_pd::Platform_pd(Allocator * md_alloc, char const *,
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
PWRN("not yet implemented");
|
PWRN("not yet implemented");
|
||||||
}
|
}
|
||||||
|
@ -258,6 +258,9 @@ Platform_pd::Platform_pd(Allocator * md_alloc, char const *,
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
/* unbind all threads */
|
/* unbind all threads */
|
||||||
while (Platform_thread *t = _next_thread()) unbind_thread(t);
|
while (Platform_thread *t = _next_thread()) unbind_thread(t);
|
||||||
|
|
||||||
|
@ -128,6 +128,9 @@ Platform_pd::Platform_pd()
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
for (unsigned i = 0; i < THREAD_MAX; i++) {
|
for (unsigned i = 0; i < THREAD_MAX; i++) {
|
||||||
if (_threads[i])
|
if (_threads[i])
|
||||||
_threads[i]->unbind();
|
_threads[i]->unbind();
|
||||||
|
@ -53,6 +53,9 @@ Platform_pd::Platform_pd(Allocator * md_alloc, char const *,
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
if (_pd_sel == ~0UL) return;
|
if (_pd_sel == ~0UL) return;
|
||||||
|
|
||||||
/* Revoke and free cap, pd is gone */
|
/* Revoke and free cap, pd is gone */
|
||||||
|
@ -354,6 +354,9 @@ Platform_pd::Platform_pd(signed pd_id, bool create)
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
/* unbind all threads */
|
/* unbind all threads */
|
||||||
while (Platform_thread *t = _next_thread()) unbind_thread(t);
|
while (Platform_thread *t = _next_thread()) unbind_thread(t);
|
||||||
|
|
||||||
|
@ -360,6 +360,9 @@ Platform_pd::Platform_pd(Allocator * md_alloc, char const *,
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
PT_DBG("Destroying all threads of pd %p", this);
|
PT_DBG("Destroying all threads of pd %p", this);
|
||||||
|
|
||||||
/* unbind all threads */
|
/* unbind all threads */
|
||||||
|
@ -170,6 +170,9 @@ Platform_pd::Platform_pd(Allocator * md_alloc, char const *,
|
|||||||
|
|
||||||
Platform_pd::~Platform_pd()
|
Platform_pd::~Platform_pd()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
Address_space::lock_for_destruction();
|
||||||
|
|
||||||
platform_specific()->free_core_sel(_vm_cnode_sel);
|
platform_specific()->free_core_sel(_vm_cnode_sel);
|
||||||
platform_specific()->free_core_sel(_vm_pad_cnode_sel);
|
platform_specific()->free_core_sel(_vm_pad_cnode_sel);
|
||||||
platform_specific()->free_core_sel(_cspace_cnode_sel);
|
platform_specific()->free_core_sel(_cspace_cnode_sel);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define _INCLUDE__BASE__WEAK_PTR_H_
|
#define _INCLUDE__BASE__WEAK_PTR_H_
|
||||||
|
|
||||||
#include <base/lock.h>
|
#include <base/lock.h>
|
||||||
|
#include <base/printf.h>
|
||||||
#include <util/list.h>
|
#include <util/list.h>
|
||||||
|
|
||||||
namespace Genode {
|
namespace Genode {
|
||||||
@ -43,21 +44,18 @@ class Genode::Weak_ptr_base : public Genode::List<Weak_ptr_base>::Element
|
|||||||
|
|
||||||
Lock mutable _lock;
|
Lock mutable _lock;
|
||||||
Weak_object_base *_obj;
|
Weak_object_base *_obj;
|
||||||
bool _valid; /* true if '_obj' points to an
|
|
||||||
existing object */
|
/*
|
||||||
|
* 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 _adopt(Weak_object_base *obj);
|
||||||
inline void _disassociate();
|
inline void _disassociate();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
|
||||||
* Return pointer to object if it exists, or 0 if object vanished
|
|
||||||
*
|
|
||||||
* \noapi
|
|
||||||
*/
|
|
||||||
Weak_object_base *obj() const { return _valid ? _obj: 0; }
|
|
||||||
|
|
||||||
explicit inline Weak_ptr_base(Weak_object_base *obj);
|
explicit inline Weak_ptr_base(Weak_object_base *obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -79,6 +77,13 @@ class Genode::Weak_ptr_base : public Genode::List<Weak_ptr_base>::Element
|
|||||||
*/
|
*/
|
||||||
inline bool operator == (Weak_ptr_base const &other) const;
|
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
|
* Inspection hook for unit test
|
||||||
*
|
*
|
||||||
@ -105,19 +110,16 @@ class Genode::Weak_object_base
|
|||||||
List<Weak_ptr_base> _list;
|
List<Weak_ptr_base> _list;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock used to defer the destruction of an object derived from
|
* Buffers dequeued weak pointer that get invalidated currently
|
||||||
* 'Weak_object_base'
|
|
||||||
*/
|
*/
|
||||||
Lock _destruct_lock;
|
Weak_ptr_base *_ptr_in_destruction = nullptr;
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destructor
|
* Lock to synchronize access to object
|
||||||
*
|
|
||||||
* \noapi
|
|
||||||
*/
|
*/
|
||||||
inline ~Weak_object_base();
|
Lock _lock;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called from 'Weak_object<T>' only
|
* To be called from 'Weak_object<T>' only
|
||||||
@ -129,13 +131,84 @@ class Genode::Weak_object_base
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception signals a weak pointer that the object
|
||||||
|
* is in destruction progress
|
||||||
|
*/
|
||||||
|
class In_destruction : Exception {};
|
||||||
|
|
||||||
|
~Weak_object_base()
|
||||||
|
{
|
||||||
|
if (_list.first())
|
||||||
|
PERR("Weak object %p not destructed properly "
|
||||||
|
"there are still dangling pointers to it", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* Mark object as safe to be destructed
|
||||||
*
|
*
|
||||||
* This method must be called by the destructor of a weak object to
|
* 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.
|
* defer the destruction until no 'Locked_ptr' is held to the object.
|
||||||
*/
|
*/
|
||||||
void lock_for_destruction() { _destruct_lock.lock(); }
|
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
|
* Inspection hook for unit test
|
||||||
@ -249,7 +322,7 @@ struct Genode::Locked_ptr : Genode::Locked_ptr_base
|
|||||||
* Only if valid, the locked pointer can be de-referenced. Otherwise,
|
* Only if valid, the locked pointer can be de-referenced. Otherwise,
|
||||||
* the attempt will result in a null-pointer access.
|
* the attempt will result in a null-pointer access.
|
||||||
*/
|
*/
|
||||||
bool is_valid() const { return curr != 0; }
|
bool is_valid() const { return curr != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -263,41 +336,24 @@ void Genode::Weak_ptr_base::_adopt(Genode::Weak_object_base *obj)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_obj = obj;
|
_obj = obj;
|
||||||
_valid = true;
|
|
||||||
|
|
||||||
|
{
|
||||||
Lock::Guard guard(_obj->_list_lock);
|
Lock::Guard guard(_obj->_list_lock);
|
||||||
_obj->_list.insert(this);
|
_obj->_list.insert(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Genode::Weak_ptr_base::_disassociate()
|
void Genode::Weak_ptr_base::_disassociate()
|
||||||
{
|
{
|
||||||
/* defer destruction of object */
|
/* defer destruction of object */
|
||||||
{
|
try {
|
||||||
Lock::Guard guard(_lock);
|
Lock::Guard guard(_lock);
|
||||||
|
|
||||||
if (!_valid)
|
if (_obj) _obj->disassociate(this);
|
||||||
return;
|
} catch(Weak_object_base::In_destruction&) {
|
||||||
|
_destruct_lock.lock();
|
||||||
_obj->_destruct_lock.lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Disassociate reference from object
|
|
||||||
*
|
|
||||||
* Because we hold the '_destruct_lock', we are safe to do
|
|
||||||
* the list operation. However, after we have released the
|
|
||||||
* 'Weak_ptr_base::_lock', the object may have invalidated
|
|
||||||
* the reference. So we must check for validity again.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_obj->_list_lock);
|
|
||||||
if (_valid)
|
|
||||||
_obj->_list.remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* release object */
|
|
||||||
_obj->_destruct_lock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -307,7 +363,7 @@ Genode::Weak_ptr_base::Weak_ptr_base(Genode::Weak_object_base *obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genode::Weak_ptr_base::Weak_ptr_base() : _obj(0), _valid(false) { }
|
Genode::Weak_ptr_base::Weak_ptr_base() : _obj(nullptr) { }
|
||||||
|
|
||||||
|
|
||||||
void Genode::Weak_ptr_base::operator = (Weak_ptr_base const &other)
|
void Genode::Weak_ptr_base::operator = (Weak_ptr_base const &other)
|
||||||
@ -316,9 +372,11 @@ void Genode::Weak_ptr_base::operator = (Weak_ptr_base const &other)
|
|||||||
if (&other == this)
|
if (&other == this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Weak_object_base *obj = other.obj();
|
|
||||||
_disassociate();
|
_disassociate();
|
||||||
_adopt(obj);
|
{
|
||||||
|
Lock::Guard guard(other._lock);
|
||||||
|
_adopt(other._obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -329,8 +387,7 @@ bool Genode::Weak_ptr_base::operator == (Weak_ptr_base const &other) const
|
|||||||
|
|
||||||
Lock::Guard guard_this(_lock), guard_other(other._lock);
|
Lock::Guard guard_this(_lock), guard_other(other._lock);
|
||||||
|
|
||||||
return (!_valid && !other._valid)
|
return (_obj == other._obj);
|
||||||
|| (_valid && other._valid && _obj == other._obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -348,39 +405,22 @@ Genode::Weak_ptr<T> Genode::Weak_object_base::_weak_ptr()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genode::Weak_object_base::~Weak_object_base()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_list_lock);
|
|
||||||
|
|
||||||
Weak_ptr_base *curr = 0;
|
|
||||||
while ((curr = _list.first())) {
|
|
||||||
|
|
||||||
Lock::Guard guard(curr->_lock);
|
|
||||||
curr->_valid = false;
|
|
||||||
_list.remove(curr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Genode::Locked_ptr_base::Locked_ptr_base(Weak_ptr_base &weak_ptr)
|
Genode::Locked_ptr_base::Locked_ptr_base(Weak_ptr_base &weak_ptr)
|
||||||
: curr(0)
|
: curr(nullptr)
|
||||||
{
|
{
|
||||||
Lock::Guard guard(weak_ptr._lock);
|
Lock::Guard guard(weak_ptr._lock);
|
||||||
|
|
||||||
if (!weak_ptr._valid)
|
if (!weak_ptr.obj()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
curr = weak_ptr._obj;
|
curr = weak_ptr.obj();
|
||||||
curr->_destruct_lock.lock();
|
curr->_lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genode::Locked_ptr_base::~Locked_ptr_base()
|
Genode::Locked_ptr_base::~Locked_ptr_base()
|
||||||
{
|
{
|
||||||
if (curr)
|
if (curr)
|
||||||
curr->_destruct_lock.unlock();
|
curr->_lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__WEAK_PTR_H_ */
|
#endif /* _INCLUDE__BASE__WEAK_PTR_H_ */
|
||||||
|
@ -84,6 +84,11 @@ class Genode::Trace::Source
|
|||||||
_unique_id(_alloc_unique_id()), _info(info), _control(control)
|
_unique_id(_alloc_unique_id()), _info(info), _control(control)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
~Source()
|
||||||
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
lock_for_destruction();
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************
|
/*************************************
|
||||||
** Interface used by TRACE service **
|
** Interface used by TRACE service **
|
||||||
|
@ -126,6 +126,9 @@ class View : public Same_buffer_list_elem,
|
|||||||
|
|
||||||
virtual ~View()
|
virtual ~View()
|
||||||
{
|
{
|
||||||
|
/* invalidate weak pointers to this object */
|
||||||
|
lock_for_destruction();
|
||||||
|
|
||||||
/* break link to our parent */
|
/* break link to our parent */
|
||||||
if (_parent)
|
if (_parent)
|
||||||
_parent->remove_child(*this);
|
_parent->remove_child(*this);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user