← Back to team overview

kicad-developers team mailing list archive

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