Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > shared_ptr cycles

Reply
Thread Tools

shared_ptr cycles

 
 
Christopher
Guest
Posts: n/a
 
      05-07-2009
I am not sure I understand this. I am need to before I get myself in
trouble!

"Because the implementation uses reference counting, cycles of
shared_ptr instances will not be reclaimed. For example, if main()
holds a shared_ptr to A, which directly or indirectly holds a
shared_ptr back to A, A's use count will be 2. Destruction of the
original shared_ptr will leave A dangling with a use count of 1. Use
weak_ptr to "break cycles." "

How would a shared pointer to A, directly or indirectly hold a shared
pointer back to A?
Shared pointers hold regular pointers as far as I know?

This is the only situation I can come up with, which I would more
easily describe as "If any shared pointer that already contains a raw
pointer B, is assigned to a shared pointer that already contains B,
the reference count is incremented and will not be decremented back to
zero, when those shared pointers are destroyed" I am not even sure if
that would happen, because ...isn't the reference count decremented
when a shared pointer is assigned?

int main()
{
boost::shared_ptr<MyClass> ptr1 = new MyClass();
boost::shared_ptr<MyClass> ptr2 = ptr1; // increment ref count
to 2

ptr1 = ptr2; // decrement ref count for assignment and then
increment?

// what's the ref count? Is this what they are describing as a
"cycle"?

return 0;
}

I am not sure if that is the situation or not. I can't think of any
other situation. Can someone please give examples?

I am about to implement a copy constructor and an assingment operator
for a class which contains shared pointers and I am wary that I might
be creating "cycles" if I do that.

I am also curious, if the above situation is what they are describing,
can't I prevent it by:

int main()
{
boost::shared_ptr<MyClass> ptr1 = new MyClass();
boost::shared_ptr<MyClass> ptr2 = ptr1; // increment ref count
to 2

if( ptr1.get() != ptr2.get() )
{
ptr1 = ptr2;
}

return 0;
}

and if so, why didn't they do it in the implementation of the shared
ptr?

I am sure I am missing something...
shared pointers have been working great so far, but I haven't had any
copy construction or assignment of classes that contained them yet.

 
Reply With Quote
 
 
 
 
Chris M. Thomasson
Guest
Posts: n/a
 
      05-07-2009
"Christopher" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
>I am not sure I understand this. I am need to before I get myself in
> trouble!
>
> "Because the implementation uses reference counting, cycles of
> shared_ptr instances will not be reclaimed. For example, if main()
> holds a shared_ptr to A, which directly or indirectly holds a
> shared_ptr back to A, A's use count will be 2. Destruction of the
> original shared_ptr will leave A dangling with a use count of 1. Use
> weak_ptr to "break cycles." "
>
> How would a shared pointer to A, directly or indirectly hold a shared
> pointer back to A?
> Shared pointers hold regular pointers as far as I know?
>
> This is the only situation I can come up with, which I would more
> easily describe as "If any shared pointer that already contains a raw
> pointer B, is assigned to a shared pointer that already contains B,
> the reference count is incremented and will not be decremented back to
> zero, when those shared pointers are destroyed" I am not even sure if
> that would happen, because ...isn't the reference count decremented
> when a shared pointer is assigned?
>
> int main()
> {
> boost::shared_ptr<MyClass> ptr1 = new MyClass();
> boost::shared_ptr<MyClass> ptr2 = ptr1; // increment ref count
> to 2
>
> ptr1 = ptr2; // decrement ref count for assignment and then
> increment?
>
> // what's the ref count? Is this what they are describing as a
> "cycle"?
>
> return 0;
> }
> [...]


That's not an example of a cycle; try something like this:


<quick and dirty pseudo-code>
__________________________________________________ ____________
struct foo {
boost::shared_ptr<foo> m_cycle;
};

int main() {
{
boost::shared_ptr<foo> p(new foo);
p->m_cycle = p;
}
// the `foo' object created above is now leaked!
return 0;
}
__________________________________________________ ____________

 
Reply With Quote
 
 
 
 
Christopher
Guest
Posts: n/a
 
      05-07-2009
