Boost logo

Boost :

From: Chuck Messenger (chuckm_at_[hidden])
Date: 2003-05-29 09:45:07


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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk