私はブーストASIOを使ってC++ライブラリを構築しました。ライブラリはスレッドセーフでもフォークセーフでもなければなりません。 サービススケジューラスレッドがあり、io_service::run()
を呼び出します。フォークセーフティをサポートするために、私はpre_fork、post_fork_parent、およびpost_fork_childハンドラを登録しました。 pre_fork()
ハンドラ、_io_service.notify_fork(boost::io_service:fork_prepare()
、post_fork_parentハンドラは_io_service.notify_fork(boost::asio::io_service::fork_parent)
、post_fork_childは_io_service.notify_fork(boost::asio::io_service::fork_child)
を呼び出します。ブーストASIOフォークを安全にする方法
私が直面している問題は、fork()
が発生したときに、サービススケジューラスレッドが何らかの操作の途中で、io_service
オブジェクトのデータメンバのロックを取得した可能性があります。そのため、子プロセスは同じ状態とpost_fork_child()で、同じオブジェクトのロックを取得しようとしたときに_io_service.notify_fork(boost::asio::io_service::fork_child)
と呼びます(ロック解除を解除するスレッドがないため)。
私がブロックされている子プロセスで参照スタックトレースは、ある -
fffffd7ffed07577 lwp_park (0, 0, 0)
fffffd7ffecffc18 mutex_lock_internal() + 378
fffffd7ffecfffb2 mutex_lock_impl() + 112
fffffd7ffed0007b mutex_lock() + b
fffffd7fff26419d __1cFboostEasioGdetailLscoped_lock4n0CLposix_mutex__2t5B6Mrn0D__v_() + 1d
fffffd7fff2866a2 __1cFboostEasioGdetailQdev_poll_reactorMfork_service6Mn0BKio_serviceKfork_event__v_() + 32
fffffd7fff278527 __1cFboostEasioGdetailQservice_registryLnotify_fork6Mn0BKio_serviceKfork_event__v_() + 107
fffffd7fff27531c __1cDdesGtunnelQServiceSchedulerPpost_fork_child6M_v_() + 1c
fffffd7fff29de24 post_fork_child() + 84
fffffd7ffec92188 _postfork_child_handler() + 38
fffffd7ffecf917d fork() + 12d
fffffd7ffec172d5 fork() + 45
fffffd7ffef94309 fork() + 9
000000000043299d main() + 67d
0000000000424b2c ????????()
どうやら「dev_poll_reactor」サービススケジューラのスレッドで(それはいくつかの保留中のイベントをディスパッチしているように見えるため)ロックされています問題を引き起こしているフォークが起こったとき。
私は問題を解決するために、フォークが発生し、pre_fork()ハンドラでio_service.stop()
を呼び出すことを保証する1つの方法が、サービススケジューラスレッドが処理の途中でないことを保証する必要がありますが、良い解決策のように聞こえません。図書館を安全にするための正しいアプローチが何であるか教えてください。
コードスニペットは次のようになります。
/**
* Combines Boost.ASIO with a thread for scheduling.
*/
class ServiceScheduler : private boost::noncopyable
{
public :
/// The actual thread used to perform work.
boost::shared_ptr<boost::thread> _service_thread;
/// Service used to manage async I/O events
boost::asio::io_service _io_service;
/// Work object to block the ioservice thread.
std::auto_ptr<boost::asio::io_service::work> _work;
...
};
/**
* CTOR
*/
ServiceScheduler::ServiceScheduler()
: _io_service(),
_work(std::auto_ptr<boost::asio::io_service::work>(
new boost::asio::io_service::work(_io_service))),
_is_running(false)
{
}
/**
* Starts a thread to run async I/O service to process the scheduled work.
*/
void ServiceScheduler::start()
{
ScopedLock scheduler_lock(_mutex);
if (!_is_running) {
_is_running = true;
_service_thread = boost::shared_ptr<boost::thread>(
new boost::thread(boost::bind(
&ServiceScheduler::processServiceWork, this)));
}
}
/**
* Processes work passed to the ASIO service and handles uncaught
* exceptions
*/
void ServiceScheduler::processServiceWork()
{
try {
_io_service.run();
}
catch (...) {
}
}
/**
* Pre-fork handler
*/
void ServiceScheduler::pre_fork()
{
_io_service.notify_fork(boost::asio::io_service::fork_prepare);
}
/**
* Post-fork parent handler
*/
void ServiceScheduler::post_fork_parent()
{
_io_service.notify_fork(boost::asio::io_service::fork_parent);
}
/**
* Post-fork child handler
*/
void ServiceScheduler::post_fork_child()
{
_io_service.notify_fork(boost::asio::io_service::fork_child);
}
私はboost 1.47を使用しており、Solaris i386でアプリケーションを実行しています。ライブラリとアプリケーションはstudio-12.0を使用して構築されています。
フォークをコールした後に、他の何かをexec()または_exit()を呼び出すことを想定していますか?もしそうなら、あなたは再考すべきです。そうでない場合、私は問題を見ません。 – janm
メインスレッドは、管理、コマンドインターフェイスタスク、親子処理のためだけに予約できます。フォーク後、子スレッドにはメインスレッドのみが存在します。復元用の内部構成データを保持し、子プロセスに必要なスレッドを作成できます。これにより、きれいなカプセル化が保証され、ロックの必要性が回避されます。 –
2つのプロジェクトでboost :: asioを使った後、私はboostを使わないほうが良いという結論に達しました。簡単な例でさえもsegfaultsします。その複雑なテンプレート構造は理解するのが非常に困難であり、意味のある段階を突き抜け、考えられる原因を特定することは不可能です。 – wallyk