2013-03-16 47 views
1

我有一個C++項目,編譯運行正確的GCC-4.1.2-46和gcc-4.4.5-6JMP自指令通過gcc4.4.6-3

編譯但它具有異常的死循環同時使用-O2編譯gcc-4.4.6-3。

我使用gdb在進程運行時附加它,並且發現線程正在運行但棧沒有改變。

objdump的程序,發現它有3個指令JMP自我,就像這樣:

432f6c:  48 89 c7    mov %rax,%rdi 
    432f6f:  90      nop 
    432f70:  e8 b3 e4 fd ff   callq 411428 <[email protected]> 
    432f75:  eb fe     jmp 432f75 <_ZN9oceanbase12updateserver11QueryEngine3getERKNS0_5TEKeyE+0x4d5> 
    432f77:  48 8d 7c 24 70   lea 0x70(%rsp),%rdi 
    432f7c:  48 89 c3    mov %rax,%rbx 
    432f7f:  e8 9c 32 00 00   callq 436220 <_ZN9oceanbase12updateserver12BitLockGuardD1Ev> 

我沒有用「轉到」代碼。

當代碼是由GCC-4.4.6-3使用-O0編譯,

的JMP自指令消失

所以我懷疑這是gcc-4.4.6.3的錯誤。

的代碼使用了BitLock保護桶一個簡單的多線程的Hashmap:

 #define ATOMIC_CAS(val, cmpv, newv) __sync_val_compare_and_swap((val), (cmpv), (newv)) 
     #define ATOMIC_ADD(val, addv) __sync_add_and_fetch((val), (addv)) 
     #define ATOMIC_SUB(val, subv) __sync_sub_and_fetch((val), (subv)) 

     template <typename Key, 
       typename Value, 
       typename BucketAllocator, 
       typename NodeAllocator> 
     class LightyHashMap 
     { 
     struct Node 
     { 
      Key key; 
      Value value; 
      union 
      { 
      Node *next; 
      int64_t flag; 
      }; 
     }; 
     static const int64_t EMPTY_FLAG = 0xffffffffffffffff; 
     static const int64_t INIT_UNIT_SIZE = (64L * 1024L/sizeof(Node)) * sizeof(Node); 
     typedef Hash<Key> HashFunc; 
     typedef Equal<Key> EqualFunc; 
     public: 
      LightyHashMap(BucketAllocator &bucket_allocator, NodeAllocator &node_allocator); 
      ~LightyHashMap(); 
     private: 
      DISALLOW_COPY_AND_ASSIGN(LightyHashMap); 
     public: 
      int create(const int64_t bucket_num); 
      void destroy(); 
      int clear(); 
     public: 
      inline int insert(const Key &key, const Value &value); 
      inline int get(const Key &key, Value &value); 
      inline int erase(const Key &key, Value *value = NULL); 
      inline int64_t uninit_unit_num() const; 
      inline int64_t bucket_using() const; 
      inline int64_t size() const; 
     private: 
      void init_bucket_unit_(const int64_t bucket_pos); 
     private: 
      BucketAllocator &bucket_allocator_; 
      NodeAllocator &node_allocator_; 
      int64_t bucket_num_; 
      Node *buckets_; 
      volatile int64_t uninit_unit_num_; 
      uint8_t *init_units_; 
      BitLock bit_lock_; 
      int64_t bucket_using_; 
      int64_t size_; 
      HashFunc hash_func_; 
      EqualFunc equal_func_; 
     }; 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::LightyHashMap(
     BucketAllocator &bucket_allocator, 
     NodeAllocator &node_allocator) : bucket_allocator_(bucket_allocator), 
             node_allocator_(node_allocator), 
             bucket_num_(0), 
             buckets_(NULL), 
             uninit_unit_num_(0), 
             init_units_(NULL), 
             bucket_using_(0), 
             size_(0), 
             hash_func_(), 
             equal_func_() 
     { 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::~LightyHashMap() 
     { 
     destroy(); 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::create(const int64_t bucket_num) 
     { 
     int ret = common::OB_SUCCESS; 
     int64_t uninit_unit_num = (bucket_num * sizeof(Node)/INIT_UNIT_SIZE) \ 
            + ((0 == (bucket_num * sizeof(Node) % INIT_UNIT_SIZE)) ? 0 : 1); 
     if (NULL != buckets_) 
     { 
      ret = common::OB_INIT_TWICE; 
     } 
     else if (0 >= bucket_num) 
     { 
      ret = common::OB_INVALID_ARGUMENT; 
     } 
     else if (NULL == (buckets_ = (Node*)bucket_allocator_.alloc(bucket_num * sizeof(Node)))) 
     { 
      ret = common::OB_MEM_OVERFLOW; 
     } 
     else if (NULL == (init_units_ = (uint8_t*)bucket_allocator_.alloc(uninit_unit_num * sizeof(uint8_t)))) 
     { 
      ret = common::OB_MEM_OVERFLOW; 
     } 
     else if (OB_SUCCESS != (ret = bit_lock_.init(bucket_num))) 
     { 
      // init bit lock fail 
     } 
     else 
     { 
      bucket_num_ = bucket_num; 
      uninit_unit_num_ = uninit_unit_num; 
      memset(init_units_, 0, uninit_unit_num_ * sizeof(uint8_t)); 
      bucket_using_ = 0; 
      size_ = 0; 
     } 
     if (common::OB_SUCCESS != ret) 
     { 
      destroy(); 
     } 
     return ret; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     void LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::destroy() 
     { 
     if (NULL != buckets_) 
     { 
      if (NULL != init_units_) 
      { 
      for (int64_t i = 0; i < bucket_num_; i++) 
      { 
       int64_t unit_pos = i * sizeof(Node)/INIT_UNIT_SIZE; 
       uint8_t ov = init_units_[unit_pos]; 
       if (0 == (ov & 0x80)) 
       { 
       continue; 
       } 
       Node *iter = buckets_[i].next; 
       while (EMPTY_FLAG != buckets_[i].flag 
        && NULL != iter) 
       { 
       Node *tmp = iter; 
       iter = iter->next; 
       node_allocator_.free(tmp); 
       } 
       buckets_[i].flag = EMPTY_FLAG; 
      } 
      } 
      bucket_allocator_.free(buckets_); 
      buckets_ = NULL; 
     } 
     if (NULL != init_units_) 
     { 
      bucket_allocator_.free(init_units_); 
      init_units_ = NULL; 
     } 
     bit_lock_.destroy(); 
     bucket_num_ = 0; 
     uninit_unit_num_ = 0; 
     bucket_using_ = 0; 
     size_ = 0; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::clear() 
     { 
     int ret = common::OB_SUCCESS; 
     if (NULL == buckets_ 
      || NULL == init_units_) 
     { 
      ret = common::OB_NOT_INIT; 
     } 
     else 
     { 
      for (int64_t i = 0; i < bucket_num_; i++) 
      { 
      int64_t unit_pos = i * sizeof(Node)/INIT_UNIT_SIZE; 
      uint8_t ov = init_units_[unit_pos]; 
      if (0 == (ov & 0x80)) 
      { 
       continue; 
      } 
      BitLockGuard guard(bit_lock_, i); 
      Node *iter = buckets_[i].next; 
      while (EMPTY_FLAG != buckets_[i].flag 
        && NULL != iter) 
      { 
       Node *tmp = iter; 
       iter = iter->next; 
       node_allocator_.free(tmp); 
      } 
      buckets_[i].flag = EMPTY_FLAG; 
      } 
      uninit_unit_num_ = (bucket_num_ * sizeof(Node)/INIT_UNIT_SIZE) \ 
           + ((0 == (bucket_num_ * sizeof(Node) % INIT_UNIT_SIZE)) ? 0 : 1); 
      memset(init_units_, 0, uninit_unit_num_ * sizeof(Node)); 
      bucket_using_ = 0; 
      size_ = 0; 
     } 
     return ret; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::insert(const Key &key, const Value &value) 
     { 
     int ret = common::OB_SUCCESS; 
     if (NULL == buckets_ 
      || NULL == init_units_) 
     { 
      ret = common::OB_NOT_INIT; 
     } 
     else 
     { 
      int64_t hash_value = hash_func_(key); 
      int64_t bucket_pos = hash_value % bucket_num_; 
      init_bucket_unit_(bucket_pos); 
      BitLockGuard guard(bit_lock_, bucket_pos); 
      if (EMPTY_FLAG == buckets_[bucket_pos].flag) 
      { 
      buckets_[bucket_pos].key = key; 
      buckets_[bucket_pos].value = value; 
      buckets_[bucket_pos].next = NULL; 
      common::atomic_inc((uint64_t*)&bucket_using_); 
      common::atomic_inc((uint64_t*)&size_); 
      } 
      else 
      { 
      Node *iter = &(buckets_[bucket_pos]); 
      while (true) 
      { 
       if (equal_func_(iter->key, key)) 
       { 
       ret = common::OB_ENTRY_EXIST; 
       break; 
       } 
       if (NULL != iter->next) 
       { 
       iter = iter->next; 
       } 
       else 
       { 
       break; 
       } 
      } 
      if (common::OB_SUCCESS == ret) 
      { 
       Node *node = (Node*)node_allocator_.alloc(sizeof(Node)); 
       if(NULL == node) 
       { 
       ret = common::OB_MEM_OVERFLOW; 
       } 
       else 
       { 
       node->key = key; 
       node->value = value; 
       node->next = NULL; 
       iter->next = node; 
       common::atomic_inc((uint64_t*)&size_); 
       } 
      } 
      } 
     } 
     return ret; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::get(const Key &key, Value &value) 
     { 
     int ret = common::OB_SUCCESS; 
     if (NULL == buckets_ 
      || NULL == init_units_) 
     { 
      ret = common::OB_NOT_INIT; 
     } 
     else 
     { 
      int64_t hash_value = hash_func_(key); 
      int64_t bucket_pos = hash_value % bucket_num_; 
      init_bucket_unit_(bucket_pos); 
      BitLockGuard guard(bit_lock_, bucket_pos); 
      ret = common::OB_ENTRY_NOT_EXIST; 
      if (EMPTY_FLAG != buckets_[bucket_pos].flag) 
      { 
      Node *iter = &(buckets_[bucket_pos]); 
      while (NULL != iter) 
      { 
       if (equal_func_(iter->key, key)) 
       { 
       value = iter->value; 
       ret = common::OB_SUCCESS; 
       break; 
       } 
       iter = iter->next; 
      } 
      } 
     } 
     return ret; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::erase(const Key &key, Value *value) 
     { 
     int ret = common::OB_SUCCESS; 
     if (NULL == buckets_ 
      || NULL == init_units_) 
     { 
      ret = common::OB_NOT_INIT; 
     } 
     else 
     { 
      int64_t hash_value = hash_func_(key); 
      int64_t bucket_pos = hash_value % bucket_num_; 
      init_bucket_unit_(bucket_pos); 
      BitLockGuard guard(bit_lock_, bucket_pos); 
      ret = common::OB_ENTRY_NOT_EXIST; 
      if (EMPTY_FLAG != buckets_[bucket_pos].flag) 
      { 
      Node *iter = &(buckets_[bucket_pos]); 
      Node *prev = NULL; 
      while (NULL != iter) 
      { 
       if (equal_func_(iter->key, key)) 
       { 
       if (NULL != value) 
       { 
        *value = iter->value; 
       } 
       if (NULL == prev) 
       { 
        buckets_[bucket_pos].flag = EMPTY_FLAG; 
       } 
       else 
       { 
        // do not free deleted node 
        prev->next = iter->next; 
       } 
       ret = common::OB_SUCCESS; 
       break; 
       } 
       prev = iter; 
       iter = iter->next; 
      } 
      } 
     } 
     return ret; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int64_t LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::uninit_unit_num() const 
     { 
     return uninit_unit_num_; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int64_t LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::bucket_using() const 
     { 
     return bucket_using_; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     int64_t LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::size() const 
     { 
     return size_; 
     } 

     template <typename Key, typename Value, typename BucketAllocator, typename NodeAllocator> 
     void LightyHashMap<Key, Value, BucketAllocator, NodeAllocator>::init_bucket_unit_(const int64_t bucket_pos) 
     { 
     while (0 < uninit_unit_num_) 
     { 
      int64_t unit_pos = bucket_pos * sizeof(Node)/INIT_UNIT_SIZE; 
      uint8_t ov = init_units_[unit_pos]; 
      if (ov & 0x80) 
      { 
      break; 
      } 
      ov = 0; 
      uint8_t nv = ov | 0x01; 
      if (ov == ATOMIC_CAS(&(init_units_[unit_pos]), ov, nv)) 
      { 
      int64_t ms_size = std::min((bucket_num_ - bucket_pos) * sizeof(Node), (uint64_t)INIT_UNIT_SIZE); 
      memset((char*)buckets_ + unit_pos * INIT_UNIT_SIZE, -1, ms_size); 
      ATOMIC_SUB(&uninit_unit_num_, 1); 
      init_units_[unit_pos] = 0x80; 
      break; 
      } 
     } 
     } 

/////////////////////// ////////////////////////////////////////////////// /////////////////

static const uint8_t BIT_MASKS[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; 

class BitLock 
{ 
    public: 
    BitLock() : size_(0), 
       bits_(NULL) 
    { 
    }; 
    ~BitLock() 
    { 
     destroy(); 
    }; 
    public: 
    inline int init(const int64_t size); 
    inline void destroy(); 
    inline int lock(const int64_t index); 
    inline int unlock(const int64_t index); 
    private: 
    int64_t size_; 
    uint8_t *bits_; 
}; 

class BitLockGuard 
{ 
    public: 
    BitLockGuard(BitLock &lock, const int64_t index) : lock_(lock), 
                 index_(index) 
    { 
     lock_.lock(index_); 
    }; 
    ~BitLockGuard() 
    { 
     lock_.unlock(index_); 
    }; 
    private: 
    BitLock &lock_; 
    const int64_t index_; 
}; 

int BitLock::init(const int64_t size) 
{ 
    int ret = common::OB_SUCCESS; 
    if (0 < size_ 
     || NULL != bits_) 
    { 
    ret = common::OB_INIT_TWICE; 
    } 
    else if (0 >= size) 
    { 
    ret = common::OB_INVALID_ARGUMENT; 
    } 
    else 
    { 
    int64_t byte_size = common::upper_align(size, 8)/8; 
    if (NULL == (bits_ = (uint8_t*)common::ob_malloc(byte_size))) 
    { 
     ret = common::OB_MEM_OVERFLOW; 
    } 
    else 
    { 
     memset(bits_, 0, byte_size); 
     size_ = size; 
    } 
    } 
    return ret; 
} 

void BitLock::destroy() 
{ 
    if (NULL != bits_) 
    { 
    common::ob_free(bits_); 
    bits_ = NULL; 
    } 
    size_ = 0; 
} 

int BitLock::lock(const int64_t index) 
{ 
    int ret = common::OB_SUCCESS; 
    if (0 >= size_ 
     || NULL == bits_) 
    { 
    ret = common::OB_NOT_INIT; 
    } 
    else if (index >= size_) 
    { 
    ret = common::OB_INVALID_ARGUMENT; 
    } 
    else 
    { 
    int64_t byte_index = index/8; 
    int64_t bit_index = index % 8; 
    while (true) 
    { 
     uint8_t ov = bits_[byte_index]; 
     if (ov & BIT_MASKS[bit_index]) 
     { 
     continue; 
     } 
     if (ov == ATOMIC_CAS(&(bits_[byte_index]), ov, ov | BIT_MASKS[bit_index])) 
     { 
     break; 
     } 
    } 
    } 
    return ret; 
} 

int BitLock::unlock(const int64_t index) 
{ 
    int ret = common::OB_SUCCESS; 
    if (0 >= size_ 
     || NULL == bits_) 
    { 
    ret = common::OB_NOT_INIT; 
    } 
    else if (index >= size_) 
    { 
    ret = common::OB_INVALID_ARGUMENT; 
    } 
    else 
    { 
    int64_t byte_index = index/8; 
    int64_t bit_index = index % 8; 
    while (true) 
    { 
     uint8_t ov = bits_[byte_index]; 
     if (!(ov & BIT_MASKS[bit_index])) 
     { 
     // have not locked 
     break; 
     } 
     if (ov == ATOMIC_CAS(&(bits_[byte_index]), ov, ov & ~BIT_MASKS[bit_index])) 
     { 
     break; 
     } 
    } 
    } 
    return ret; 
} 
+4

沒有代碼,沒有人可以幫助你。嘗試創建一個證明問題的[SSCCE](http://sscce.org/)。 – 2013-03-16 05:08:34

+0

不僅「goto」會導致您的應用程序進入循環。 – 2013-03-16 05:11:30

+0

您可能正在尋找某種形式的函數內聯,其中編譯器決定跳轉到代碼塊而不是進行實際的調用。沒有看到原始代碼很難說。 – Jason 2013-03-16 05:44:12

回答

2

我懷疑問題是在循環中BitLock::lock

while (true) 
    { 
     uint8_t ov = bits_[byte_index]; 
     if (ov & BIT_MASKS[bit_index]) 
     { 
     continue; 
     } 
     if (ov == ATOMIC_CAS(&(bits_[byte_index]), ov, ov | BIT_MASKS[bit_index])) 
     { 
     break; 
     } 
    } 

ATOMIC_CAS宏將充當內存屏障,防止編譯器/處理器從兩端猜測加載__sync_val_compare_and_swap,但不會在它之前做任何關於加載的事情,所以可以根據調用__sync_val_compare_and_swap之前的值bits_[byte_index]來優化條件ov & BIT_MASKS[bit_index],從而導致無限循環。

嘗試以下,來代替:

while (true) 
    { 
     uint8_t ov = bits_[byte_index]; 
     if (ov & BIT_MASKS[bit_index]) 
     { 
     __sync_synchronize(); // or define ATOMIC_SYNC if you prefer 
     continue; 
     } 
     if (ov == ATOMIC_CAS(&(bits_[byte_index]), ov, ov | BIT_MASKS[bit_index])) 
     { 
     break; 
     } 
    } 

__sync_synchronize存儲器屏障將阻止環路惡化爲微不足道的。

+0

是的,當我添加__sync_synchronize()或asm(「暫停」)或修改代碼時,它不會產生無限循環:if(!(ov&BIT_MASKS [bit_index])&& ov == ATOMIC_CAS(&(bits_ [byte_index]),ov,ov | BIT_MASKS [bit_index])) – user2176204 2013-03-17 01:58:26

+0

好吧,很高興知道...另外,只是要小心,我敢肯定,即使if(!(ov&BIT_MASKS [bit_index])&& ov == ATOMIC_CAS(&(bits_ [byte_index]),ov,ov | BIT_MASKS [bit_index]))'可能碰巧工作,我認爲它不可靠;如果前者的條件是'false',那麼編譯器就不會像原來的代碼那樣執行完全相同的優化,而且在GCC的後續版本中它可能會很好。所以你應該放置一個明確的內存障礙,因爲這就是你所需要的語義是正確的。 – 2013-03-17 06:19:25

+0

此外,其他解決方案可能會阻止*編譯器*重新排序負載,但它不會阻止足夠智能的*處理器*執行相同操作。 – 2013-03-17 06:20:44

2

更新:

_Unwind_Resume功能

恢復unwind進程,在沒有返回到正常執行線程(即不是catch)的清理代碼結束時調用。

它根據this

它需要pthread_cancel可以參見The Secret Life of C++: Day 3: Exceptions

。 它在清理後恢復展開。

所以它似乎有一個線程其中有沒有catch例外......鑑於行爲與不同版本的GCC和不同的優化級別的不同我敢說你正在使用線程和有一個race condition