2012-02-20 12 views
21

मैंने बूस्ट एएसआईओ का उपयोग कर सी ++ लाइब्रेरी बनाई है। पुस्तकालय को थ्रेड-सुरक्षित और कांटा-सुरक्षित दोनों होना चाहिए। इसमें सेवा शेड्यूलर थ्रेड है, जो 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) पर कॉल करता है।बूस्ट एएसआईओ फोर्क सुरक्षित बनाने के लिए कैसे करें

समस्या का सामना करना पड़ रहा है, जब 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()() हैंडलर कॉल करने के लिए, लेकिन वह नहीं करता होगा किसी भी प्रसंस्करण के बीच जब कांटा होता है और एक तरह से नहीं है जरूरत एक अच्छा समाधान की तरह ध्वनि नहीं है। क्या आप कृपया मुझे बता सकते हैं कि लाइब्रेरी कांटा सुरक्षित बनाने के लिए सही दृष्टिकोण क्या है?

कोड स्निपेट इस तरह दिखते हैं।

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

मैं बूस्ट 1.47 का उपयोग कर रहा हूं और सोलारिस i386 पर एप्लिकेशन चला रहा हूं। लाइब्रेरी और एप्लिकेशन स्टूडियो -12.0 का उपयोग करके बनाया गया है।

+0

आप बच्चे में कुछ भी अन्य कॉल कार्यकारी() या _exit() करने के लिए जाने के बाद कांटा फोन की उम्मीद कर रहे हैं? यदि हां, तो आपको पुनर्विचार करना चाहिए। यदि नहीं, तो मुझे समस्या नहीं दिखाई दे रही है। – janm

+0

आप केवल प्रशासन, कमांड इंटरफेस कार्यों और अभिभावक-बाल हैंडलिंग के लिए मुख्य धागा आरक्षित कर सकते हैं। कांटा के बाद, बच्चे में केवल मुख्य धागा मौजूद है। आप बाल प्रक्रिया में आवश्यक थ्रेड को पुनर्स्थापित करने और बनाने के लिए आंतरिक कॉन्फ़िगरेशन डेटा रख सकते हैं। इस तरह एक साफ encapsulation सुनिश्चित करता है, और लॉकिंग जरूरतों से बचाता है। –

+3

दो परियोजनाओं के लिए boost :: asio का उपयोग करने का प्रयास करने के बाद, मैं निष्कर्ष पर पहुंचा कि बूस्ट का उपयोग न करना बेहतर है। यह सरल उदाहरणों पर भी segfaults। इसकी जटिल टेम्पलेट संरचना को अर्थपूर्ण तरीके से समझने और असंभव कारणों की पहचान करने के लिए असंभव और असंभव है। – wallyk

उत्तर

2

एएसओ कोड निर्दिष्ट करता है कि notify_fork() काम नहीं करता है जब io_service कोड में कोई कोड है।

इस समारोह जबकि किसी अन्य io_service समारोह बुलाया नहीं किया जाना चाहिए, या एक आई/ओ io_service के साथ जुड़े वस्तु पर किसी भी समारोह, एक और धागा में बुलाया जा रहा है। हालांकि, यह कार्य पूरा होने के भीतर से इस फ़ंक्शन को कॉल करने के लिए सुरक्षित है, बशर्ते कोई अन्य धागा io_service तक पहुंच न हो।

ऐसा लगता है कि run या लाइब्रेरी से जुड़े आईओ में से कोई भी शामिल है। मुझे लगता है कि आपकी 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(...); 
} 

देखभाल अभी भी

  1. लिया जाना सुनिश्चित करें post_fork() पूरा कर लिया है पहले run() नहीं बुलाया जाता है की जरूरत है।
  2. सुनिश्चित नया work वस्तु run समाप्ति सुनिश्चित करने के लिए देखा जाता है अगले run
  3. उचित तुल्यकालन के लिए बनाया जाता है।
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); 
}