2016-09-03 10 views
6

मैं अपनी वास्तविक परियोजनाओं में से एक में हार्ड-टू-डीबग स्थिति में आया हूं, जहां मैं गलती से एक भेड़ के बच्चे के अंदर एक स्थानीय चर के संदर्भ में पहुंच रहा था चले गए पहुंच किसी अन्य थ्रेड से की जा रही थी, लेकिन दूसरे धागे को समाप्त होने तक लेम्बा को जीवित रखा गया था।ले जाया गया स्थानीय चर के संदर्भ में अमान्य उपयोग को पकड़ना और डिबग करना lambda

बग केवल ऑप्टिमाइज़ेशन अक्षम के साथ हुआ और लापरवाह रिफैक्टरिंग के कारण हुआ।

struct state 
{ 
    int x = 100; 
}; 

template <typename TF> 
void eat1(TF&& f) 
{ 
    // Call the lambda. 
    f(); 

    // Simulate waiting for the second thread 
    // to finish. 
    std::this_thread::sleep_for(1000ms); 
} 

template <typename TF> 
void eat0(TF&& f) 
{ 
    // Move the lambda to some other handler. 
    eat1(std::forward<TF>(f)); 
} 

void use_state(state& s) 
{ 
    // Will print `100`. 
    std::cout << s.x << "\n"; 

    // Separate thread. Note that `s` is captured by 
    // reference. 
    std::thread t{[&s] 
     { 
      // Simulate computation delay. 
      std::this_thread::sleep_for(500ms); 

      // Will print garbage. 
      std::cout << s.x << "\n"; 
     }}; 

    t.detach(); 
} 

int main() 
{ 
    eat0([] 
     { 
      // Local lambda variable that will be accessed 
      // after the lambda is moved. 
      state s; 

      // Function that takes `s` by reference and 
      // accesses it in a separate thread after the 
      // lambda is moved. 
      use_state(s); 
     }); 
} 

हैरानी की बात है, sanitizers और चेतावनी झंडे में से कोई भी यहाँ मदद करने में कामयाब रहे:

मैं एक न्यूनतम उदाहरण (available here on wandbox) कि इस मुद्दे को reproduces बना लिया है। आर्क लिनक्स x64 पर जी ++ 6.1.1:

मैं संकलनकर्ता और sanitizers के निम्नलिखित संयोजनों की कोशिश की है,

-Wall -Wextra -Wpedantic -g -O0 

झंडे हमेशा सक्षम के साथ:

  • संकलनकर्ता ; आर्क लिनक्स x64 पर क्लैंग ++ 3.8.0; फेडोरा x64 पर जी ++ 5.3.1; फेडोरा x64 पर क्लैंग ++ 3.7.0

  • सैनिटाइजर्स: -fsanitize=address; -fsanitize=undefined, -fsanitize=thread

किसी भी संयोजन ने किसी सहायक डायग्नोस्टिक का उत्पादन नहीं किया। मैं या तो AddressSanitizer मुझे बताने की मैं एक झूलने संदर्भ ऐक्सेस कर रहे थे, या UndefinedSanitizer यूबी को पकड़ने के लिए, जबकि यह उन तक पहुंचने या ThreadSanitizer मुझे बताने की एक अलग थ्रेड गलत स्मृति स्थान ऐक्सेस कर रहे थे की उम्मीद।

क्या इस समस्या का निदान करने का कोई विश्वसनीय तरीका है? क्या मुझे इस उदाहरण को किसी भी sanitizers के बग ट्रैकर्स को फीचर अनुरोध/दोष के रूप में पोस्ट करना चाहिए?

+0

सी ++ जटिल है। संदर्भ द्वारा कैप्चरिंग में कुछ भी गलत नहीं है। यह भाषा का हिस्सा है। यदि धागे का समय अलग है, तो यह अच्छी तरह परिभाषित है।मैं नहीं देख सकता कि कंपाइलर के लिए रनटाइम थ्रेड 'टाइमिंग को कम करना संभव है। आप सबकुछ पकड़ने वाले कंपाइलर पर भरोसा नहीं कर सकते हैं। –

+0

वैसे मैंने एमएसवीएस से स्थिर विश्लेषण की कोशिश की जो सीपीपी कोर दिशानिर्देशों का उपयोग करता है और यह इसे भी नहीं उठाता है। या तो वह चेक अभी तक लागू नहीं किया गया है या अभी तक इस मामले को कवर करने वाला नियम नहीं है। सुनिश्चित नहीं है कि क्या आप यह देखने के लिए कोई समस्या उठाना चाहते हैं कि इसका निदान किया जा सकता है या नहीं। [लिंक] (https://github.com/isocpp/CppCoreGuidelines) – NathanOliver

उत्तर

4

वालग्रिंड के मेमचेक टूल ने डिफ़ॉल्ट रूप से इस समस्या को पकड़ा। हालांकि, इस तरह की गंदे बग में memcheck से बचने की संभावना है। मुझे यकीन नहीं है कि असली कार्यक्रम पर समस्या पकड़ी जाएगी।

तथ्य यह है कि पहला लैम्ब्डा स्थानांतरित किया गया था, समस्या के लिए प्रासंगिक नहीं है (हालांकि यह डिबगिंग प्रक्रिया जटिल हो सकता है)। समस्या एक ऐसे फ़ंक्शन में स्थानीय चर का उपयोग करने के कारण है जिसने अपना निष्पादन समाप्त कर दिया है (फिर से, तथ्य यह है कि एक अलग थ्रेड से पहुंच केवल जांच को और अधिक कठिन बना देती है लेकिन किसी अन्य तरीके से बग में योगदान नहीं देती है)। तथ्य यह है कि पहला लैम्ब्डा जीवित रखा गया था, किसी भी तरह से आपकी रक्षा नहीं करना चाहिए - स्थानीय चर लैम्बडा आमंत्रण से संबंधित है और लैम्ब्डा स्वयं नहीं है।

+0

मैंने इसे वालग्रिंड के माध्यम से भी चलाया। valgrind शिकायत नहीं की, एक झपकी नहीं। –

संबंधित मुद्दे