Presentation is loading. Please wait.

Presentation is loading. Please wait.

Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013 Copyright Tony Van Eerd (2009-2013) and BlackBerry, Inc. 2012-2013.

Similar presentations


Presentation on theme: "Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013 Copyright Tony Van Eerd (2009-2013) and BlackBerry, Inc. 2012-2013."— Presentation transcript:

1 Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013 Copyright Tony Van Eerd ( ) and BlackBerry, Inc

2 May 15, The C++ (11) Memory Model C++ (11) Atomics What you can but shouldnt do with them Things to discuss

3 The Memory Model May 15, ?

4 4 ?

5 5 ?

6 6 ?

7 Predicted # of Spocks in Star Trek 2013, According to Moores Law (1968: 1 good + 1 evil) (1984: ~1½ Spocks) Cache Coherency

8 8 May 15, 2012

9 9 David Hilley -

10 10 May 15, 2012

11 11 May 15, 2012 Want

12 12 May 15, 2012 Got

13 13 May 15, 2012 Got 13 May 15, 2012 Want

14 14 May 15, 2012 Also Want 14 May 15, 2012 Want

15 15 May 15, 2012 Speed 15 May 15, 2012 Sequential Consistency...and eat it too Cake…

16 May 15, Sequential Consistency: All memory reads and writes of all threads are executed as if interleaved in some global order, consistent with the program order of each thread. A Bit of Precision… ABCabcαβγABCabcαβγ AaαbBβCcγAaαbBβCcγ αβaγbAcBCαβaγbAcBC Thread α α β γ Thread a a b c Thread A A B C

17 P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)2 R(x)2. P4: W(x)2. May 15, Sequential Consistency: All memory reads and writes of all threads are executed as if interleaved in some global order, consistent with the program order of each thread. A Bit of Precision… P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)1 R(x)2. P4: W(x)2. P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)2 R(x)1. P4: W(x)2. P1: W(x)1. P2: R(x)? R(x)?. P3: R(x)? R(x)?. P4: W(x)2. P1: W(x)1. P2: R(x)? R(x)?. P3: R(x)? R(x)?. P4: W(x)2. Joe Pfeiffer, New Mexico State University - P1: W(x)1. P2: R(x)2 R(x)1. P3: R(x)2 R(x)1. P4: W(x)2. P1: W(x)1. P2: R(x)2 R(x)1. P3: R(x)2 R(x)1. P4: W(x)2. P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)2 R(x)2. P4: W(x)2. P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)1 R(x)2. P4: W(x)2. P1: W(x)1. P2: R(x)1 R(x)2. P3: R(x)2 R(x)1. P4: W(x)2. RelaxRelax Yeah, well, you know, that's just, like, your opinion, man.

18 May 15, Sequential Consistency: All memory reads and writes of all threads are executed as if interleaved in some global order, consistent with the program order of each thread. A Bit of Precision…

19 May 15, Sequential Consistency: All memory reads and writes of all threads are executed as if interleaved in some global order, consistent with the program order of each thread. A Bit of Precision… Relaxed Memory Model: 16 egotistical and/or layed-back Spocks that each dont care what the others think. (But are each individually, internally, consistent.)

20 May 15, Sequential Consistency: All memory reads and writes of all threads are executed as if interleaved in some global order, consistent with the program order of each thread. A Bit of Precision… Relaxed Memory Model: 16 egotistical and/or layed-back Spocks that each dont care what the others think. (But are each individually, internally, consistent.) (relaxing what we mean by precision here)

21 C++ Atomics May 15, Have your cake and eat it too.

22 C++ Atomics May 15, Have your cake and eat it too. But be careful, you baked it!

