2

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の信号が部分的にクリーンアップされますが、一部のメモリがリークすることです。

スロットを関数に渡して複数回使用する正しい方法は何ですか?どんな助けでもいただければ幸いです。

ありがとう、クリス

4

2 に答える 2

2

私はそれがバグだと確信しています。小さな例に折りたたむと、たとえば次のようになります。

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 行目に割り当てられた 1 つのオブジェクト (a)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();

ただし、 2 番目connectの では、同じ接続オブジェクトが再利用され、同じ行が元のポインター値をやみくもに上書きします。2 番目のオブジェクトはクリーンアップされますが、最初のオブジェクトはクリーンアップされません。

検証として、クリーンアップされるsignal_base_impl::slot_disconnected唯一の場所である のブレークポイントが 1 回だけトリガーされます。signal_data

これは 1.39.0 で追跡しましたが、1.40.0 でも同じようです。

このような変更を行い、Boost のカスタム ビルドを実行することに慣れている場合は、既存の接続boost::signals::detail::signal_base_impl::connect_slotのフィールドで見つかった以前のイテレータ値をクリーンアップするように変更できます。signal_data

これらを一定の回数だけ設定し、時間の経過とともに大きくならないことがわかっているいくつかの小さなメモリ リークに対処することをお勧めします。

アップデート:

これを Boost バグ トラッカーに送信するつもりでしたが、既に存在しています。ただし、これははるかに小さいテスト ケースです。

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

3 年前に開設されましたが、どのマイルストーンにも割り当てられていません :-[

于 2009-11-18T02:36:24.437 に答える
0

他の人の参考のために、私は自分のコピーを維持し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));
   }
   /* ... */
};
于 2009-11-18T18:13:21.227 に答える