Subject: [Boost-bugs] [Boost C++ Libraries] #11638: stored weak_ptr of track object will prevent the track object memory being freed
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2015-09-10 04:46:22
#11638: stored weak_ptr of track object will prevent the track object memory being
freed
-------------------------------------------+----------------------
Reporter: Lutts Cao <lutts.cao@â¦> | Owner: fmhess
Type: Bugs | Status: new
Milestone: To Be Determined | Component: signals2
Version: Boost 1.56.0 | Severity: Problem
Keywords: signals2 weak_ptr memory leak |
-------------------------------------------+----------------------
boost signals2 will store weak_ptr of a tracked object, if the tracked
object is allocated by std::make_shared() or std::allocate_shared(), after
it is destructed, the memory occupied by it will not be freed because the
weak_ptr is still hold by the signal connection. Only when the signal is
re-triggered will the memory being freed.
there's another bug here, after the tracked object is destructed,
signals2::connection.connected() will return false, and
connection.disconnect() seem does nothing when connected is false, and
leave the tracked object memory leaked.
below is some test cases
{{{
#include <iostream> // std::cout
#include <memory> // std::shared_ptr
#include <functional> // std::function
#include <boost/signals2.hpp>
class TrackObject {
public:
TrackObject() { std::cout << "construct TrackObject" << std::endl; }
~TrackObject() { std::cout << "destruct TrackObject" << std::endl; }
static void* operator new(std::size_t sz) {
void* p = ::operator new(sz);
std::cout << "custom new for size " << sz << ", got p " << p <<
std::endl;
return p;
}
static void operator delete(void* p) {
std::cout << "custom delete for p " << p << std::endl;
return ::operator delete(p);
}
static void* operator new[](std::size_t sz) {
std::cout << "custom new for size " << sz << std::endl;
return ::operator new(sz);
}
private:
char data[50];
};
class SignalEmitter {
public:
using SignalType = boost::signals2::signal<void()>;
SignalEmitter() = default;
virtual ~SignalEmitter() = default;
boost::signals2::connection
registerHandler(std::function<void()> func,
std::shared_ptr<TrackObject> trackobj) {
SignalType::slot_type handler(func);
return signal_.connect(handler.track_foreign(trackobj));
}
void trigger() {
signal_();
}
private:
SignalType signal_;
};
template <class T>
struct custom_allocator {
typedef T value_type;
custom_allocator() noexcept {}
template <class U> custom_allocator(const custom_allocator<U>&) noexcept
{}
T* allocate(std::size_t n) {
std::cout << "allocate " << n
<< " objects, object size is " << sizeof(T) << std::endl;
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t n) {
std::cout << "deallocate " << n << " objects" << std::endl;
::operator delete(p);
}
};
int main() {
std::cout << "---TestCase1: check wp will hold the memory---" <<
std::endl;
custom_allocator<TrackObject> alloc;
{
std::weak_ptr<TrackObject> wp4;
{
auto p4 = std::allocate_shared<TrackObject>(alloc);
wp4 = p4;
}
std::cout << "---TestCase1: expect to free the memory before this
line, "
<< "but wp prevents it from being freed ---"
<< std::endl;
}
std::cout
<< "---TestCase1: the memory is only freed when weak_ptr is out of
scope"
<< ", before this line ---"
<< std::endl;
std::cout
<< "---TestCase2: check signal2 will prevent memory from being
freed---"
<< std::endl;
{
SignalEmitter signal_emiiter;
boost::signals2::connection conn;
{
auto trackobj = std::allocate_shared<TrackObject>(alloc);
conn = signal_emiiter.registerHandler(
[]() {
std::cout << "signal triggered" << std::endl;
}, trackobj);
std::cout << "---TestCase2: after registerHandler: conn.connected: "
<< conn.connected()
<< std::endl;
signal_emiiter.trigger();
}
std::cout << "---TestCase2: should free trackobj here, but not---\n";
std::cout << "---TestCase2: connected: " << conn.connected() <<
std::endl;
std::cout
<< "---TestCase2: connected() shows that it's already
disconnected"
<< std::endl;
conn.disconnect();
std::cout << "--TestCase2: so, conn.disconnect() does nothing, "
"the memory is still not freed" << std::endl;
signal_emiiter.trigger();
std::cout
<< "---TestCase2: only re-trigger will free memory before this
line ---"
<< std::endl;
}
std::cout << "---TestCase3: check non-make_shared workaround---" <<
std::endl;
{
SignalEmitter signal_emiiter;
{
std::shared_ptr<TrackObject> trackobj(new TrackObject());
signal_emiiter.registerHandler(
[](){
std::cout << "signal triggered" << std::endl;
}, trackobj);
signal_emiiter.trigger();
}
std::cout << "---TestCase3: without use make_shared, memory will be "
"freed before this line---" << std::endl;
signal_emiiter.trigger();
std::cout << "---TestCase3: re-trigger do nothing ---" << std::endl;
}
}
}}}
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/11638> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:19 UTC