23 23 May 15, 2012 namespace std { // 29.3, order and consistency enum memory_order; template T kill_dependency(T y) noexcept; // 29.4, lock-free property #define ATOMIC_BOOL_LOCK_FREE unspecified #define ATOMIC_CHAR_LOCK_FREE unspecified #define ATOMIC_CHAR16_T_LOCK_FREE unspecified #define ATOMIC_CHAR32_T_LOCK_FREE unspecified #define ATOMIC_WCHAR_T_LOCK_FREE unspecified #define ATOMIC_SHORT_LOCK_FREE unspecified #define ATOMIC_INT_LOCK_FREE unspecified #define ATOMIC_LONG_LOCK_FREE unspecified #define ATOMIC_LLONG_LOCK_FREE unspecified #define ATOMIC_POINTER_LOCK_FREE unspecified // 29.5, generic types template struct atomic; template<> struct atomic ; template struct atomic ; // , general operations on atomic types // In the following declarations, atomic-type is either // atomic or a named base class for T from // Table 145 or inferred from Table 146 or from bool. // If it is atomic, then the declaration is a template // declaration prefixed with template. bool atomic_is_lock_free(const volatile atomic-type *) noexcept; bool atomic_is_lock_free(const atomic-type *) noexcept; void atomic_init(volatile atomic-type *, T) noexcept; void atomic_init(atomic-type *, T) noexcept; void atomic_store(volatile atomic-type *, T) noexcept; void atomic_store(atomic-type *, T) noexcept; void atomic_store_explicit(volatile atomic-type *, T, memory_order) noexcept; void atomic_store_explicit(atomic-type *, T, memory_order) noexcept; T atomic_load(const volatile atomic-type *) noexcept; T atomic_load(const atomic-type *) noexcept; T atomic_load_explicit(const volatile atomic-type *, memory_order) noexcept; T atomic_load_explicit(const atomic-type *, memory_order) noexcept; T atomic_exchange(volatile atomic-type *, T) noexcept; T atomic_exchange(atomic-type *, T) noexcept; T atomic_exchange_explicit(volatile atomic-type *, T, memory_order) noexcept; T atomic_exchange_explicit(atomic-type *, T, memory_order) noexcept; bool atomic_compare_exchange_weak(volatile atomic-type *, T*, T) noexcept; bool atomic_compare_exchange_weak(atomic-type *, T*, T) noexcept; bool atomic_compare_exchange_strong(volatile atomic-type *, T*, T) noexcept; bool atomic_compare_exchange_strong(atomic-type *, T*, T) noexcept; bool atomic_compare_exchange_weak_explicit(volatile atomic-type *, T*, T, memory_order, memory_order) noexcept; bool atomic_compare_exchange_weak_explicit(atomic-type *, T*, T. memory_order, memory_order) noexcept; bool atomic_compare)exchange_strong_explicit(volatile atomic-type *, T*, T, memory_order, memory_order) noexcept; bool atomic_compare_exchange_strong_explicit(atomic-type *, T*, T, memory_order, memory_order) noexcept; // , templated operations on atomic types template T atomic_fetch_add(volatile atomic *, T) noexcept; template T atomic_fetch_add(atomic *, T) noexcept; template T atomic_fetch_add_explicit(volatile atomic *, T, memory_order) noexcept; template T atomic_fetch_add_explicit(atomic *, T, memory_order) noexcept; template T atomic_fetch_sub(volatile atomic *, T) noexcept; template T atomic_fetch_sub(atomic *, T) noexcept; template T atomic_fetch_sub_explicit(volatile atomic *, T, memory_order) noexcept; template T atomic_fetch_sub_explicit(atomic *, T, memory_order) noexcept; template T atomic_fetch_and(volatile atomic *, T) noexcept; template T atomic_fetch_and(atomic *, T) noexcept; template T atomic_fetch_and_explicit(volatile atomic *, T, memory_order) noexcept; template T atomic_fetch_and_explicit(atomic *, T, memory_order) noexcept; template T atomic_fetch_or(volatile atomic *, T) noexcept; template T atomic_fetch_or(atomic *, T) noexcept; template T atomic_fetch_or_explicit(volatile atomic *, T, memory_order) noexcept; template T atomic_fetch_or_explicit(atomic *, T, memory_order) noexcept; template T atomic_fetch_xor(volatile atomic *, T) noexcept; template T atomic_fetch_xor(atomic *, T) noexcept; template T atomic_fetch_xor_explicit(volatile atomic *, T, memory_order) noexcept; template T atomic_fetch_xor_explicit(atomic *, T, memory_order) noexcept; // , arithmetic operations on atomic types // In the following declarations, atomic-integral is either // atomic or a named base class for T from // Table 145 or inferred from Table 146. // If it is atomic, then the declaration is a template // specialization declaration prefixed with template <>. integral atomic_fetch_add(volatile atomic-integral *, integral ) noexcept; integral atomic_fetch_add(atomic-integral *, integral ) noexcept; integral atomic_fetch_add_explicit(volatile atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_add_explicit(atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_sub(volatile atomic-integral *, integral ) noexcept; integral atomic_fetch_sub(atomic-integral *, integral ) noexcept; integral atomic_fetch_sub_explicit(volatile atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_sub_explicit(atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_and(volatile atomic-integral *, integral ) noexcept; integral atomic_fetch_and(atomic-integral *, integral ) noexcept; integral atomic_fetch_and_explicit(volatile atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_and_explicit(atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_or(volatile atomic-integral *, integral ) noexcept; integral atomic_fetch_or(atomic-integral *, integral ) noexcept; integral atomic_fetch_or_explicit(volatile atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_or_explicit(atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_xor(volatile atomic-integral *, integral ) noexcept; integral atomic_fetch_xor(atomic-integral *, integral ) noexcept; integral atomic_fetch_xor_explicit(volatile atomic-integral *, integral, memory_order) noexcept; integral atomic_fetch_xor_explicit(atomic-integral *, integral, memory_order) noexcept; // , partial specializations for pointers template T* atomic_fetch_add(volatile atomic *, ptrdiff_t) noexcept; template T* atomic_fetch_add(atomic *, ptrdiff_t) noexcept; template T* atomic_fetch_add_explicit(volatile atomic *, ptrdiff_t, memory_order) noexcept; template T* atomic_fetch_add_explicit(atomic *, ptrdiff_t, memory_order) noexcept; template T* atomic_fetch_sub(volatile atomic *, ptrdiff_t) noexcept; template T* atomic_fetch_sub(atomic *, ptrdiff_t) noexcept; template T* atomic_fetch_sub_explicit(volatile atomic *, ptrdiff_t, memory_order) noexcept; template T* atomic_fetch_sub_explicit(atomic *, ptrdiff_t, memory_order) noexcept; // , initialization #define ATOMIC_VAR_INIT(value) see below // 29.7, flag type and operations struct atomic_flag; bool atomic_flag_test_and_set(volatile atomic_flag*) noexcept; bool atomic_flag_test_and_set(atomic_flag*) noexcept; bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order) noexcept; bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order) noexcept; void atomic_flag_clear(volatile atomic_flag*) noexcept; void atomic_flag_clear(atomic_flag*) noexcept; void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order) noexcept; void atomic_flag_clear_explicit(atomic_flag*, memory_order) noexcept; #define ATOMIC_FLAG_INIT see below // 29.8, fences extern "C" void atomic_thread_fence(memory_order) noexcept; extern "C" void atomic_signal_fence(memory_order) noexcept; typedef enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; } #include

24 24 May 15, 2012 template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { void store(T, memory_order = memory_order_seq_cst) volatile noexcept; void store(T, memory_order = memory_order_seq_cst) noexcept; T load(memory_order = memory_order_seq_cst) const volatile noexcept; T load(memory_order = memory_order_seq_cst) const noexcept;... }; template struct atomic {... };

25 25 May 15, 2012 template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { void store(T, memory_order = memory_order_seq_cst) volatile noexcept; void store(T, memory_order = memory_order_seq_cst) noexcept; T load(memory_order = memory_order_seq_cst) const volatile noexcept; T load(memory_order = memory_order_seq_cst) const noexcept;... };

26 VOLATILENOTFORHERE 26 May 15, 2012 VOLATILE

27 27 May 15, 2012 template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { void store(T, memory_order = memory_order_seq_cst) volatile noexcept; void store(T, memory_order = memory_order_seq_cst) noexcept; T load(memory_order = memory_order_seq_cst) const volatile noexcept; T load(memory_order = memory_order_seq_cst) const noexcept;... };

28 28 May 15, 2012 template struct atomic { void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst);... }; template struct atomic; template<> struct atomic ; template struct atomic ; // C-like: T atomic_load(const A * object); T atomic_load(const volatile A * object); T atomic_load_explicit(const A * object, memory_order); T atomic_load_explicit(const volatile A * object, memory_order); bool atomic_compare_exchange_weak_explicit( volatile A * object, C * expected, C desired, memory_order success, memory_order failure);

