在单个 slot_type 上使用多个 boost::connect 导致内存泄漏

发布于 2024-08-11 10:03:04 字数 1606 浏览 9 评论 0原文

当我尝试将多个信号连接到单个 slot_type 时,我正在使用 boost::signals 并泄漏内存。我在各个论坛上看到过同样的泄漏报告,但找不到任何提到执行此操作的正确方法或任何解决方法的内容。

我想做的事情:

我试图将 boost::bind() 的结果传递到函数中。在此函数中,我想将多个信号连接到该结果。第一个连接工作正常,但第一个连接之后的每个连接都会泄漏句柄。

以下是一些示例代码:

typedef boost::signal0<void> LeakSignalType;

class CalledClass
{
   /* ... */
   void connectToSlots(LeakSignalType::slot_type &aSlot)
   {
        LeakSignalType *sig;
        std::list<LeakSignalType*> sigList;
        std::list<LeakSignalType*>::iterator sigIter;

        for(int i = 0; i < 50; i++)
        {
            /*Connect signals to the passed slot */
            sig = new LeakSignalType;
            sig->connect(aSlot);
            sigList.push_back(sig);
        }
        for(sigIter = sigList.begin(); sigIter != sigList.end(); sigIter++)
        {
            /* Undo everything we just did */
            delete *sigIter;
        }
        sigList.clear();
        /*Everything should be cleaned up now */
   }
   /* ... */
}

class CallingClass : public boost::signals::trackable
{
   CalledClass calledInstance;
   /* ... */
   void boundFunction(int i)
   {
   /*Do Something*/
   }

   void connectSignals()
   {
       calledInstance.connectToSlots(boost::bind( &CallingClass::boundFunction, this, 1));
   }
   /* ... */
};

现在调用 CallingClass::connectSignals()

我预计对 connectToSlots 的调用会将 50 个信号连接到单个插槽,然后断开连接并清理所有这些信号。实际发生的情况是,1 个信号完全清理,然后其余 49 个信号部分清理,但泄漏了一些内存。

将插槽传递到函数中以供多次使用的正确方法是什么?任何帮助将不胜感激。

谢谢, 克里斯

I'm using boost::signals and leaking memory when I try to connect multiple signals to a single slot_type. I've seen this same leak reported on various forums, but can't find any that mention the correct way to do this, or any workaround.

What I am trying to do:

I am trying to pass the result of boost::bind() into a function. In this function, I want to connect multiple signals to that result. The first connect works fine, but every connect after the first will leak a handle.

Here is some sample code:

typedef boost::signal0<void> LeakSignalType;

class CalledClass
{
   /* ... */
   void connectToSlots(LeakSignalType::slot_type &aSlot)
   {
        LeakSignalType *sig;
        std::list<LeakSignalType*> sigList;
        std::list<LeakSignalType*>::iterator sigIter;

        for(int i = 0; i < 50; i++)
        {
            /*Connect signals to the passed slot */
            sig = new LeakSignalType;
            sig->connect(aSlot);
            sigList.push_back(sig);
        }
        for(sigIter = sigList.begin(); sigIter != sigList.end(); sigIter++)
        {
            /* Undo everything we just did */
            delete *sigIter;
        }
        sigList.clear();
        /*Everything should be cleaned up now */
   }
   /* ... */
}

class CallingClass : public boost::signals::trackable
{
   CalledClass calledInstance;
   /* ... */
   void boundFunction(int i)
   {
   /*Do Something*/
   }

   void connectSignals()
   {
       calledInstance.connectToSlots(boost::bind( &CallingClass::boundFunction, this, 1));
   }
   /* ... */
};

Now call CallingClass::connectSignals().

I expect that the call to connectToSlots will connect 50 signals to a single slot, then disconnect and clean up all of those signals. What actually happens is that 1 signal completely cleans up, then the remaining 49 partially clean up, but leak some memory.

What is the correct way to pass a slot into a function to use multiple times? Any help would be appreciated.

Thanks,
Chris

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不念旧人 2024-08-18 10:03:05

我很确定这是一个错误。如果您将其折叠为一个小示例,例如:

void boundFunction(int) { }
typedef boost::signal0<void> LeakSignalType;
LeakSignalType::slot_type aSlot = boost::bind(&::boundFunction, 1);
LeakSignalType sig1, sig2;
sig1.connect(aSlot);
sig2.connect(aSlot);

并跟踪分配,您会发现在行分配了一个对象(boost::signals::detail::signal_base_impl::iterator) boost/lib/signals/src/signal_base.cpp 的 75 部分未释放。

// Allocate storage for an iterator that will hold the point of
// insertion of the slot into the list. This is used to later remove
// the slot when it is disconnected.
std::auto_ptr<iterator> saved_iter(new iterator);

在第一个 connect 上,此迭代器附加到一个新的连接对象,其中 signal_data 为 NULL:

data->watch_bound_objects.get_connection()->signal_data =
  saved_iter.release();

但是,在第二个 connect 上,相同连接对象被复用,同一行盲目覆盖原来的指针值。第二个对象已清理,但第一个对象尚未清理。

作为验证,signal_base_impl::slot_disconnected 中的断点(唯一清理 signal_data 的地方)仅触发一次。

我在 1.39.0 中找到了这个,但看起来在 1.40.0 中也是一样的。

您可以修改 boost::signals::detail::signal_base_impl::connect_slot 来清理它在现有连接的 signal_data 字段中找到的任何先前的迭代器值,如果您我们很乐意进行这样的更改并运行 Boost 的自定义构建。

最好确保您只设置固定次数,并忍受一些小的内存泄漏,您知道这些泄漏不会随着时间的推移而增加。

更新:

