Утечка памяти при использовании множественного boost :: connect для одного slot_type - PullRequest
2 голосов
/ 18 ноября 2009

Я использую boost::signals и теряю память, когда пытаюсь подключить несколько сигналов к одному slot_type. Я видел такую ​​же утечку на разных форумах, но не могу найти ни одной, в которой упоминается правильный способ сделать это, или какой-нибудь обходной путь.

Что я пытаюсь сделать:

Я пытаюсь передать результат 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 частично очищаются, но теряют часть памяти.

Как правильно передать слот в функцию для использования несколько раз? Любая помощь будет оценена.

Спасибо, Chris

Ответы [ 2 ]

2 голосов
/ 18 ноября 2009

Я уверен, что это ошибка. Если вы свернете его до крошечного примера, например ::100100

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), расположенный в строке 75 boost/lib/signals/src/signal_base.cpp, не освобожден.

// 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.

Может быть, лучше просто убедиться, что вы устанавливаете их только фиксированное количество раз, и жить с небольшими утечками памяти, которые, как вы знаете, со временем не увеличатся.

Обновление:

Я собирался отправить это на баг-трекер, но он уже там. Однако это гораздо меньший контрольный пример.

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

Открыт 3 года назад, не связан ни с одним этапом: - [

0 голосов
/ 18 ноября 2009

Для справки других людей, мне повезло, я сохранил собственную копию 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));
   }
   /* ... */
};
...