バックグラウンド タスクを実行するために boost::asio を使用する一連のクラスに取り組んでいます。実際には、プログラムは継続的に実行されますが、テスト中のクリーンアップのためにシグナル ハンドラーを追加しました。
しかし、SIGINT の受信後にコード内の関数呼び出しを監視しているときに、オブジェクトのプライベート実装が期待どおりに破棄されていないことがわかりました (メモリ リーク)。これは、boost::shared_ptr で管理されます。プライベート実装クラスを以下に示します。
class TestImpl: public boost::enable_shared_from_this<TestImpl>, boost::noncopyable {
TestImpl(): update_timer(io_svc), signals(io_svc, SIGINT, SIGTERM) {
signals.async_wait(boost::bind(&boost::asio::io_service::stop, &io_svc));
};
public:
virtual ~TestImpl() {
std::cout << "Destroyed." << std::endl;
};
static boost::shared_ptr<TestImpl> create() {
boost::shared_ptr<TestImpl> ptr(new TestImpl);
ptr->start();
return ptr;
}
void start() {
update_timer.expires_from_now(boost::posix_time::seconds(1));
update_timer.async_wait(boost::bind(&TestImpl::update, shared_from_this()));
run_thread = boost::thread(boost::bind(&TestImpl::run, shared_from_this()));
};
void cleanup() {
run_thread.join();
};
private:
void run() {
io_svc.run();
};
void update() {
std::cout << "Updating." << std::endl;
update_timer.expires_from_now(boost::posix_time::seconds(1));
update_timer.async_wait(boost::bind(&TestImpl::update, shared_from_this()));
};
boost::asio::io_service io_svc;
boost::asio::deadline_timer update_timer;
boost::thread run_thread;
boost::asio::signal_set signals;
};
これは、プライベート実装を使用しているコードです。
class Test {
public:
Test(): impl(TestImpl::create()) { };
virtual ~Test() { std::cout << "Destroyed." << std::endl; };
int run() {
boost::asio::signal_set signals(io_svc, SIGINT, SIGTERM);
signals.async_wait(boost::bind(&boost::asio::io_service::stop, &io_svc));
io_svc.run();
impl->cleanup();
return 0;
};
private:
boost::asio::io_service io_svc;
boost::shared_ptr<TestImpl> impl;
};
int main() {
Test test;
test.run();
}
TestImpl クラスがリークする理由がわかりません。デバッグすることで、両方の io_service インスタンスが SIGINT で停止され、スレッドが結合されることを確認できます。これにより、破壊時に切り離されないことがわかります。TestImpl インスタンスが持続する原因となっている循環参照がどこかにあるに違いないようです。