+ All Categories
Home > Documents > Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013...

Dont Try This at Work Low Level Threading with C++11 Tony Van Eerd, BlackBerry May 13, 2013...

Date post: 26-Mar-2015
Category:
Upload: claire-hernandez
View: 217 times
Download: 2 times
Share this document with a friend
Popular Tags:
71
Don’t 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
Transcript
Page 1: 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.

Don’t Try This at WorkLow Level Threading with C++11

Tony Van Eerd, BlackBerry

May 13, 2013

Copyright Tony Van Eerd (2009-2013) and BlackBerry, Inc. 2012-2013

Page 2: 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.

May 15, 2012 2

•The C++ (11) Memory Model

• C++ (11) Atomics

• What you can but shouldn’t do with them

Things to discuss

Page 3: 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.

The Memory Model

May 15, 2012 3

?

Page 4: 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.

4May 15, 2012

?

Page 5: 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.

6May 15, 2012

?

Page 6: 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.

7May 15, 2012

2009 2013 2 16

Predicted # of Spocks in Star Trek 2013,According to Moore’s Law

(1968: 1 good + 1 evil)(1984: ~1½ Spocks)

← Cache Coherency

Page 7: 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.

9May 15, 2012

David Hilley - http://www.thegibson.org/blog/David Hilley - http://www.thegibson.org/blog/

Page 8: 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.

10May 15, 2012

Page 9: 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.

11May 15, 2012

Want

Page 10: 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.

12May 15, 2012

Got

Page 11: 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.

13May 15, 2012

Got13May 15, 2012

Want

Page 12: 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.

14May 15, 2012

Also Want

14May 15, 2012

Want

Page 13: 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.

15May 15, 2012

Speed15May 15, 2012

SequentialConsistenc

y

...and eat it too

Cake…

Page 14: 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.

May 15, 2012 16

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αβγ

AaαbBβCcγ

αβaγbAcBC

Thread α

αβγ

Thread a

abc

Thread A

ABC

Page 15: 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.

P1: W(x)1 . P2: R(x)1 R(x)2 . P3: R(x)2 R(x)2 . P4: W(x)2 .

May 15, 2012 17

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 - http://www.cs.nmsu.edu/~pfeiffer

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, Yeah, well, you know, that's just, like, your that's just, like, your opinion, man.opinion, man.

Page 16: 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.

May 15, 2012 18

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…

Page 17: 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.

May 15, 2012 19

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 don’t care what the others think. (But are each individually, internally, consistent.)

Page 18: 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.

May 15, 2012 20

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 don’t care what the others think. (But are each individually, internally, consistent.)

(relaxing what we mean by “precision” here)

Page 19: 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.

C++ Atomics

May 15, 2012 21

Have your cake and eat it too.

Page 20: 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.

C++ Atomics

May 15, 2012 22

Have your cake and eat it too.

But be careful, you baked it!

Page 21: 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.

23May 15, 2012

