kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #25310
LIB_PART weak_ptr-technique
Hello,
in anticipation of the eeschema rework I'm looking through the source
base to get a feeling for the current data model. A year or two ago I
filed a bug report regarding the "weak_ptr"-technique that LIB_PART is
using.
The offending line reads:
PART_SPTR m_me; ///<
http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/sp_techniques.html#weak_without_shared
where PART_SPTR is defined as "std::shared_ptr<LIB_PART>". The m_me is
initialized with "this" and a null_deleter, which does nothing when
m_me's lifetime ends, ie. it does not delete the pointee-object as
opposed to default behaviour of shared_ptrs. The commented technique
describes a usage pattern, where a !!!weak_ptr!!! to the object is
handed out from a member function. That way, the handed out weak_ptrs
get invalidated when the lifetime of m_me ends. However, a valid
weak_ptr can always be promoted to a shared_ptr, which results in a
shared_ptr to the object which does not keep the pointee-object alive
(which actually breaks normal assumptions one has about holding a
shared_ptr to an object)! So this described technique implicitly needs
the user to _not_ store promoted shared_ptrs after temporary promotion
from the weak_ptr, and even then this pattern totally falls apart once
multithreading kicks in. On top of that, the implementation in LIB_PART
does not return a weak_ptr, it returns the shared_ptr, rendering the
technique completely void unless client code drops the shared_ptr in
favor of a weak_ptr.
To make it a bit clearer, following is some source code...
========== source snip ===============
#include <memory>
#include <iostream>
struct null_deleter {
template< typename T >
void operator()(T*) {}
};
struct weak_ptr_me {
weak_ptr_me()
: me_( this, null_deleter() ), pointer_(&some_member_)
{}
~weak_ptr_me() {
pointer_ = nullptr;
}
std::shared_ptr< weak_ptr_me > me_;
std::shared_ptr< weak_ptr_me > get_me() { return me_; }
void funky() {
// need pointer access to avoid not crashing by luck
*pointer_ = 4;
std::cout << this << ": funky me!" << std::endl;
}
int* pointer_;
int some_member_;
};
int main()
{
std::shared_ptr< weak_ptr_me > ptr;
{
weak_ptr_me* me = new weak_ptr_me();
me->funky();
ptr = me->get_me();
delete me;
}
if (ptr) {
// even though ptr thinks the object is alive, it is really not.
ptr->funky();
}
return 0;
}
=================== source snip =============
...which will certainly result in an access violation. I thought I might
clarify this but it will hopefully be gone soon anyways.
Cheers!
Michael