On May 6, 11:31*pm, "Chris M. Thomasson" <(E-Mail Removed)> wrote:
> "Christopher" <(E-Mail Removed)> wrote in message
>
> news:(E-Mail Removed)...
>
>
>
>
>
> >I am not sure I understand this. I am need to before I get myself in
> > trouble!

>
> > "Because the implementation uses reference counting, cycles of
> > shared_ptr instances will not be reclaimed. For example, if main()
> > holds a shared_ptr to A, which directly or indirectly holds a
> > shared_ptr back to A, A's use count will be 2. Destruction of the
> > original shared_ptr will leave A dangling with a use count of 1. Use
> > weak_ptr to "break cycles." "

>
> > How would a shared pointer to A, directly or indirectly hold a shared
> > pointer back to A?
> > Shared pointers hold regular pointers as far as I know?

>
> > This is the only situation I can come up with, which I would more
> > easily describe as "If any shared pointer that already contains a raw
> > pointer B, is assigned to a shared pointer that already contains B,
> > the reference count is incremented and will not be decremented back to
> > zero, when those shared pointers are destroyed" I am not even sure if
> > that would happen, because ...isn't the reference count decremented
> > when a shared pointer is assigned?

>
> > int main()
> > {
> > * boost::shared_ptr<MyClass> ptr1 = new MyClass();
> > * boost::shared_ptr<MyClass> ptr2 = ptr1; * * // increment ref count
> > to 2

>
> > * ptr1 = ptr2; *// decrement ref count for assignment and then
> > increment?

>
> > * // what's the ref count? Is this what they are describing as a
> > "cycle"?

>
> > * return 0;
> > }
> > [...]

>
> That's not an example of a cycle; try something like this:
>
> <quick and dirty pseudo-code>
> __________________________________________________ ____________
> struct foo {
> * boost::shared_ptr<foo> m_cycle;
>
> };
>
> int main() {
> * {
> * * boost::shared_ptr<foo> p(new foo);
> * * p->m_cycle = p;
> * }
> * // the `foo' object created above is now leaked!
> * return 0;}
>
> __________________________________________________ ____________- Hide quoted text -
>
> - Show quoted text -




I don't understand the underlying reason the reference count becomes
incorrect. I need to understand it more thoroughly to prevent it.
Can we walk through what the reference count is at each step?

Here is what I envision, but might be incorrect:

{
boost::shared_ptr<foo> p(new foo); // ref count is 1
p->m_cycle = p; // ref count becomes 2 because it
was assigned
} // it was actually assigned to
m_cycle
--- block end-----

// p is being destroyed because it is out of scope
// ref count becomes 1
// When p is destroyed, m_cycle still contains a raw pointer that
points to the address of foo
// m_cycle is no longer accessable

So, this scenario is when a smart pointer<t> that points to an t
object that contains a smart pointer<t>
If I make sure that no object contains smart pointers capable of
pointing to the object type, am I safe?
Are there more scenarios I should be wary of?

I am trying to come up with some method of thinking that I can prevent
a cycle from occuring as I implement my objects.

So far, I beleive my current scenario to be a safe one:

where I have an object that contains a smart pointer to differant
object type.
I implement a copy constructor for the object
I assign the smart pointer member to the copied object's smart pointer
member.
I do the same in an assignment operator

Should I be checking that the two smart pointers lhs and rhs do not
already point to the same thing before assignment?
Or am I safe?

I want to have this down pat as other scenarios are sure to arise.









 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      05-07-2009
On May 7, 1:37 pm, Christopher <(E-Mail Removed)> wrote:
> On May 6, 11:31 pm, "Chris M. Thomasson" <(E-Mail Removed)> wrote:
> > "Christopher" <(E-Mail Removed)> wrote in message


> >news:(E-Mail Removed)....


> > >I am not sure I understand this. I am need to before I get
> > >myself in trouble!