29 29 May 15, 2012 template struct atomic // generic T, integral, pointer, bool { atomic() = default; constexpr atomic(T); atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); T operator=(T t) { store(t); } operator T() { return load(); } T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); bool is_lock_free(); }; template struct atomic; template<> struct atomic ; template struct atomic ;

30 30 May 15, 2012 struct atomic_flag { atomic_flag() = default; atomic_flag(const atomic_flag&) = delete; atomic_flag& operator=(const atomic_flag&) = delete; bool test_and_set(memory_order = memory_order_seq_cst); void clear(memory_order = memory_order_seq_cst); }; atomic_flag guard = ATOMIC_FLAG_INIT; struct atomic_flag; is_lock_free == true

31 31 May 15, 2012 template struct atomic // generic T, integral, pointer, bool { atomic() = default; constexpr atomic(T); atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); T operator=(T t) { store(t); } operator T() { return load(); } T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); bool is_lock_free(); }; template struct atomic; template<> struct atomic ; template struct atomic ;

32 32 May 15, 2012 template struct atomic { // pointers and intergrals... // as above // both: T fetch_add(T, memory_order = memory_order_seq_cst); T fetch_sub(T, memory_order = memory_order_seq_cst); T operator++(int); T operator--(int); T operator++(); // atomic! not the same as: a = a + 1 T operator--(); T operator+=(T); T operator-=(T); // integrals only: T fetch_and(T, memory_order = memory_order_seq_cst); T fetch_or(T, memory_order = memory_order_seq_cst); T fetch_xor(T, memory_order = memory_order_seq_cst); T operator&=(T); T operator|=(T); T operator^=(T); }; template struct atomic; template<> struct atomic ; template struct atomic ;

33 33 May 15, 2012 template struct atomic { atomic() = default; constexpr atomic(T); atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); T operator=(T t) { store(t); } operator T() { return load(); } T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); bool is_lock_free(); }; template struct atomic; template<> struct atomic ; template struct atomic ;

