c++ - Is std::atomic_compare_exchange_weak thread-unsafe by design? -


यह ऊपर उठाया गया था कि मौजूदा std :: atomic_compare_exchange_weak के कार्यान्वयन गैर-परमाणु तुलना निर्देश के साथ सीएएस का बूलियन परिणाम, उदाहरण के लिए

  ताला cmpxchgq% rcx, (% rsp) cmpq% rdx,% rax  

- & gt; अगली, new_node);

विनिर्देश (29.6.5 [atomics.types.operations.req] / 21-22) लगता है कि तुलना के परिणाम होना चाहिए परमाणु आपरेशन का एक हिस्सा:

प्रभाव: परमाणु रूप से तुलना करता है ...

रिटर्न: तुलना का परिणाम

लेकिन क्या यह वास्तव में कार्यान्वित है? क्या हमें बग रिपोर्ट विक्रेताओं को या एलडब्ल्यूजी को फाइल करनी चाहिए?

टीएल; डीआर : Atomic_compare_exchange_weak डिजाइन द्वारा सुरक्षित है, लेकिन वास्तविक कार्यान्वयन छोटी गाड़ी हैं।

यह कोड है जो वास्तव में इस छोटे स्निपेट के लिए जनरेट करता है:

  struct node {int data; नोड * अगला; }; std :: परमाणु & LT; नोड * & gt; सिर; शून्य धक्का (इंट डेटा) {नोड * new_node = नया नोड {डेटा}; New_node- & gt; अगली = head.load (std :: memory_order_relaxed); जबकि (! Head.compare_exchange_weak (new_node- & gt; अगला, new_node, std :: memory_order_release, std :: memory_order_relaxed)) {}}  

परिणाम:

 < कोड> movl% edi,% ebx # मेमोरी movl आवंटित करें $ 16,% edi callq _Znwm movq% rax,% rcx # डेटा के साथ आरंभ और 0 movl% ebx, (% rcx) movq $ 0, 8 (% rcx); मरे हुए स्टोर को दूर किया जाना चाहिए था # head.load movq head (% rip),% rdx movq% rdx, 8 (% rcx) के साथ # ओवरराइट करना चाहिए। 16, 0x90 को संरेखित करें। LBB0_1: #% while.cond # = & gt; इस इनर लूप हेडर: गहराई = 1 में तुलना की गई स्थिति / स्थिति की स्थिति में मूवी का मूलांक मूल्य, रैंक% आरडीएक्स,% राक # परमाणु आपरेशन यहां, दूसरे तर्क को% राक की तुलना करता है, स्टोर पहले तर्क # दूसरे में अगर उसी में, और% रैक्स में दूसरा अन्यथा लॉक cmpxchgq% rcx, सिर (% आरआईपी) # बिना शर्त लिखना पुरानी मूल्य वापस अगले - रुको, क्या? Movq% rax, 8 (% rcx) # जांचें कि क्या cmpxchg ने परिणाम की स्थिति cmpq% rdx,% rax movq% rax,% rdx jne। बस रजिस्टरों की तुलना करें हालांकि, पूरे ऑपरेशन सुरक्षित नहीं है। 

महत्वपूर्ण बिंदु यह है: compare_exchange_ का वर्णन (कमजोर | मजबूत) कहते हैं:

परमाणु रूप से [...] यदि सच, इसके द्वारा मेमोरी पॉइंट की सामग्री को वांछित के साथ बदलें, और यदि झूठी, मेमोरी की सामग्री के साथ अपेक्षित मेमोरी की सामग्री को अपडेट करती है, जो इस बात की ओर इशारा करती है

या छद्म कोड में:

  यदि (* यह == अपेक्षित) * यह = वांछित; अन्य की उम्मीद = * यह;  

ध्यान दें कि अपेक्षित केवल यदि तुलना झूठी है पर लिखी जाती है, और * यह केवल यदि तुलना सही है पर लिखा है सी ++ का सार मॉडल निष्पादन की अनुमति नहीं देता है, जहां दोनों को लिखा है। ऊपर धक्का की शुद्धता के लिए यह महत्वपूर्ण है, क्योंकि यदि head पर लिखना होता है, तो अचानक नया_नोड उस स्थान को इंगित करता है जो अन्य धागे को दिखाई देता है, जिसका अर्थ है कि अन्य धागे अगला ( head-> अगला तक प्रवेश करके) पढ़ना शुरू करें, और अगर अपेक्षित पर लिखें (जो उपनाम new_node- & gt; अगले ) भी होता है, वह एक दौड़ है।

और नाराज़ new_node-> next बिना शर्त से लिखता है इस मामले में जहां तुलना सही है, यह एक आविष्कारित लेखन है।

यह क्लैग में एक बग है। मुझे नहीं पता है कि जीसीसी एक ही काम करता है।

इसके अतिरिक्त, मानक का शब्दांश उप-योग है यह दावा करता है कि पूरे ऑपरेशन परमाणु रूप से होना चाहिए, लेकिन यह असंभव है, क्योंकि अपेक्षित एक परमाणु वस्तु नहीं है; लिखता है कि परमाणु रूप से नहीं हो सकता है मानक क्या कहना चाहिए यह है कि तुलना और * को लिखना परमाणु रूप से हो, लेकिन अपेक्षित पर लिखना नहीं है लेकिन ऐसा नहीं है कि यह बुरा नहीं है, क्योंकि कोई भी वास्तव में उस परमाणु को लिखने की अपेक्षा नहीं करता है।

इसलिए, रगड़ (और संभवत: जीसीसी) के लिए एक बग रिपोर्ट होनी चाहिए, और मानक के लिए एक दोष रिपोर्ट


Comments