namespace std {// 29.3, order and consistencyenum memory_order;template <class T>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 typestemplate<class T> struct atomic;template<> struct atomic<integral >;template<class T> struct atomic<T*>;// 29.6.1, general operations on atomic types// In the following declarations, atomic-type is either// atomic<T> or a named base class for T from// Table 145 or inferred from Table 146 or from bool.// If it is atomic<T>, then the declaration is a template// declaration prefixed with template <class T>.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;// 29.6.2, templated operations on atomic typestemplate <class T>T atomic_fetch_add(volatile atomic<T>*, T) noexcept;template <class T>T atomic_fetch_add(atomic<T>*, T) noexcept;template <class T>T atomic_fetch_add_explicit(volatile atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_add_explicit(atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_sub(volatile atomic<T>*, T) noexcept;template <class T>T atomic_fetch_sub(atomic<T>*, T) noexcept;template <class T>T atomic_fetch_sub_explicit(volatile atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_sub_explicit(atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_and(volatile atomic<T>*, T) noexcept;template <class T>T atomic_fetch_and(atomic<T>*, T) noexcept;template <class T>T atomic_fetch_and_explicit(volatile atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_and_explicit(atomic<T>*, T, memory_order) noexcept;template <class T>

T atomic_fetch_or(volatile atomic<T>*, T) noexcept;template <class T>T atomic_fetch_or(atomic<T>*, T) noexcept;template <class T>T atomic_fetch_or_explicit(volatile atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_or_explicit(atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_xor(volatile atomic<T>*, T) noexcept;template <class T>T atomic_fetch_xor(atomic<T>*, T) noexcept;template <class T>T atomic_fetch_xor_explicit(volatile atomic<T>*, T, memory_order) noexcept;template <class T>T atomic_fetch_xor_explicit(atomic<T>*, T, memory_order) noexcept;// 29.6.3, arithmetic operations on atomic types// In the following declarations, atomic-integral is either// atomic<T> or a named base class for T from// Table 145 or inferred from Table 146.// If it is atomic<T>, 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;// 29.6.4, partial specializations for pointerstemplate <class T>T* atomic_fetch_add(volatile atomic<T*>*, ptrdiff_t) noexcept;template <class T>T* atomic_fetch_add(atomic<T*>*, ptrdiff_t) noexcept;template <class T>T* atomic_fetch_add_explicit(volatile atomic<T*>*, ptrdiff_t, memory_order) noexcept;template <class T>T* atomic_fetch_add_explicit(atomic<T*>*, ptrdiff_t, memory_order) noexcept;template <class T>T* atomic_fetch_sub(volatile atomic<T*>*, ptrdiff_t) noexcept;template <class T>T* atomic_fetch_sub(atomic<T*>*, ptrdiff_t) noexcept;template <class T>T* atomic_fetch_sub_explicit(volatile atomic<T*>*, ptrdiff_t, memory_order) noexcept;template <class T>T* atomic_fetch_sub_explicit(atomic<T*>*, ptrdiff_t, memory_order) noexcept;// 29.6.5, initialization#define ATOMIC_VAR_INIT(value) see below// 29.7, flag type and operationsstruct 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, fencesextern "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 <atomic>

Page 22: 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.

24May 15, 2012

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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 <class T> struct atomic { ...};

Page 23: 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.

25May 15, 2012

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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; ...};

Page 24: 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.

VOLATILEVOLATILENOTNOTFORFORHEREHERE

26May 15, 2012

VOLATILE

VOLATILE

VOLATILE

VOLATILE

VOLATILE

VOLATILE

Page 25: 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.

27May 15, 2012

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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; ...};

Page 26: 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.

28May 15, 2012

template <class T> struct atomic {

void store(T, memory_order = memory_order_seq_cst); T load(memory_order = memory_order_seq_cst); ...};

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

// 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);

Page 27: 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.

29May 15, 2012

template <class T> 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<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

Page 28: 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.

30May 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”

Page 29: 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.

31May 15, 2012

template <class T> 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<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

Page 30: 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.

32May 15, 2012

template <class T> 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<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

Page 31: 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.

33May 15, 2012

template <class T> 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<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

Page 32: 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.

34May 15, 2012

template <class T> 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<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

Page 33: 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.

35May 15, 2012

Sequential Consistency vs Acquire/Release vs RelaxedAn 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

seq_cst

release

acquire

seq_cst

seq_cst

relaxed

relaxed

relaxed

relaxed

boom

P.S.

Locks useAcquire/Release

(not S.C.)

Page 34: 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.

36May 15, 2012

x 1st

y 2nd

y == 0 implies

y 1st

x 2nd

x == 0 implies

Sequential Consistency vs Acquire/Release vs Relaxed

∴ z != 0

Page 35: 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.

38May 15, 2012

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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};

Page 36: 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.

template <class T> 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);};

39May 15, 2012

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

static SharedData data;static atomic<bool> 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<bool> locked;

if(!locked.exchange(true)){ do_exclusive(data); locked.store(false);}

Page 37: 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.

