memory lost using cyclic pointers (shared_ptr)

Hi, I think that it is expected that shared_ptr cannot handle points that have cyclic references. In the following example, "a" points to "b" and "b" points to "a". There is a memory leak. I then modified it to remove the dependence using bare pointers, then the leak is resolved. But this is not a very satisfactory solution, as the programmer has to know whether to use bare point or shared_ptr. For very complexity program, it may not be possible to figure out it correctly. Does anybody know what is the best strategy for handing cyclic pointers? ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ cat main.cpp #include <tr1/memory> #include <iostream> struct B; struct A { std::tr1::shared_ptr<B> p; void print() { std::cout << "A" << std::endl; } }; struct B { std::tr1::shared_ptr<A> p; void print() { std::cout << "B" << std::endl; } }; int main () { std::tr1::shared_ptr<A> a(new A); std::tr1::shared_ptr<B> b(new B); a->p=b; b->p=a; a->p->print(); b->p->print(); } ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ valgrind ./main.exe ==75090== Memcheck, a memory error detector ==75090== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==75090== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info ==75090== Command: ./main.exe ==75090== --75090-- ./main.exe: --75090-- dSYM directory is missing; consider using --dsymutil=yes B A ==75090== ==75090== HEAP SUMMARY: ==75090== in use at exit: 4,280 bytes in 6 blocks ==75090== total heap usage: 6 allocs, 0 frees, 4,280 bytes allocated ==75090== ==75090== LEAK SUMMARY: ==75090== definitely lost: 16 bytes in 1 blocks ==75090== indirectly lost: 80 bytes in 3 blocks ==75090== possibly lost: 0 bytes in 0 blocks ==75090== still reachable: 4,184 bytes in 2 blocks ==75090== suppressed: 0 bytes in 0 blocks ==75090== Rerun with --leak-check=full to see details of leaked memory ==75090== ==75090== For counts of detected and suppressed errors, rerun with: -v ==75090== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) #removing cyclic reference by using bare pointer. ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ cat main.cpp #include <tr1/memory> #include <iostream> struct B; struct A { std::tr1::shared_ptr<B> p; void print() { std::cout << "A" << std::endl; } }; struct B { std::tr1::shared_ptr<A> p; void print() { std::cout << "B" << std::endl; } }; int main () { std::tr1::shared_ptr<A> a(new A); std::tr1::shared_ptr<B> b(new B); a->p=b; b->p=a; a->p->print(); b->p->print(); } ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ vim main.cpp ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ make g++ -o main.exe main.cpp ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ valgrind main.exe valgrind: main.exe: command not found ~/linux/test/cpp/library/std/tr1/shared_ptr/loop$ valgrind ./main.exe ==75167== Memcheck, a memory error detector ==75167== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==75167== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info ==75167== Command: ./main.exe ==75167== --75167-- ./main.exe: --75167-- dSYM directory is missing; consider using --dsymutil=yes B A ==75167== ==75167== HEAP SUMMARY: ==75167== in use at exit: 4,184 bytes in 2 blocks ==75167== total heap usage: 6 allocs, 4 frees, 4,272 bytes allocated ==75167== ==75167== LEAK SUMMARY: ==75167== definitely lost: 0 bytes in 0 blocks ==75167== indirectly lost: 0 bytes in 0 blocks ==75167== possibly lost: 0 bytes in 0 blocks ==75167== still reachable: 4,184 bytes in 2 blocks ==75167== suppressed: 0 bytes in 0 blocks ==75167== Rerun with --leak-check=full to see details of leaked memory ==75167== ==75167== For counts of detected and suppressed errors, rerun with: -v ==75167== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) -- Regards, Peng

I think that it is expected that shared_ptr cannot handle points that have cyclic references. In the following example, "a" points to "b" and "b" points to "a". There is a memory leak. I then modified it to remove the dependence using bare pointers, then the leak is resolved. But this is not a very satisfactory solution, as the programmer has to know whether to use bare point or shared_ptr. For very complexity program, it may not be possible to figure out it correctly.
Does anybody know what is the best strategy for handing cyclic pointers?
On of the directions should use weak_ptr.

2011/9/9 Igor R <boost.lists@gmail.com>:
On of the directions should use weak_ptr. The question was what to do if these directions are very hard figure out correctly. Conservative GC can be a semi-solution leaks will still remain but programming will be much simpler.
-- WBR, Max Vasin.

On Fri, Sep 9, 2011 at 4:13 AM, Max Vasin <max.vasin@gmail.com> wrote:
2011/9/9 Igor R <boost.lists@gmail.com>:
On of the directions should use weak_ptr. The question was what to do if these directions are very hard figure out correctly. Conservative GC can be a semi-solution leaks will still remain but programming will be much simpler.
One could imagine a scenario in which you define your cross-linked data structure to the Boost Graph Library as a graph. When it's time to free your whole structure, you run a graph-traversal algorithm to identify cycles, then manually reset() specific shared_ptr instances... But if there's a well-defined time at which you want to free your whole linked structure, it would be simpler to put all nodes into a memory pool that can be freed in one swell foop. If your data structure is sufficiently similar across runs, you might build it, run graph traversal as described above to identify cycles, analyze those cycles and decide where to change shared_ptr to weak_ptr for future runs. But that tactic wouldn't work if the data structure varies wildly with different input data.
participants (4)
-
Igor R
-
Max Vasin
-
Nat Linden
-
Peng Yu