34 34 May 15, 2012 template struct atomic { void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst }; template struct atomic; template<> struct atomic ; template struct atomic ;

35 35 May 15, 2012 Sequential Consistency vs Acquire/Release vs Relaxed An operation A synchronizes-with an operation B if A is a store to some atomic variable m, with memory_order_release / memory_order_seq_cst, and B is a load from the same variable m, with memory_order_acquire / memory_order_seq_cst, and B reads the value stored by A. seq_cst release acquire seq_cst relaxed boom P.S. Locks use Acquire/Release (not S.C.)

36 36 May 15, 2012 x 1 st y 2 nd y == 0 implies y 1 st x 2 nd x == 0 implies Sequential Consistency vs Acquire/Release vs Relaxed z != 0

37 37 May 15, 2012

38 38 May 15, 2012 template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst };

39 template struct atomic { T exchange(T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; 39 May 15, 2012 template struct atomic; template<> struct atomic ; template struct atomic ; static SharedData data; static atomic locked; if(!locked.exchange(true, memory_order_acquire)) { do_exclusive(data); locked.store(false, memory_order_release); } static SharedData data; static atomic_flag locked; if(!locked.test_and_set()) { do_exclusive(data); locked.clear(); } static SharedData data; static atomic locked; if(!locked.exchange(true)) { do_exclusive(data); locked.store(false); }

40 May 15, template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; static atomic count; int next; int was = count.load(); do { next = was + 1; } while (!count.compare_exchange_weak(was, next)); // compare_exchange: if (count == was) count = next; else was = count; // compare_exchange: if (count == was) count = next; else was = count; template struct atomic { bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; // compare_exchange: if (count untouched) count = next; else was = count; // compare_exchange: if (count untouched) count = next; else was = count; template struct atomic { bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_strong(T&, T, memory_order, memory_order); }; static atomic count; int next; int was = count.load(); do { next = was + 1; } while (!count.compare_exchange_weak(was, next, acq_rel, relaxed)); ^ atomically OR...

41 May 15, template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { T fetch_add(T, memory_order = memory_order_seq_cst); T operator++(int); }; static atomic count; count++; // or count.fetch_add(memory_order_acq_rel); do { next = (was + 1) % length; } while (!count.compare_exchange_weak(was, next));

42 May 15, template struct atomic; template<> struct atomic ; template struct atomic ; template struct atomic { bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst); bool compare_exchange_weak(T&, T, memory_order, memory_order); }; // Lock Free Stack... void push(T val) { } T pop() { }

43 May 15, void push(Val val) { //...? }

44 May 15, void push(Val val) { Node * newhead = new Node(val); }

45 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head; }

46 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head; newhead->next = oldhead; }