> > > "Because the implementation uses reference counting,
> > > cycles of shared_ptr instances will not be reclaimed. For
> > > example, if main() holds a shared_ptr to A, which directly
> > > or indirectly holds a shared_ptr back to A, A's use count
> > > will be 2. Destruction of the original shared_ptr will
> > > leave A dangling with a use count of 1. Use weak_ptr to
> > > "break cycles." "


> > > How would a shared pointer to A, directly or indirectly
> > > hold a shared pointer back to A? Shared pointers hold
> > > regular pointers as far as I know?


> > > This is the only situation I can come up with, which I
> > > would more easily describe as "If any shared pointer that
> > > already contains a raw pointer B, is assigned to a shared
> > > pointer that already contains B, the reference count is
> > > incremented and will not be decremented back to zero, when
> > > those shared pointers are destroyed" I am not even sure if
> > > that would happen, because ...isn't the reference count
> > > decremented when a shared pointer is assigned?


> > > int main()
> > > {
> > > boost::shared_ptr<MyClass> ptr1 = new MyClass();
> > > boost::shared_ptr<MyClass> ptr2 = ptr1; // increment ref count
> > > to 2


> > > ptr1 = ptr2; // decrement ref count for assignment and then
> > > increment?


> > > // what's the ref count? Is this what they are describing as a
> > > "cycle"?


> > > return 0;
> > > }
> > > [...]


> > That's not an example of a cycle; try something like this:


> > <quick and dirty pseudo-code>
> > __________________________________________________ ____________
> > struct foo {
> > boost::shared_ptr<foo> m_cycle;


> > };

>
> > int main() {
> > {
> > boost::shared_ptr<foo> p(new foo);
> > p->m_cycle = p;
> > }
> > // the `foo' object created above is now leaked!
> > return 0;}


> > __________________________________________________ ____________


> I don't understand the underlying reason the reference count
> becomes incorrect.


It doesn't become incorrect. The rule for reference counting is
that the object will be deleted when there are no more pointers
to it. In the above, there will be no more pointers to the foo
object in p only once the foo object has been deleted. And it
won't be deleted until there are no more pointers to it.

This is what is called a cycle: starting at one of the managed
objects, you can navigate, using shared_ptr, through a cycle
leading back to the original object. So even if all pointers
external to the objects cease to exist, there are still pointers
to them.

The obvious solution to this problem is to use garbage
collection for memory management, which frees the memory if no
"reachable" pointers to it exist---that "reachable" makes a
critical difference. Often, a better solution would be to not
use dynamic allocation at all. And in many cases where dynamic
allocation is necessary, the objects involved have definite
lifetimes anyway, so no lifetime management is necessary (nor
should be used, since it will violate the contract). In
practice, there are really very few cases where shared_ptr is
appropriate.

> I need to understand it more thoroughly to prevent it. Can we
> walk through what the reference count is at each step?


> Here is what I envision, but might be incorrect:


> {
> boost::shared_ptr<foo> p(new foo); // ref count is 1
> p->m_cycle = p; // ref count becomes 2 because it
> was assigned} // it was actually assigned to


> m_cycle
> --- block end-----


> // p is being destroyed because it is out of scope
> // ref count becomes 1
> // When p is destroyed, m_cycle still contains a raw pointer that
> points to the address of foo
> // m_cycle is no longer accessable


> So, this scenario is when a smart pointer<t> that points to an
> t object that contains a smart pointer<t> If I make sure that
> no object contains smart pointers capable of pointing to the
> object type, am I safe? Are there more scenarios I should be
> wary of?


Cycles may involve more than one object, and may involve objects
of different types. If you never have a shared_ptr to an object
which might contain a shared_ptr, you are safe. Otherwise, the
analysis is more difficult. In practice, this isn't too great
a problem. Objects to which shared_ptr is reasonable generally
don't have shared_ptr, naturally. Provided you use shared_ptr
intelligently.

