2012-02-20 11 views
21

Tôi đã xây dựng một thư viện C++ sử dụng ASIO tăng cường. Thư viện cần phải vừa an toàn vừa an toàn. Nó có chuỗi lịch trình dịch vụ, gọi là io_service::run(). Để hỗ trợ tính năng an toàn ngã ba, tôi đã đăng ký các trình xử lý pre_fork, post_fork_parent và post_fork_child. Trình xử lý pre_fork(), gọi _io_service.notify_fork(boost::io_service:fork_prepare(), các cuộc gọi xử lý post_fork_parent _io_service.notify_fork(boost::asio::io_service::fork_parent) và các cuộc gọi post_fork_child _io_service.notify_fork(boost::asio::io_service::fork_child).Làm thế nào để tăng an toàn asio fork

Sự cố mà tôi đang gặp phải khi xảy ra sự cố fork(), chuỗi lịch trình dịch vụ có thể đang ở giữa một số thao tác và có thể đã bị khóa trên các thành viên dữ liệu của đối tượng io_service. Vì vậy, quá trình con thấy chúng trong cùng một trạng thái và trong post_fork_child() khi chúng ta gọi _io_service.notify_fork(boost::asio::io_service::fork_child) nó cố gắng để có được khóa trên cùng một đối tượng và do đó bị chặn vô thời hạn (vì không có thread trong con để phát hành mở khóa).

Các vết đống tôi thấy trong quá trình đứa trẻ, mà bị chặn, là -

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 ????????() 

Rõ ràng "dev_poll_reactor" bị khóa (vì nó có vẻ được cử một số sự kiện đang treo) trong thread dịch vụ lên lịch khi ngã ba đã xảy ra gây ra vấn đề.

Tôi nghĩ rằng để giải quyết vấn đề, tôi cần đảm bảo rằng chuỗi lịch trình dịch vụ không ở giữa bất kỳ quá trình xử lý nào khi ngã ba xảy ra và một cách để đảm bảo sẽ gọi io_service.stop() trong trình xử lý pre_fork() nhưng không Âm thanh như một giải pháp tốt. Bạn có thể vui lòng cho tôi biết phương pháp phù hợp để làm cho nĩa thư viện có an toàn không?

Đoạn mã trông giống như thế này.

/** 
* 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); 
} 

Tôi đang sử dụng tăng 1,47 và chạy ứng dụng trên Solaris i386. Thư viện và ứng dụng được xây dựng bằng cách sử dụng studio-12.0.

+0

Bạn có dự định thực hiện bất kỳ lệnh gọi hàm exec() hoặc _exit() nào khác sau khi bạn gọi cho ngã ba không? Nếu vậy, bạn nên xem xét lại. Nếu không, tôi không thấy vấn đề. – janm

+0

Bạn có thể đặt chủ đề chính cho chỉ quản trị, các tác vụ giao diện lệnh và xử lý cha-con. Sau ngã ba, chỉ có chủ đề chính tồn tại ở trẻ. Bạn có thể giữ một dữ liệu cấu hình nội bộ để khôi phục và tạo các luồng cần thiết trong tiến trình con. Bằng cách này, đảm bảo đóng gói sạch sẽ và tránh nhu cầu khóa. –

+3

Sau khi cố gắng sử dụng boost :: asio cho hai dự án, tôi đã đạt đến kết luận tốt hơn là không nên sử dụng boost. Nó phân biệt ngay cả trên các ví dụ đơn giản. Cấu trúc khuôn mẫu phức tạp của nó là quá khó hiểu và không thể có nghĩa là bước qua và xác định ngay cả một nguyên nhân có thể xảy ra. – wallyk

Trả lời

2

Mã asio chỉ định rằng notify_fork() không hoạt động khi có bất kỳ mã nào trong mã io_service.

Chức năng này không được gọi trong khi bất kỳ chức năng io_service khác, hoặc bất kỳ chức năng trên một I/O đối tượng liên quan đến việc io_service, đang được gọi trong chủ đề khác. Tuy nhiên, an toàn để gọi chức năng này từ trong một trình xử lý hoàn thành, miễn là không có chủ đề nào khác đang truy cập vào io_service.

Điều đó dường như bao gồm run hoặc bất kỳ IO nào liên kết với thư viện. Tôi nghĩ rằng pre_fork của bạn chế biến, nên thiết lập lại một mục công việc.

ví dụ: từ boost documentation Chăm sóc

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(...); 
} 

vẫn cần phải được thực hiện

  1. Đảm bảo run() không được gọi trước khi post_fork() đã hoàn thành.
  2. Đảm bảo đối tượng work mới được tạo cho run
  3. đồng bộ hóa đúng để đảm bảo chấm dứt được phát hiện run.
0

Bạn có thể sử dụng io_service :: run_one để kiểm tra xem ngã ba đã được lên lịch hay io_service vẫn đang chạy. Khi một ngã ba nên xảy ra một số công việc có thể được thêm vào io_service để làm cho thread để thức dậy. Chủ đề kiểm tra tình trạng chạy và dừng lại một cách im lặng. Sau khi ngã ba xảy ra hoặc là cha mẹ hoặc đứa trẻ có thể khởi động lại một sợi công nhân.

/** 
* 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); 
}