Memory lock with 64-bit pointers

The book of Herlichi and Shavit (The Art of Multiprocessor Programming ) a solution for the reclamation of memory uses Java AtomicStampedReference<T>;.

To write one in C ++ for x86_64, it seems to me that at least a shift operation of 12 bytes is required - 8 for a 64-bit pointer and 4 for int.

Is there support for x86 hardware for this, and if not, are there any pointers to how to make lifeless memory recovery without it?

+3
source share
7 answers

, , , ++. , - CMPXCHG16B Intel.

+3

Windows , , , , , . , , Boost .

, . , , , , .

+3

, x64 ; CMPXCHG16B.

+2

, , 64 . compare&set ( ASM GCC ICC):

inline bool CAS_ (volatile uint64_t* mem, uint64_t old_val, uint64_t new_val)
{
  unsigned long old_high = old_val >> 32, old_low = old_val;
  unsigned long new_high = new_val >> 32, new_low = new_val;

  char res = 0;
  asm volatile("lock; cmpxchg8b (%6);"
               "setz %7; "
               : "=a" (old_low),  // 0
                 "=d" (old_high)  // 1
               : "0" (old_low),   // 2
                 "1" (old_high),  // 3
                 "b" (new_low),   // 4
                 "c" (new_high),  // 5
                 "r" (mem),       // 6
                 "m" (res)        // 7
               : "cc", "memory");
  return res;
}

-. 40- 128- (, Nehalem). - , ..; , .

template <typename pointer_type, typename tag_type, int PtrAlign=7, int PtrWidth=40>
struct tagged_pointer
{
  static const unsigned int PtrMask = (1 << (PtrWidth - PtrAlign)) - 1;
  static const unsigned int TagMask = ~ PtrMask;

  typedef unsigned long raw_value_type;

  raw_value_type raw_m_;

  tagged_pointer () : raw_m_(0) {}
  tagged_pointer (pointer_type ptr) { this->pack(ptr, 0); }
  tagged_pointer (pointer_type ptr, tag_type tag) { this->pack(ptr, tag); }

  void pack (pointer_type ptr, tag_type tag)
  {
    this->raw_m_ = 0;
    this->raw_m_ |= ((ptr >> PtrAlign) & PtrMask);
    this->raw_m_ |= ((tag << (PtrWidth - PtrAlign)) & TagMask);
  }

  pointer_type ptr () const
  {
    raw_value_type p = (this->raw_m_ & PtrMask) << PtrAlign;
    return *reinterpret_cast<pointer_type*>(&p);
  }

  tag_type tag () const
  {
    raw_value_type t = (this->raw_m_ & TagMask) >> (PtrWidth - PtrAlign_;
    return *reinterpret_cast<tag_type*>(&t);
  }
};

, , .

+2

: x86_64 gcc 128- CAS. -mcx16 gcc.

int main()
{
   __int128_t x = 0;
   __sync_bool_compare_and_swap(&x,0,10);
   return 0;
}

:

gcc -mcx16 file.c
+2

, , ,

, , :

class AtomicReference<T>{
  public:
    void set(T *ref, int stamp){ ... }
    T *get(int *stamp){ ... }
  private:  
    T *_ref; 
    int _stamp; 

};

, :

  • set() , .
  • get() * , .

JDonner, , , .

: , - ( (test_and_set()! =..)). lockfree. , N.

N- ,

pragma 9.8.1, AtomicMarkableReference insteam . "" , , ( ). , , , .

, . .

+1

cmpxchg16b , , x86-64 .

, asm-inline. : Compare and Swap

However, you do not need this operation if you just want to prevent problems with the earlier version and ABA. The hazard pointer is simpler and does not require special asm code (as long as you use the atomic values ​​of C ++ 11). I have a repo on a bitpack with experimental implementations of various non-locking algorithms: Lock Free Experiment (beware that all of these implementations are experimental toys and not reliable and proven code for production.)

+1
source

Source: https://habr.com/ru/post/1711547/


All Articles