2016-08-10 66 views
1
#include <iostream> 

#include <atomic> 
#include <memory> 


template<typename T> 
class LockFreeQueue { 
public: 
    struct CountedNode; 

private: 
    std::atomic<CountedNode> head; 
public: 
    struct Node{ 
     explicit Node(const T& d) : next(CountedNode()), data(std::make_shared<T>(d)), node_counter(0) { } 
     std::atomic<CountedNode> next; 
     std::shared_ptr<T> data; 
     std::atomic<unsigned> node_counter; 
    }; 
    struct CountedNode { 
     CountedNode() noexcept : node(nullptr), counter(0) {} 
     explicit CountedNode(const T& data) noexcept : node(new Node(data) /* $4 */), counter(0) {} 
     Node* node; 
     int counter; 
    }; 


    void push(const T& data) 
    { 
     CountedNode new_node(data), curr, incrementedNext, next /*($2) */; 
     CountedNode empty; /*($3) */ 
     if (head.compare_exchange_strong(empty, new_node)) std::cout << "EQUALS\n"; // $1 
     else std::cout << "NOT EQUALS\n"; 

     if (head.compare_exchange_strong(next, new_node)) std::cout << "EQUALS\n"; // $1 
     else std::cout << "NOT EQUALS\n"; 
    } 

}; 


int main() { 
    LockFreeQueue<int> Q; 
    Q.push(2); 

    return 0; 
} 



    int main(){ 
    LockFreeQueue<int> Q; 
    Q.push(2); 

    return 0; 
    } 

好的。它編譯和執行沒有錯誤。但是,這仍然存在問題,我將在下面介紹。同一個班級的同一個實例,但行爲不同。可能的UB

http://coliru.stacked-crooked.com/a/1fe71fafc5dde518

在我的眼睛,導致它沒有預期: NOTEQUALS EQUALS

我有上面這段代碼野生問題。

特別是,行$1的比較使我成爲一個問題。我的意思是,這個比較總是返回false,但它應該在第一次返回true。

我很困惑,所以我看着內存emptyhead,實際上他們是不同的。 head等於0x00000000 0x00000000 0x00000000 0x00000000(當涉及字節),它似乎是確定的。但是empty等於: 0x00000000 0x00000000 0x00000000 0x7f7f7f7f7f$2next更有趣的等於0x00000000 0x00000000 0x00000000 0x00000000,所以實際上等於head。但是,例如,curr,incrementedNext等於0x00000000 0x00000000 0x00000000 0x7f7f7f7f7f。 所以這種行爲是不確定的,所以我想任何未定義的行爲,但爲什麼?我不正確的,請解釋我的這種行爲。

P.S.我知道在$4內存泄漏,但現在我忽略它。

我編譯它爲: g++ -latomic main.cpp -std=c++14。 我的gcc版本是6.1.0。我也在gcc 5.1.0上測試過。結果是一樣的。

鏈接到由@PeterCordes創建的源:https://godbolt.org/g/X02QV8

+1

你[剛纔問這個(https://stackoverflow.com /問題/ 38862289 /的-相同情況下的最-同一類,但是,不同的行爲劣勢UB)。 –

+0

是的,我刪除並創建了新的(正確的)帖子。 – Gilgamesz

+0

@KerrekSB,這是一個問題嗎?畢竟,我刪除了我以前的。 – Gilgamesz

回答

4

填充。 std::atomic::compare_exchange*比較兩個對象的內存表示,就像通過memcmp。如果結構具有paddding,則其內容不確定,並且可能使兩個實例看起來不同,即使它們在成員方面相同(請注意CountedNode甚至沒有定義operator==)。

在64位版本中,在counter之後有填充,並且您看到此問題。在32位版本中,沒有,而你沒有。

編輯:以下部分是,我現在認爲是錯誤的;只保留完整性。 std::atomic_init不會做任何事情來填充填充;這個例子似乎只是偶然的工作。


head(以及Node::next)應與std::atomic_init初始化:

std::atomic_init(&head, CountedNode()); 

有了到位,your example works as expected

+0

它不起作用。問題依然存在。你的鏈接僅僅是因爲填充的內容不確定,並且你的代碼片段中'empty'的填充等於零。但是,如果您更改堆棧視圖(請參閱鏈接我的意思,(35行)),您會再次看到「NOT EQUAL」,請參閱:http://coliru.stacked-crooked.com/a/d6a5c66734862e91 我試過用「手動」寫入到'empty's內存,它幫助,我的意思是:http://coliru.stacked-crooked.com/a/56c9c69622fb3492 – Gilgamesz

+0

1.在那種情況下,工作如何std :: atomic_init是必要的嗎? 2.如何解決這個問題比手動書寫填充更好? – Gilgamesz

+0

沒錯。事實上,我懷疑'std :: atomic_init'並沒有真正的幫助 - 它只是將memcpy的參數放入原子中,但沒有說這個參數的填充爲零。所以基本上,看起來像'std :: atomic '其中'T'是除了整數或指針類型之外的東西是一個壞主意。 –

相關問題