2017-05-25 3 views
0

De réponses à ma question précédente: Pointers in c++ after deletepile sans verrouillage en référence comptant

il est devenu clair que l'utilisation des valeurs de pointeurs, qui pointent vers « supprimé » la mémoire (en particulier, les copier) conduire à comportement undef dans C++ 11 et comportement défini par l'implémentation dans C++ 14.

Antomy Williams dans son livre « l'accès simultané C de l'action » suggère la pile suivante sans serrure avec comptage de référence:

#include <atomic> 
#include <memory> 

template<typename T> 
class lock_free_stack 
{ 
private: 
    struct node; 
    struct counted_node_ptr 
    { 
     int external_count; 
     node* ptr; 
    }; 
    struct node 
    { 
     std::shared_ptr<T> data; 
     std::atomic<int> internal_count; 
     counted_node_ptr next; 
     node(T const& data_): 
      data(std::make_shared<T>(data_)), 
      internal_count(0) 
     {} 
    }; 
    std::atomic<counted_node_ptr> head; 
    void increase_head_count(counted_node_ptr& old_counter) 
    { 
     counted_node_ptr new_counter; 
     do 
     { 
      new_counter=old_counter; 
      ++new_counter.external_count; 
     } 
     while(!head.compare_exchange_strong(
        old_counter,new_counter, 
        std::memory_order_acquire, 
        std::memory_order_relaxed)); 
     old_counter.external_count=new_counter.external_count; 
    } 
public: 
    ~lock_free_stack() 
    { 
     while(pop()); 
    } 
    void push(T const& data) 
    { 
     counted_node_ptr new_node; 
     new_node.ptr=new node(data); 
     new_node.external_count=1; 
     new_node.ptr->next=head.load(std::memory_order_relaxed) 
      while(!head.compare_exchange_weak(
         new_node.ptr->next,new_node, 
         std::memory_order_release, 
         std::memory_order_relaxed)); 
    } 
    std::shared_ptr<T> pop() 
    { 
     counted_node_ptr old_head= 
      head.load(std::memory_order_relaxed); 
     for(;;) 
     { 
      increase_head_count(old_head); 
      node* const ptr=old_head.ptr; 
      if(!ptr) 
      { 
       return std::shared_ptr<T>(); 
      } 
      if(head.compare_exchange_strong(
        old_head,ptr->next,std::memory_order_relaxed)) 
      { 
       std::shared_ptr<T> res; 
       res.swap(ptr->data); 
       int const count_increase=old_head.external_count-2; 
       if(ptr->internal_count.fetch_add(
         count_increase,std::memory_order_release)==-count_increase) 
       { 
        delete ptr; 
       } 
       return res; 
      } 
      else if(ptr->internal_count.fetch_add(
         -1,std::memory_order_relaxed)==1) 
      { 
       ptr->internal_count.load(std::memory_order_acquire); 
       delete ptr; 
      } 
     } 
    } 
}; 

Je suis inquiet pour la fonction

void increase_head_count(counted_node_ptr& old_counter) 
{ 
    counted_node_ptr new_counter; 
    do 
    { 
     new_counter=old_counter; 
     ++new_counter.external_count; 
    } 
    while(!head.compare_exchange_strong(
       old_counter,new_counter, 
       std::memory_order_acquire, 
       std::memory_order_relaxed)); 
    old_counter.external_count=new_counter.external_count; 
} 

Il semble que new_counter = ancien_counter; peut être exécuté, quand old_counter.ptr a été supprimé par un autre thread. Donc, quelqu'un pourrait-il confirmer ou refuser que cette implémentation de pile est strictement incorrecte dans C++ 11?

+1

Ni 'std :: atomic' ou' std :: shared_ptr' n'ont besoin d'être verrouillés (implémentation et type défini) alors comment la classe entière est-elle "verrouillée"? –

+0

@RichardCritten: La classe utilise aussi 'new' et' delete', qui ne sont presque certainement pas sans verrou. –

+0

Ce code est une citation de "Concurrence C++ en action" – Programmer1234

Répondre

0

Il est possible, mais pas problématique, car l'appel compare_exchange le détectera et supprimera new_counter.