(In my own RefCntPtr, the objects pointed to must derive from
RefCntObj. This is a lot more robust than shared_ptr, since you
can't get two different counters for the same object, and by
requiring the specific base class, it is easy to ensure that
RefCntPtr can only point to objects which are designed with it
in mind---and which, thus, don't contain RefCntPtr.)

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
Bart van Ingen Schenau
Guest
Posts: n/a
 
      05-07-2009
Christopher wrote:

> On May 6, 11:31 pm, "Chris M. Thomasson" <(E-Mail Removed)> wrote:
>>
>> That's not an example of a cycle; try something like this:
>>
>> <quick and dirty pseudo-code>
>> __________________________________________________ ____________
>> struct foo {
>> boost::shared_ptr<foo> m_cycle;
>>
>> };
>>
>> int main() {
>> {
>> boost::shared_ptr<foo> p(new foo);
>> p->m_cycle = p;
>> }
>> // the `foo' object created above is now leaked!
>> return 0;}
>>
>> __________________________________________________ ____________- Hide
>> quoted text -
>>
>> - Show quoted text -

>
>
>
> I don't understand the underlying reason the reference count becomes
> incorrect.


That is because the reference count does not become incorrect. The
problem is that the (shared) pointer referring to the foo object is also
not accessible from the program.

> I need to understand it more thoroughly to prevent it.
> Can we walk through what the reference count is at each step?
>
> Here is what I envision, but might be incorrect:
>
> {
> boost::shared_ptr<foo> p(new foo); // ref count is 1
> p->m_cycle = p; // ref count becomes 2 because it
> was assigned
> } // it was actually assigned to
> m_cycle
> --- block end-----
>
> // p is being destroyed because it is out of scope
> // ref count becomes 1
> // When p is destroyed, m_cycle still contains a raw pointer that
> points to the address of foo
> // m_cycle is no longer accessable


Yes, that is correct.
The ref count remains (correctly) at 1, because there is still the
m_cycle pointer that refers to the foo object.

>
> So, this scenario is when a smart pointer<t> that points to an t
> object that contains a smart pointer<t>
> If I make sure that no object contains smart pointers capable of
> pointing to the object type, am I safe?
> Are there more scenarios I should be wary of?


You also have a potential cycle if:
- class T contains a shared_ptr<U>, and
- class U contains a shared_ptr<T>
(or with even more classes involved)

But rest assured, such designs are rare and usually suspect due to the
circular references between the classes.

The biggest risk for creating cycles in shared_ptr's is if you
incorrectly use them to implement a doubly-linked list or tree structure
or a circular list.
Usually it is better to use standard containers instead of rolling your
own, but if you must, you must not use shared_ptr because its semantics
are wrong for that use.

>
> I am trying to come up with some method of thinking that I can prevent
> a cycle from occuring as I implement my objects.


The only way I know of is to use pen and paper and think about your
design.
- Draw a diagram of the classes and the structural connections between
them
- If the class diagram shows a loop somewhere, draw a diagram showing
how the instances of the relevant classes are usually connected to each
other.
- If the instance diagram also shows a loop, ensure that at least one of
the links is *not* implemented with a shared_ptr.

>
> So far, I beleive my current scenario to be a safe one:
>
> where I have an object that contains a smart pointer to differant
> object type.
> I implement a copy constructor for the object
> I assign the smart pointer member to the copied object's smart pointer
> member.
> I do the same in an assignment operator


With smart pointers, there is typically no need to implement a custom
copy constructor or assignment operator. The compiler-generated ones
will work correctly.

>
> Should I be checking that the two smart pointers lhs and rhs do not
> already point to the same thing before assignment?
> Or am I safe?


There is no way to tell. Cycles between objects is a higher-level issue
that can not be detected when writing code.

>
> I want to have this down pat as other scenarios are sure to arise.


Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
cycles of shared_ptr avoid using weak_ptr. raj s C++ 4 07-28-2008 09:29 AM
#include <boost/shared_ptr.hpp> or #include "boost/shared_ptr.hpp"? Colin Caughie C++ 1 08-29-2006 02:19 PM
Program for drawing clock cycles? Allan Herriman VHDL 2 03-21-2006 01:15 PM
Combinational logic running over multiple clock cycles in Xilinx Taras_96 VHDL 2 08-22-2005 08:00 AM
what is "web development cycles"? popman ASP .Net 1 05-24-2004 03:22 AM



Advertisments