title: How to make boost asio fork safe
body: <p>I have built a C++ library using boost ASIO. The library needs to be both thread-safe and fork-safe. 
  It has service scheduler thread, which calls <code>io_service::run()</code>. To support fork-safety, I've registered pre_fork, post_fork_parent and post_fork_child handlers. The <code>pre_fork()</code> handler, calls <code>_io_service.notify_fork(boost::io_service:fork_prepare()</code>, post_fork_parent handler calls <code>_io_service.notify_fork(boost::asio::io_service::fork_parent)</code> and post_fork_child calls <code>_io_service.notify_fork(boost::asio::io_service::fork_child)</code>. </p>
  
  <p>The problem I'm facing in, when the <code>fork()</code> happens, the service scheduler thread might be in middle of some operation and might have acquired lock on data members of <code>io_service</code> object. So, the child process sees them in the same state and in the post_fork_child() when we call <code>_io_service.notify_fork(boost::asio::io_service::fork_child)</code> it tries to acquire the lock on the same object and hence gets blocked indefinitely (as there is no thread in child to release the unlock). </p>
  
  <p>The stack trace I see in the child process, which is blocked, is - </p>
  
  <pre><code>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 ???????? () 
  </code></pre>
  
  <p>Apparently the "dev_poll_reactor" is locked (because it seems to be dispatching some pending events) in the service scheduler thread when the fork has happened which is causing the problem. </p>
  
  <p>I think to solve the problem, I need to ensure that service scheduler thread is not in the middle of any processing when the fork happens and one way to guarantee that would be to call <code>io_service.stop()</code> in pre_fork() handler but that doesn't sound like a good solution. Could you please let me know what is the right approach to make the library fork safe? </p>
  
  <p>The code snippets looks something like this. </p>
  
  <pre><code>/** 
   * Combines Boost.ASIO with a thread for scheduling. 
   */ 
  class ServiceScheduler : private boost::noncopyable 
  { 
  public : 
      /// The actual thread used to perform work. 
      boost::shared_ptr&lt;boost::thread&gt;             _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&lt;boost::asio::io_service::work&gt; _work; 
      ... 
  }; 
  
  /** 
   * CTOR 
   */ 
  ServiceScheduler::ServiceScheduler() 
      : _io_service(), 
        _work(std::auto_ptr&lt;boost::asio::io_service::work&gt;( 
                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&lt;boost::thread&gt;( 
                  new boost::thread(boost::bind( 
                          &amp;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);
  }
  </code></pre>
  
  <p>I'm using boost 1.47 and running the application on Solaris i386. The library and application are built using studio-12.0. </p>