Boost logo

Boost Users :

From: Jason Winnebeck (yg-boost-users_at_[hidden])
Date: 2003-07-17 07:53:15


Hello everyone,

I am new to this list and a little new to Boost. I have a question on
program design when using the excellent boost shared pointer's library.

I am converting my old network library that uses explicit memory
management to use shared_ptr where appropriate. So far this has gone
well but I am having problem with cycles.

This post is more of request for comments on my thought process, and if
there is a "traditional" or better way of handling these issues. The
question in general is "How to deal with the cycles that come about from
event generator/listener designs?" The problem in more specific terms
with my thinking follows.

I'm having a problem with trying to mitigate reference cycles in a event
generator/event listener paradigm. One creates a listener object, and
registers itself as a listener of that object. As a result of the event,
it may want to invoke methods of that object, so it contains a reference
to that object.

So Generator contains a reference to Listener, and Listener contains a
reference to Generator. This is a problem with reference counted pointers.

People tell me that cycles in general signify a flaw, so perhaps my
fundamental design is indeed flawed. A real example from my library is
a Connection object representing a network connection. Library user
derives a new class implementing the ConnectionListener interface and
registers it with Connection. When a packet arrives an event in the
listener is invoked, and let's say the listener wants to process the
packet and send a packet in response. It does this by passing a packet
to the Connection. Thus the Connection refers to the listener, and the
listener refers to the Connection (to send a packet).

There are some possible solutions. One way to break cycles is to use
weak references. In this way you can observe an object without
preventing it from being collected.

In the general case, cycles are very bad because you aren't sure which
object can be deleted when. However in this case the relationship is
simple. Generator can only generate events when it is alive, and
Listener will only try to interact with Generator as a response to its
event (so we know Generator is alive if we got its event).

One could use a weak pointer in the Generator to the Listener, but then
the Listener may die as soon as it is registered. The Listener can use a
weak pointer to the Generator, and this seems intuitive since the
Listener is merely an observer, but accessing a weak pointer safely in a
multithreaded environment is expensive (I think so?) when we are
positive it exists.

The issue is compounded in my library since Generator is an object that
I have written (therefore I can trust it to understand all of my
"choices"), while the Listener is defined by the user. So I prefer to
make the task as easy as possible on the user -- so I want to allow the
user to use weak or strong references if possible so he doesn't have to
worry about cycles. If I must impose a restriction along the lines of
"you can have a pointer to object type x", I will, but I much prefer to
not have restrictions like that.

One way I've been considering (and have implemented some already) is to
have the Generator release all its Listeners when it is done generating
events. This works, but in my old API I have ways to retrieve the set
listener, and this cannot work since the set listener may go to NULL at
any time (as a weak pointer). So my best option for now seems to be to
break my old API by allowing the user to set listeners and objects but
not be able to retrieve them back (or allow retrieve function knowing
that an empty shared_ptr may be returned).

Another possible way is for the Generator always to pass a reference to
itself in the event. This is possible and clean and very fast, but
requires major changes to my API, but the prior way of disallowing gets
is safe in that it allows the user to not have to think twice about
keeping a reference to my object.

I have more ideas but this post is getting long. If you have comments
about how something like this using shared_ptr is typically designed, or
comments related to my more specific problem, I would be grateful.

Thanks in advance,
Jason Winnebeck


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net