47 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head; newhead->next = oldhead; stack.head = newhead; }

48 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head; do { next = was + 1; } while (!count.compare_exchange_weak(was, next)); }

49 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head; do { newhead->next = oldhead; } while(!stack.head.compare_exchange_weak(oldhead, newhead)); }

50 May 15, void push(Val val) { Node * newhead = new Node(val); Node * oldhead = stack.head.load(relaxed); do { newhead->next = oldhead; } while(!stack.head.compare_exchange_weak(oldhead, newhead, release)); }

51 May 15, Val pop() { //...? }

52 May 15, Val pop() { Node * oldhead = stack.head; }

53 May 15, Val pop() { Node * oldhead = stack.head; Node * newhead = oldhead->next; }

54 May 15, Val pop() { Node * oldhead = stack.head; if (oldhead == NULL) throw StackEmpty(); Node * newhead = oldhead->next; }

55 May 15, Val pop() { Node * oldhead = stack.head; if (oldhead == NULL) throw StackEmpty(); Node * newhead = oldhead->next; }

56 May 15, Val pop() { Node * oldhead = stack.head; if (oldhead == NULL) throw StackEmpty(); Node * newhead = oldhead->next; }

57 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

58 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

59 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

60 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

61 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

62 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

63 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; }

64 May 15, Val pop() { Node * oldhead = stack.head.load(acquire); do { if (!oldhead) throw StackEmpty(); newhead = oldhead->next; } while (!head.compare_exchange_weak(oldhead, newhead, acq_rel)); Val val = oldhead->val; recycle(oldhead); return val; } ABA // compare_exchange: if (count == was) count = next; else was = count; // compare_exchange: if (count == was) count = next; else was = count; // compare_exchange: if (count untouched) count = next; else was = count; // compare_exchange: if (count untouched) count = next; else was = count;

65 More Scary Things 42 memory_order_consume False Sharing Bonus Question… 65 May 15, 2012

66 More Scary Things 66 May 15, 2012 // Thread 1: r1 = y.load(relaxed); x.store(r1, relaxed); assert(r1 == 42); // Thread 2: r2 = x.load(relaxed); y.store(42, relaxed); assert(r2 == 42); 42

67 More Scary Things 67 May 15, 2012 foo = 42; // publish p = &foo; memory_order_consume foo| | |bar | | p | int y = bar + 17; if (p != NULL) int x = *p; assert(x == 42);

68 More Scary Things 68 May 15, 2012 prev.compare_exchange(...); False Sharing next | prev | next.load(); struct Node { atomic next; atomic prev; //… }

69 More Scary Things 69 May 15, 2012 Bonus Question… atomic is implemented with locks if/when T is too large to be natively atomic. locks use acquire/release semantics atomics offer sequential consistency How do you implement sequential consistency given only acquire/release? (Note, that acq + rel != seq_cst, for example, recall…)

70 70 May 15, 2012 x 1 st y 2 nd y == 0 implies y 1 st x 2 nd x == 0 implies Sequential Consistency vs Acquire/Release vs Relaxed z != 0

71 71 May 15, 2012

72 Thanks to… Michael Wong, IBM Toronto Lab, Hans Boehm, Hewlett-Packard, Joe Pfeiffer, New Mexico State University, Bartosz Milewski, Anthony Williams, Dmitriy Vjukov, David Hilley, Jeremy Manson, 72 May 15, 2012

73 73 May 15, 2012 Use Locks!

74 74 May 15, 2012 from Abstrusegoose.com - licensed under CC BY-NC 3.0 ^ atomics


Download ppt "Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013 Copyright Tony Van Eerd (2009-2013) and BlackBerry, Inc. 2012-2013."

Similar presentations


Ads by Google