2012-02-20 5 views
21

私はブースト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を使用して構築されています。

+0

フォークをコールした後に、他の何かをexec()または_exit()を呼び出すことを想定していますか?もしそうなら、あなたは再考すべきです。そうでない場合、私は問題を見ません。 – janm

+0

メインスレッドは、管理、コマンドインターフェイスタスク、親子処理のためだけに予約できます。フォーク後、子スレッドにはメインスレッドのみが存在します。復元用の内部構成データを保持し、子プロセスに必要なスレッドを作成できます。これにより、きれいなカプセル化が保証され、ロックの必要性が回避されます。 –

+3

2つのプロジェクトでboost :: asioを使った後、私はboostを使わないほうが良いという結論に達しました。簡単な例でさえもsegfaultsします。その複雑なテンプレート構造は理解するのが非常に困難であり、意味のある段階を突き抜け、考えられる原因を特定することは不可能です。 – wallyk

答えて

2

io_serviceコードにコードがある場合、asioコードはnotify_fork()が機能しないことを指定します。

この関数は、他io_service関数ながら呼ばれる、または io_serviceに関連付けられたI/Oオブジェクトの任意の関数、別のスレッドで と呼ばされてはいけません。ただし、他のスレッドが io_serviceにアクセスしていない場合は、完了ハンドラ内で からこの関数を呼び出すことは安全です。

これは、runまたはそのライブラリに関連付けられているIOのいずれかを含むように見えます。私はあなたのpre_forkの処理は、作業項目をリセットする必要がありますと思います。

boost documentation

boost::asio::io_service io_service; 
auto_ptr<boost::asio::io_service::work> work(
    new boost::asio::io_service::work(io_service)); 
... 
pre_fork() { 
    work.reset(); // Allow run() to exit. 
    // check run has finished... 
    io_service.notify_fork(...); 
} 

ケアから静止post_fork()が完了する前にrun()が呼び出されていないことを確認し

  1. を取られる必要があります。
  2. 新しいworkオブジェクトがrun終了が発見されていることを確認するために
  3. run次の適切な同期のために作成されていることを確認。
0

io_service :: run_oneを使用して、フォークがスケジュールされているかどうか/ io_serviceがまだ実行されているかどうかを確認できます。フォークが起こっているときに、スレッドを起床させるためにio_serviceにいくつかの作業を追加することができます。スレッドは実行条件をチェックし、即座に停止します。フォークが発生した後、親または子のどちらかがワーカースレッドを再起動できます。

/** 
* 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; 
    ServiceScheduler(); 
    void start(); 
    void pre_fork(); 
private: 
    void processServiceWork(); 
    void post_fork_parent(); 
    void post_fork_child(); 
    std::atomic<bool> _is_running; 
}; 

/** 
* 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() 
{ 
    if(!_is_running) { 
     _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 { 
     while(_is_running) { 
      _io_service.run_one(); 
     } 
    } 
    catch (...) { 
    } 
    _is_running = false; 
} 

/** 
* Pre-fork handler 
*/ 
void ServiceScheduler::pre_fork() 
{ 
    _is_running = false; 
    _io_service.post([](){ /*no_op*/}); 
    _service_thread->join(); 
    _service_thread.reset(); 
    _io_service.notify_fork(boost::asio::io_service::fork_prepare); 
} 

/** 
* Post-fork parent handler 
*/ 
void ServiceScheduler::post_fork_parent() 
{ 
    start(); 
    _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); 
} 
関連する問題