when I run my program everything goes fine. At the end it prints out this:
*** glibc detected *** ./streamShare: double free or corruption (fasttop): 0x08292130 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xcc2ff1]
/lib/tls/i686/cmov/libc.so.6[0xcc46f2]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xcc779d]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x1c86f1]
./streamShare[0x804be7f]
./streamShare[0x804be3e]
./streamShare[0x804abc0]
./streamShare[0x804a5f2]
./streamShare[0x804a1c4]
./streamShare[0x804a1d7]
./streamShare[0x804a46a]
./streamShare[0x804ba45]
./streamShare[0x804b49c]
./streamShare[0x804ac68]
./streamShare[0x804ac48]
./streamShare[0x804a676]
./streamShare[0x804a237]
./streamShare[0x8049a3f]
./streamShare[0x804d2e5]
./streamShare[0x804d34d]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc6eb56]
./streamShare[0x8049361]
I checked out, it happens when a function returns, where all the objects of the program are instatied automatically. Anyway I didn't define any destructor for these objects, I tried to use STL Containers and TR1 shared_ptr. I guess everything happens in the default destructors. Is there a way to know where does it breaks up? I mean, I'd like to know which object distruction does the whole mess. I'm using these containers and shared pointers:
typedef std::tr1::shared_ptr<messageListener> mlsptr;
typedef std::map<const char*, mlsptr, ltstr> CONSTCHP2MSLST;
messageListener doesn't have a distructor. And two of these vectors:
std::vector<MSG> queueto1;
where MSG destructor is:
MSG::~MSG() {
destroy();
}
void MSG::destroy() {
if (payload != NULL)
delete[] payload;
payload = NULL;
payloadLen = 0;
}
that never gave problems before and I this it shouldn't ever...
Any recomendations how to track this problem? I'm clueless...
EDIT:
HERE IS VALGRIND OUTPUT:
valgrind ./streamShare -v
==25795== Memcheck, a memory error detector
==25795== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==25795== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
==25795== Command: ./streamShare -v
==25795==
==25795== Invalid free() / delete / delete[]
==25795== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
==25795== by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97)
==25795== by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75)
==25795== by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140)
==25795== by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153)
==25795== by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358)
==25795== by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834)
==25795== by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68)
==25795== by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115)
==25795== by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383)
==25795== by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972)
==25795== by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970)
==25795== Address 0x42c3358 is 0 bytes inside a block of size 8 free'd
==25795== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
==25795== by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97)
==25795== by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75)
==25795== by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140)
==25795== by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153)
==25795== by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358)
==25795== by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834)
==25795== by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68)
==25795== by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115)
==25795== by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383)
==25795== by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972)
==25795== by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970)
==25795==
==25795==
==25795== HEAP SUMMARY:
==25795== in use at exit: 0 bytes in 0 blocks
==25795== total heap usage: 22 allocs, 30 frees, 496 bytes allocated
==25795==
==25795== All heap blocks were freed -- no leaks are possible
==25795==
==25795== For counts of detected and suppressed errors, rerun with: -v
==25795== ERROR SUMMARY: 8 errors from 1 contexts (suppressed: 19 from 8)
Judging by your Valgrind output, the problem is that an object pointed to by a shared_ptr
is being deleted twice. One possibility of getting that is if you initialize two shared_ptr
with the same raw pointer, e.g.:
int* p = new int(123);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
shared_ptr
is not magical, and it cannot know if the object you ask it to take care of is already owned by some other unrelated shared_ptr
, if all you give it is a raw pointer. In the example above, each shared_ptr
will create its own reference counter, initialized to 1; when they die, each will decrement its own counter, see that it is 0, and delete the object, producing double deletion. I suspect your case is similar. If you show the code used to initialize shared_ptr
objects that are added to the vector, this can be verified.
[EDIT] Since this is validated to be the cause of the crash now, let me elaborate a
bit on how to properly use shared_ptr
.
First of all, the nature of the problem. The way shared_ptr
is written, it works with any C++ type, and provides reference counting semantics. The obvious problem is that most types do not provide any space to store the reference counter (e.g. consider shared_ptr<int>
- there's no extra space "inside" int
). To work around this, for every shared object, a separate block of memory is allocated that contains the reference counter. This is done whenever you create a shared_ptr
from a raw pointer. The shared_ptr
object itself then stores the original raw pointer, and the pointer to reference counter (which is why it's more "fat" than a raw pointer, which can be trivially checked with sizeof
). When you create one shared_ptr
from another (using copy constructor or assignment operator), it copies the pointer to reference counter, so all shared_ptr
instances created from one another maintain a single counter, and guarantee correct deletion. But if you have two unrelated "families" of shared_ptr
objects to the same objects (where two or more pointers were created from the same raw pointer), those "families" do not know about each other, and will refcount separately, and each will delete when it hits 0.
What this means in practice is that, when using shared_ptr
, one must adhere to certain rules. Those depend on which implementation you use.
With std::tr1::shared_ptr
, or older Boost versions, the only fully safe pattern for object allocation is this:
shared_ptr<T> x(new T(...));
In other words, the result of new
should be immediately put into a shared_ptr
- you can then copy the latter as much as you want.
A reasonably safe pattern is also this:
auto_ptr<T> x(new T);
...
shared_ptr<T> y(x);
shared_ptr
implements the usual transfer-of-ownership when initializing from auto_ptr
, and the semantics of the latter (so long as they're correctly followed) ensure that only one auto_ptr
to an object should exist; thus, it is safe to construct a shared_ptr
from that.
Sometimes you also have to deal with C++ libraries which do not use auto_ptr
to indicate transfer of pointer ownership, but simply document the intent for specific functions. In those cases it should also be safe to use shared_ptr
, but of course you should be sure that you've understood the documentation correctly...
In C++0x std::shared_ptr
, and in newer versions of boost::shared_ptr
, there's a helper provided to ensure correct instantiation of shared objects:
shared_ptr<int> p = make_shared<int>(123);
The return type of make_shared<T>()
is already shared_ptr<T>
, so at no point you're dealing with raw pointers in your code, reducing chance to get something wrong.
First step is to compile your program with -g 3
switch so that you get much more debugging info.
There's not a lot to go on, but there is a possibility that your ~MSG()
is getting called when the vector reallocates to grow.
Here's the most simple version of your code:
#include <vector>
struct MSG {
MSG(int count);
~MSG();
void destroy();
char* payload;
int payloadLen;
};
MSG::MSG(int count): payload(new char[count]), payloadLen(count) {}
MSG::~MSG() {
destroy();
}
void MSG::destroy() {
if (payload != NULL)
delete[] payload;
payload = NULL;
payloadLen = 0;
}
std::vector<MSG> queueto1;
int main() {
queueto1.push_back(MSG(10));
return 0;
}
And G++ says:
block freed twice
Exited: ExitFailure 127
Now when the push_back(MSG(10))
is being called, it is creating a temporary MSG
instance and then using the default copy constructor to set the item in the vector, before calling ~MSG()
on the temporary (which deletes the payload that the item in the vector now points at).
You can implement the copy constructor MSG(const MSG& copy)
to transfer ownership, but that requires a const_cast on the copy and is very very messy.
Two easy options are available: MSG::payload
is a shared pointer, or queueto1 is a std::vector<MSG*>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With