May 15, 2012 40

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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<int> 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 <class T> 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 <class T> 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<int> count;

int next;int was = count.load();

do { next = was + 1;} while (!count.compare_exchange_weak(was, next, acq_rel, relaxed));

^ atomically

OR...

Page 38: 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.

May 15, 2012 41

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> struct atomic{ T fetch_add(T, memory_order = memory_order_seq_cst); T operator++(int);};

static atomic<int> count;

count++;// orcount.fetch_add(memory_order_acq_rel);

do { next = (was + 1) % length;} while (!count.compare_exchange_weak(was, next));

Page 39: 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.

May 15, 2012 42

template<class T> struct atomic;template<> struct atomic< integral >;template<class T> struct atomic<T*>;

template <class T> 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(){}

Page 40: 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.

May 15, 2012 43

void push(Val val){ //...?}

Page 41: 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.

May 15, 2012 44

void push(Val val){ Node * newhead = new Node(val);}

Page 42: 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.

May 15, 2012 45

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

Page 43: 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.

May 15, 2012 46

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

Page 44: 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.

May 15, 2012 47

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

Page 45: 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.

May 15, 2012 48

void push(Val val){ Node * newhead = new Node(val); Node * oldhead = stack.head;

do { next = was + 1; } while (!count.compare_exchange_weak(was, next));}

Page 46: 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.

May 15, 2012 49

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));}

Page 47: 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.

May 15, 2012 50

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));}

Page 48: 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.

May 15, 2012 51

Val pop(){ //...?}

Page 49: 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.

May 15, 2012 52

Val pop(){ Node * oldhead = stack.head;}

Page 50: 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.

May 15, 2012 53

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

Page 51: 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.

May 15, 2012 54

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

Page 52: 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.

May 15, 2012 55

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

Page 53: 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.

May 15, 2012 56

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

Page 54: 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.

May 15, 2012 57

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;}

Page 55: 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.

May 15, 2012 58

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;}

Page 56: 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.

May 15, 2012 59

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;}

Page 57: 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.

May 15, 2012 60

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;}

Page 58: 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.

May 15, 2012 61

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;}

Page 59: 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.

May 15, 2012 62

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;}

Page 60: 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.

May 15, 2012 63

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;}

Page 61: 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.

May 15, 2012 64

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;}

ABAABA

// 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;

Page 62: 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.

More Scary Things 42

memory_order_consume

False Sharing

Bonus Question…

65May 15, 2012

Page 63: 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.

More Scary Things66May 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

Page 64: 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.

More Scary Things67May 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);

Page 65: 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.

More Scary Things68May 15, 2012

prev.compare_exchange(...);

False Sharing

next | prev |

next.load();

struct Node{ atomic<Node*> next; atomic<Node*> prev; //…}

Page 66: 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.

More Scary Things69May 15, 2012

Bonus Question…

• atomic<T> 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…)

Page 67: 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.

70May 15, 2012

x 1st

y 2nd

y == 0 implies

y 1st

x 2nd

x == 0 implies

Sequential Consistency vs Acquire/Release vs Relaxed

∴ z != 0

Page 68: 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.

71May 15, 2012

Page 69: 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.

Thanks to…

Michael Wong, IBM Toronto Lab, [email protected]

Hans Boehm, Hewlett-Packard, http://www.hpl.hp.com/personal/Hans_Boehm

Joe Pfeiffer, New Mexico State University, http://www.cs.nmsu.edu/~pfeiffer

Bartosz Milewski, http://bartoszmilewski.com

Anthony Williams, http://www.justsoftwaresolutions.co.uk/

Dmitriy V’jukov, http://www.1024cores.net/

David Hilley, http://www.thegibson.org/blog/

Jeremy Manson, http://jeremymanson.blogspot.com

72May 15, 2012

Page 70: 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.

73May 15, 2012

Use Locks!

Page 71: 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.

74May 15, 2012

from Abstrusegoose.com - licensed under CC BY-NC 3.0 ^atomics


Recommended