I tried out sp_collector.cpp. Here's my code (the Network Simulation Library):

    #include <iostream>
    #include <set>
    #include <boost/utility.hpp>
    #include <boost/shared_ptr.hpp>

    using namespace std;
    using namespace boost;

struct NodeImpl;

NodeImpl* NodeImpl_Create(int id);

    struct Node {
        Node(int id) : pimpl_(NodeImpl_Create(id)) { }
        Node(const Node& n) : pimpl_(n.pimpl_) { }
        Node() { }
        void Connect(const Node& n);
    private:
        shared_ptr<NodeImpl> pimpl_;
        friend bool operator<(const Node& n1, const Node& n2);
    };

    bool operator<(const Node& n1, const Node& n2) {
        return n1.pimpl_ < n2.pimpl_;
    }

    struct NodeImpl : noncopyable {
        NodeImpl(int id) : id_(id) { cout << id_ << " lives\n"; }
        ~NodeImpl() { cout << id_ << " dies\n"; }
    private:
        int id_;
        set<Node> nodes_;
        friend struct Node;
    };

NodeImpl* NodeImpl_Create(int id) { return new NodeImpl(id); }

    void Node::Connect(const Node& n) {
        pimpl_->nodes_.insert(n);
    }

    int main() {
        {
            Node n1(1);

            {
                Node n2(2), n3(3), n4(4);

                n1.Connect(n2);
                n2.Connect(n2);
                n3.Connect(n3);
                n4.Connect(n4);
            }

            cout << "n1 points to the cluster {n2, n3, n4}\n";
        }

cout << "n2, n3, n4 remain alive. That sucks.\n";

        void free_unreachable_objects();
        free_unreachable_objects();

        cout << "Done\n";
    }

Running it, you'll see that free_unreachable_objects() fails to find/delete the dangling n2, n3 and n4.

Investigating, I determined that the reason is this: in order for sp_collector to do its magic, it must always be the case that shared_ptr's are contained directly within things pointed to by other shared_ptr's. In my example, this invariant is violated, because I'm storing Node's in a set. A Node contains a shared_ptr. You can have a Node directly in a NodeImpl, no problem. But you can't have a Node in a disembodied bit of memory, as produced by set to store its contents.

In the following modification, I change NodeImpl so that it contains the the shared_ptr's directly:

    26a27,28
    > const int MAX_NODES = 10;
    >
    28c30
    <     NodeImpl(int id) : id_(id) { cout << id_ << " lives\n"; }
    ---
    >     NodeImpl(int id) : id_(id), nnodes_(0) {
                                       cout << id_ << " lives\n"; }
    32c34,35
    <     set<Node> nodes_;
    ---
    >     Node nodes_[MAX_NODES];
    >     int nnodes_;
    39c42,43
    <     pimpl_->nodes_.insert(n);
    ---
    >     if (pimpl_->nnodes_ < MAX_NODES)
    >         pimpl_->nodes_[pimpl_->nnodes_++] = n;

Now, sp_collector succeeds in finding & deleting the stale objects. Unfortunately, it means that I don't get to use the standard containers. All I get to use is a rotten array. Boo hoo!

And so, sp_collector suffers the same problem as shared_cyclic_ptr, and requires the same remedy: that is, it is necessary to be able to "tag" a container as being one which might hold "internal" shared_ptr's.

Any interest in modifying shared_ptr to support something like this?


- Chuck



_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to