我本来打算将其提交给 Boost bug 跟踪器,但它已经在那里了。然而,这是一个小得多的测试用例。

https://svn.boost.org/trac/boost/ticket/738

3年前开业,未分配任何里程碑:-[

I'm pretty sure it's a bug. If you collapse it down to a tiny example, e.g.:

void boundFunction(int) { }
typedef boost::signal0<void> LeakSignalType;
LeakSignalType::slot_type aSlot = boost::bind(&::boundFunction, 1);
LeakSignalType sig1, sig2;
sig1.connect(aSlot);
sig2.connect(aSlot);

and trace the allocations, you'll find that one object (a boost::signals::detail::signal_base_impl::iterator) allocated at line 75 of boost/lib/signals/src/signal_base.cpp is not freed up.

// Allocate storage for an iterator that will hold the point of
// insertion of the slot into the list. This is used to later remove
// the slot when it is disconnected.
std::auto_ptr<iterator> saved_iter(new iterator);

On the first connect, this iterator is attached to a fresh connection object, where signal_data is NULL:

data->watch_bound_objects.get_connection()->signal_data =
  saved_iter.release();

On the second connect, however, the same connection object is reused, and the same line blindly overwrites the original pointer value. The second object is cleaned up, but the first is not.

As verification, a breakpoint in signal_base_impl::slot_disconnected, the only place where signal_data is cleaned up, is only triggered once.

I tracked this down in 1.39.0, but it looks like it's the same in 1.40.0.

You could modify boost::signals::detail::signal_base_impl::connect_slot to clean up any previous iterator value it finds in the signal_data field of an existing connection, if you're comfortable making such a change and running a custom build of Boost.

It might be better to just make sure you're only setting these up a fixed number of times and live with a few small memory leaks that you know won't grow over time.

Update:

I was going to submit this to the Boost bug tracker, but it's already there. This is a much smaller test case, however.

https://svn.boost.org/trac/boost/ticket/738

Opened 3 years ago, not assigned to any milestone :-[

李不 2024-08-18 10:03:05

为了供其他人参考,我很幸运地维护了自己的 signal_data 副本,并在删除信号之前将其删除。不知道有什么副作用,YMMV。

像这样的东西:

typedef boost::signal0<void> LeakSignalType;
struct LeakSignalStruct
{
    LeakSignalType  signal;
    /* A pointer to keep track of the pointer Boost loses */
    boost::signals::detail::named_slot_map_iterator *signal_data;
};

class CalledClass
{
   /* ... */
   void connectToSlots(LeakSignalType::slot_type &aSlot)
   {
        LeakSignalStruct *sig;
        std::list<LeakSignalStruct*> sigList;
        std::list<LeakSignalStruct*>::iterator sigIter;

        for(int i = 0; i < 50; i++)
        {
            /*Connect signals to the passed slot */
            sig = new LeakSignalStruct;
            sig->connect(aSlot);
            /* Make a backup of the reference that Boost will lose */
            sig->signal_data = (boost::signals::detail::named_slot_map_iterator*)connection.get_connection()->signal_data;
            sigList.push_back(sig);
        }

        /* Boost remembers the last signal_data and will delete it itself,
           so we better lose our reference to avoid double-delete */
        sig->signal_data = NULL;

        for(sigIter = sigList.begin(); sigIter != sigList.end(); sigIter++)
        {
            /* Undo everything we just did */
            /* Boost lost this reference, so we delete it ourselves */
            delete (*sigIter)->signal_data;
            delete *sigIter;
        }
        sigList.clear();
        /*Everything should be cleaned up now */
   }
   /* ... */
};

class CallingClass : public boost::signals::trackable
{
   CalledClass calledInstance;
   /* ... */
   void boundFunction(int i)
   {
   /*Do Something*/
   }

   void connectSignals()
   {
       calledInstance.connectToSlots(boost::bind( &CallingClass::boundFunction, this, 1));
   }
   /* ... */
};

For other people's reference, I am having some luck maintaining my own copy of signal_data, and deleting it before deleting the signal. Don't know of any side effects, YMMV.

Something like this:

typedef boost::signal0<void> LeakSignalType;
struct LeakSignalStruct
{
    LeakSignalType  signal;
    /* A pointer to keep track of the pointer Boost loses */
    boost::signals::detail::named_slot_map_iterator *signal_data;
};

class CalledClass
{
   /* ... */
   void connectToSlots(LeakSignalType::slot_type &aSlot)
   {
        LeakSignalStruct *sig;
        std::list<LeakSignalStruct*> sigList;
        std::list<LeakSignalStruct*>::iterator sigIter;

        for(int i = 0; i < 50; i++)
        {
            /*Connect signals to the passed slot */
            sig = new LeakSignalStruct;
            sig->connect(aSlot);
            /* Make a backup of the reference that Boost will lose */
            sig->signal_data = (boost::signals::detail::named_slot_map_iterator*)connection.get_connection()->signal_data;
            sigList.push_back(sig);
        }

        /* Boost remembers the last signal_data and will delete it itself,
           so we better lose our reference to avoid double-delete */
        sig->signal_data = NULL;

        for(sigIter = sigList.begin(); sigIter != sigList.end(); sigIter++)
        {
            /* Undo everything we just did */
            /* Boost lost this reference, so we delete it ourselves */
            delete (*sigIter)->signal_data;
            delete *sigIter;
        }
        sigList.clear();
        /*Everything should be cleaned up now */
   }
   /* ... */
};

class CallingClass : public boost::signals::trackable
{
   CalledClass calledInstance;
   /* ... */
   void boundFunction(int i)
   {
   /*Do Something*/
   }

   void connectSignals()
   {
       calledInstance.connectToSlots(boost::bind( &CallingClass::boundFunction, this, 1));
   }
   /* ... */
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文