2013-03-16 91 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