Shared memory concurrency Shared memory x = y = 0 x = y = 0 x = 1 if (x == 1) { Thread 1 Thread 2 if (y == 1) x = 0 print x y = 1 } Monday 11 May 15 10
Shared memory concurrency Shared memory x = y = 0 x = y = 0 x = 1 if (x == 1) { Thread 1 Thread 2 if (y == 1) x = 0 print x y = 1 } Intuitively this program always prints 0 Monday 11 May 15 10
Shared memory concurrency But if the compiler propagates the constant x = 1... x = y = 0 x = y = 0 x = 1 if (x == 1) { Thread 1 Thread 2 if (y == 1) x = 0 print x y = 1 } Monday 11 May 15 11
Shared memory concurrency But if the compiler propagates the constant x = 1... x = y = 0 x = y = 0 x = 1 if (x == 1) { Thread 1 Thread 2 if (y == 1) x = 0 print x y = 1 } print 1 ...the program always writes 1 rather than 0. Monday 11 May 15 11
This talk 0. Concurrency and optimisations, not so simple 1. The layman semantics 2. Escape lanes for the expert programmer 3. Compiler testing via a theory of sound optimisations 4. Escape lanes are a Pandora’s box 5. The way forward... Monday 11 May 15 12
The layman solution forbid data-races Monday 11 May 15 13
Standard way out: prohibit data races Monday 11 May 15 14
ADA 83 Data-races are errors Monday 11 May 15 15
Posix Threads Specification Data-races are errors Monday 11 May 15 16
C++2011 / C11 Les data-races sont des erreurs Data-races are errors Monday 11 May 15 17
C++2011 / C11 How to use C/C++ to implement low-level system code? Les data-races sont des erreurs Data-races are errors Monday 11 May 15 17
Escape lanes for expert programmers Monday 11 May 15 18
Low-level atomics in C11/C++11 std::atomic<int> flag0(0),flag1(0),turn(0); void lock(unsigned index) { if (0 == index) { Atomic variable declaration flag0.store(1, std::memory_order_relaxed); turn.exchange(1, std::memory_order_acq_rel); while (flag1.load(std::memory_order_acquire) && 1 == turn.load(std::memory_order_relaxed)) std::this_thread::yield(); } else { New syntax flag1.store(1, std::memory_order_relaxed); turn.exchange(0, std::memory_order_acq_rel); for memory accesses while (flag0.load(std::memory_order_acquire) && 0 == turn.load(std::memory_order_relaxed)) std::this_thread::yield(); } } Qualifier void unlock(unsigned index) { if (0 == index) { flag0.store(0, std::memory_order_release); } else { flag1.store(0, std::memory_order_release); } } Monday 11 May 15 19
The qualifiers LESS RELAXED MO_SEQ_CST MO_RELEASE / MO_ACQUIRE MO_RELEASE / MO_CONSUME MO_RELAXED MORE RELAXED Monday 11 May 15 20
The qualifiers LESS RELAXED Sequential consistent accesses MO_SEQ_CST MO_RELEASE / MO_ACQUIRE MO_RELEASE / MO_CONSUME MO_RELAXED MORE RELAXED Monday 11 May 15 20
The qualifiers LESS RELAXED Sequential consistent accesses MO_SEQ_CST Efficient implementation of message passing MO_RELEASE / MO_ACQUIRE MO_RELEASE / MO_CONSUME MO_RELAXED MORE RELAXED Monday 11 May 15 20
The qualifiers LESS RELAXED Sequential consistent accesses MO_SEQ_CST Efficient implementation of message passing MO_RELEASE / MO_ACQUIRE Efficient implementation of message passing on ARM/Power MO_RELEASE / MO_CONSUME MO_RELAXED MORE RELAXED Monday 11 May 15 20
The qualifiers LESS RELAXED Sequential consistent accesses MO_SEQ_CST Efficient implementation of message passing MO_RELEASE / MO_ACQUIRE Efficient implementation of message passing on ARM/Power MO_RELEASE / MO_CONSUME No synchronisation; direct access to hardware MO_RELAXED MORE RELAXED Monday 11 May 15 20
Memory access synchronisation x = y = 0 Thread 1 Thread 2 y = 1 if (x.load(MO_ACQUIRE) == 1) x.store(1,MO_RELEASE) r2 = y Monday 11 May 15 21
Memory access synchronisation x = y = 0 Thread 1 Thread 2 y = 1 if (x.load(MO_ACQUIRE) == 1) x.store(1,MO_RELEASE) r2 = y Non-atomic loads must return the most recent write in the happens-before order (unique in a DRF program) Monday 11 May 15 21
Understanding MO_RELAXED x = y = 0 Thread 1 Thread 2 y = 1 if (x.load(MO_RELAXED) == 1) x.store(1,MO_RELAXED) r2 = y Monday 11 May 15 22
Understanding MO_RELAXED x = y = 0 Thread 1 Thread 2 y = 1 if (x.load(MO_RELAXED) == 1) x.store(1,MO_RELAXED) r2 = y DATA RACE Two conflicting accesses not related by happens-before Monday 11 May 15 22
Understanding MO_RELAXED x = y = 0 Thread 1 Thread 2 y.store(1,MO_RELAXED) if (x.load(MO_RELAXED) == 1) x.store(1,MO_RELAXED) r2 = y.load(MO_RELAXED) WELL DEFINED but r2 = 0 is possible Monday 11 May 15 23
Intuition Understanding MO_RELAXED the compiler (or hardware) can reorder independent accesses x = y = 0 Thread 1 Thread 2 y.store(1,MO_RELAXED) if (x.load(MO_RELAXED) == 1) x.store(1,MO_RELAXED) r2 = y.load(MO_RELAXED) WELL DEFINED but r2 = 0 is possible Monday 11 May 15 23
Intuition Understanding MO_RELAXED the compiler (or hardware) can reorder independent accesses x = y = 0 Thread 1 Thread 2 y.store(1,MO_RELAXED) if (x.load(MO_RELAXED) == 1) x.store(1,MO_RELAXED) r2 = y.load(MO_RELAXED) Allow a RELAXED load to see any store that: WELL DEFINED - does not happens-after it but r2 = 0 is possible - is not hidden by an intervening store hb-ordered between them Monday 11 May 15 23
The full model is store a = case a of Store → T � → F r a − → b = ( a , b ) ∈ r visible side e ff ect set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before = is fence a = case a of Fence → T � → F { ab ∈ happens-before . let ( a , b ) = ab in rs element rs head a = visible side e ff ect actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before a b } same thread a rs head ∨ is atomic rmw a a r b = ( a , b ) ∈ r is lock or unlock a = is lock a ∨ is unlock a release-sequence visible sequence of side e ff ects tail = visible sequence of side e ff ects tail vsse head b = release sequence = a rel − − − − − − − − − → b = r a ̸ − → b = ( a , b ) / ∈ r modification-order is at atomic location b ∧ { c . vsse head − − − − − − − − − − → c ∧ is atomic action a = is release a rel ∧ ( happens-before ¬ ( b − − − − − − − − → c ) ∧ is atomic load a ∨ is atomic store a ∨ is atomic rmw a ( b = a rel ) ∨ modification-order modification-order r ( ∀ a . vsse head − − − − − − − − − − → a − − − − − − − − − − → c → = r − modification-order ( rs element a rel b ∧ a rel − − − − − − − − − − → b ∧ happens-before = ⇒ ¬ ( b − − − − − − − − → a )) } modification-order modification-order ( ∀ c . a rel − − − − − − − − − − → c − − − − − − − − − − → b = ⇒ is load or store a = is load a ∨ is store a rs element a rel c ))) r s r s a − → b − → c = a → b ∧ b − → c − myimage f s = { y . ∃ x ∈ s . ( y = f x ) } is read a = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order = relation over s rel = domain rel ⊆ s ∧ range rel ⊆ s is atomic load a ∨ is atomic rmw a ∨ is load a visible sequences of side e ff ects = visible sequences of side e ff ects = release sequence actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order a b } λ ( vsse head , b ) . rel − → | s = rel ∩ ( s × s ) ( b , if is at atomic location b then is write a = is atomic store a ∨ is atomic rmw a ∨ is store a { vsse head } ∪ hypothetical-release-sequence hypothetical release sequence = a − − − − − − − − − − − − − − − − → b = visible sequence of side e ff ects tail vsse head b is at atomic location b ∧ ( else rel | s = rel ∩ ( s × s ) ( b = a ) ∨ {} ) is acquire a = modification-order ( rs element a b ∧ a − − − − − − − − − − → b ∧ ( case memory order a of modification-order modification-order ( ∀ c . a − − − − − − − − − − → c − − − − − − − − − − → b = ⇒ rel Some mem ord → − → | s = rel ∩ ( s × s ) rs element a c ))) visible sequences of side e ff ects set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect = ( mem ord ∈ myimage ( visible sequences of side e ff ects actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect ) visible-side-e ff ect { Mo acquire , Mo acq rel , Mo seq cst } ∧ ( is read a ∨ is fence a )) ∨ rel | s = rel ∩ ( s × s ) (* 29.8:5 states that consume fences are acquire fences. *) hypothetical release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order = (( mem ord = Mo consume ) ∧ is fence a ) consistent reads from mapping = consistent reads from mapping = � None → is lock a ) hypothetical release sequence actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order a b } ( ∀ b . ( is read b ∧ is at non atomic location b ) = ⇒ strict preorder ord = irreflexive ord ∧ trans ord visible-side-e ff ect ( if ( ∃ a vse . a vse − − − − − − − − − → b ) visible-side-e ff ect rf then ( ∃ a vse . a vse − − − − − − − − − → b ∧ a vse − → b ) synchronizes-with is consume a = synchronizes with = a − − − − − − − − − − → b = rf total over s ord = else ¬ ( ∃ a . a − → b ))) ∧ is read a ∧ ( memory order a = Some Mo consume ) (* – additional synchronization, from thread create etc. – *) relation over s ord ∧ additional-synchronized-with a − − − − − − − − − − − − − − − − → b ∨ ord ord ( ∀ b . ( is read b ∧ is at atomic location b ) = ⇒ ( ∀ x ∈ s . ∀ y ∈ s . x − − → y ∨ y − − → x ∨ ( x = y )) ( if ( ∃ ( b � , vsse ) ∈ visible-sequences-of-side-e ff ects . ( b � = b )) is release a = ( same location a b ∧ a ∈ actions ∧ b ∈ actions ∧ ( then ( ∃ ( b � , vsse ) ∈ visible-sequences-of-side-e ff ects . ( case memory order a of (* – mutex synchronization – *) ( b � = b ) ∧ ( ∃ c ∈ vsse . c rf − → b )) strict total order over s ord = Some mem ord → sc ( is unlock a ∧ is lock b ∧ a − → b ) ∨ rf strict preorder ord ∧ total over s ord else ¬ ( ∃ a . a − → b ))) ∧ mem ord ∈ { Mo release , Mo acq rel , Mo seq cst } ∧ ( is write a ∨ is fence a ) (* – release/acquire synchronization – *) � None → is unlock a ) rf ( is release a ∧ is acquire b ∧ ¬ same thread a b ∧ ( ∀ ( x , a ) ∈ − → . | ord x − − → pred y = release-sequence rf rf ( ∃ c . a − − − − − − − − − → c − → b )) ∨ ∀ ( y , b ) ∈ − → . ord ord ord pred x ∧ x − − → y ∧ ¬ ( ∃ z . pred z ∧ x − − → z − − → y ) happens-before − − − − − − − − → b ∧ a is seq cst a = ( memory order a = Some Mo seq cst ) (* – fence synchronization – *) same location a b ∧ is at atomic location b ( is fence a ∧ is release a ∧ is fence b ∧ is acquire b ∧ modification-order = ⇒ ( x = y ) ∨ x − − − − − − − − − − → y ) ∧ | ord x − − → y = ( ∃ x . ∃ y . same location x y ∧ (* new CoWR *) location kind = ord ord ord is atomic action x ∧ is atomic action y ∧ is write x ∧ x − − → y ∧ ¬ ( ∃ z . x − − → z − − → y ) happens-before Mutex ( ∀ ( a , b ) ∈ − − − − − − − − → . sequenced-before sequenced-before a − − − − − − − − − → x ∧ y − − − − − − − − − → b ∧ | Non atomic ∀ c . hypothetical-release-sequence rf ( ∃ z . x − − − − − − − − − − − − − − − − → z − → y ))) ∨ rf | Atomic → b ∧ − c well founded r = wf r is write a ∧ same location a b ∧ is at atomic location b ( is fence a ∧ is release a ∧ modification-order = ⇒ ( c = a ) ∨ a − − − − − − − − − − → c ) ∧ is atomic action b ∧ is acquire b ∧ actions respect location kinds = actions respect location kinds = (* new CoRW *) type abbrev action id : string ( ∃ x . same location x b ∧ ∀ a . happens-before is atomic action x ∧ is write x ∧ ( ∀ ( a , b ) ∈ − − − − − − − − → . case location a of Some l → sequenced-before ∀ c . ( case location-kind l of a − − − − − − − − − → x ∧ rf type abbrev thread id : string c − → a ∧ Mutex → is lock or unlock a hypothetical-release-sequence rf ( ∃ z . x − − − − − − − − − − − − − − − − → z − → b ))) ∨ is write b ∧ same location a b ∧ is at atomic location a � Non atomic → is load or store a modification-order � Atomic → is load or store a ∨ is atomic action a ) ( is atomic action a ∧ is release a ∧ = ⇒ c − − − − − − − − − − → b ) ∧ type abbrev location : string � None → T is fence b ∧ is acquire b ∧ ( ∃ x . same location a x ∧ is atomic action x ∧ rf ( ∀ ( a , b ) ∈ − → . is atomic rmw b sequenced-before ⇒ a | modification-order x − − − − − − − − − → b ∧ = − − − − − − − − − − → b ) ∧ type abbrev val : string is at location kind = is at location kind = release-sequence rf ( ∃ z . a − − − − − − − − − → z − → x ))))) case location a of rf ( ∀ ( a , b ) ∈ − → . is seq cst b Some l → ( location-kind l = lk0 ) | sc modification-order � None → F = ⇒ ( ¬ is seq cst a ∧ ( ∀ x . x − → λ c . is write c ∧ same location b c b = ⇒ x − − − − − − − − − − → a )) ∨ memory order enum = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence = a | sc − → λ c . is write c ∧ same location b c b ) ∧ Mo seq cst | Mo relaxed synchronizes with actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence a b } | Mo release is at mutex location a = (* -Fence restrictions- *) | Mo acquire is at location kind a Mutex (* 29.3:3 *) | Mo consume carries-a-dependency-to | Mo acq rel carries a dependency to = a − − − − − − − − − − − − − → b = sequenced-before ( ∀ a . ∀ ( x , b ) ∈ − − − − − − − − − → . ∀ y . rf sequenced-before data-dependency → ) + b is at non atomic location a = a (( − → ∩ − − − − − − − − − → ) ∪ − − − − − − − − − ( is fence x ∧ is seq cst x ∧ is atomic action b ∧ is at location kind a Non atomic is write a ∧ same location a b ∧ action = a | sc rf − → x ∧ y − → b ) Lock of action id thread id location carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf = modification-order = ⇒ ( y = a ) ∨ a − − − − − − − − − − → y ) ∧ | Unlock of action id thread id location is at atomic location a = | Atomic load of action id thread id memory order enum location val carries a dependency to actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf a b } is at location kind a Atomic (* 29.3:4 *) | Atomic store of action id thread id memory order enum location val sequenced-before rf ( ∀ ( a , x ) ∈ − − − − − − − − − → . ∀ ( y , b ) ∈ → . − | Atomic rmw of action id thread id memory order enum location val val | Load of action id thread id location val ( is atomic action a ∧ is fence x ∧ is seq cst x ∧ dependency-ordered-before same thread a b = ( thread id of a = thread id of b ) dependency ordered before = a − − − − − − − − − − − − − − − → d = is write a ∧ same location a b ∧ | Store of action id thread id location val a ∈ actions ∧ d ∈ actions ∧ sc | Fence of action id thread id memory order enum x − → b ∧ is atomic action b ) ( ∃ b . is release a ∧ is consume b ∧ modification-order release-sequence rf = ⇒ ( y = a ) ∨ a − − − − − − − − − − → y ) ∧ threadwise relation over s rel = ( ∃ e . a − − − − − − − − − → e − → b ) ∧ relation over s rel ∧ ( ∀ ( a , b ) ∈ rel . same thread a b ) carries-a-dependency-to ( b − − − − − − − − − − − − − → d ∨ ( b = d ))) ( action id of ( Lock aid ) = aid ) ∧ (* 29.3:5 *) ( action id of ( Unlock aid ) = aid ) ∧ sequenced-before sequenced-before ( ∀ ( a , x ) ∈ − − − − − − − − − → . ∀ ( y , b ) ∈ − − − − − − − − − → . ∀ z . ( action id of ( Atomic load aid ) = aid ) ∧ ( is atomic action a ∧ is fence x ∧ is seq cst x ∧ same location a b = ( location a = location b ) dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to = ( action id of ( Atomic store aid ) = aid ) ∧ is write a ∧ is fence y ∧ is seq cst y ∧ ( action id of ( Atomic rmw aid ) = aid ) ∧ is atomic action b ∧ same location a b ∧ ( action id of ( Load aid ) = aid ) ∧ dependency ordered before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to a b } sc rf x − → y ∧ z → b ) − ( action id of ( Store aid ) = aid ) ∧ locations of actions = { l . ∃ a . ( location a = Some l ) } modification-order = ⇒ ( z = a ) ∨ a − − − − − − − − − − → z ) ( action id of ( Fence aid ) = aid ) simple happens before simple happens before = − − − − − − − − − − − − − → = well formed action a = sequenced-before synchronizes-with ( − − − − − − − − − → ∪ − − − − − − − − − − → ) + all data dependency all data dependency = − − − − − − − − − − − − → = ( thread id of ( Lock tid ) = tid ) ∧ case a of → mem ord ∈ rf carries-a-dependency-to ( thread id of ( Unlock tid ) = tid ) ∧ Atomic load mem ord ( − → ∪ − − − − − − − − − − − − − → ) + { Mo relaxed , Mo acquire , Mo seq cst , Mo consume } ( thread id of ( Atomic load tid ) = tid ) ∧ consistent simple happens before shb = ( thread id of ( Atomic store ) = tid ) ∧ � Atomic store mem ord → mem ord ∈ tid shb irreflexive ( − − → ) ( thread id of ( Atomic rmw tid ) = tid ) ∧ { Mo relaxed , Mo release , Mo seq cst } consistent control dependency = consistent control dependency = ( thread id of ( Load tid ) = tid ) ∧ � Atomic rmw mem ord → mem ord ∈ control-dependency all data dependency irreflexive (( − − − − − − − − − − − → ∪ − − − − − − − − − − − − → ) + ) { Mo relaxed , Mo release , Mo acquire , Mo acq rel , Mo seq cst , Mo consume } ( thread id of ( Store tid ) = tid ) ∧ inter-thread-happens-before � → T inter thread happens before = − − − − − − − − − − − − − − − → = ( thread id of ( Fence tid ) = tid ) synchronizes-with let r = − − − − − − − − − − → ∪ consistent execution actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc = dependency-ordered-before − − − − − − − − − − − − − − − → ∪ well formed threads actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ∧ well formed threads = well formed threads = ( memory order ( Atomic load mem ord ) = synchronizes-with sequenced-before ( − − − − − − − − − − → ◦ − − − − − − − − − → ) in consistent locks actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency sc ∧ ( inj on action id of ( actions ) ∧ Some mem ord ) ∧ r sequenced-before r let release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in ( memory order ( Atomic store ) = ( ∀ a . well formed action a ) ∧ ( − → ∪ ( − − − − − − − − − → ◦ − → )) + mem ord let hypothetical-release-sequence = hypothetical release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in Some mem ord ) ∧ threadwise relation over actions sequenced-before ∧ let synchronizes-with = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence in ( memory order ( Atomic rmw mem ord ) = threadwise relation over actions data-dependency ∧ let carries-a-dependency-to = carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf in threadwise relation over actions control-dependency ∧ Some mem ord ) ∧ consistent inter thread happens before = consistent inter thread happens before = let dependency-ordered-before = dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to in strict preorder sequenced-before ∧ ( memory order ( Fence mem ord ) = inter-thread-happens-before irreflexive ( − − − − − − − − − − − − − − − → ) let inter-thread-happens-before = inter thread happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency synchronizes-with dependency-ordered-before in Some mem ord ) ∧ strict preorder data-dependency ∧ let happens-before = happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency inter-thread-happens-before in ( memory order = strict preorder control-dependency ∧ let visible-side-e ff ect = visible side e ff ect set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before in None ) relation over actions additional-synchronized-with ∧ happens-before happens before = − − − − − − − − → = let visible-sequences-of-side-e ff ects = visible sequences of side e ff ects set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect in ( ∀ a . thread id of a ∈ threads ) ∧ sequenced-before inter-thread-happens-before consistent inter thread happens before inter-thread-happens-before ∧ actions respect location kinds ∧ − − − − − − − − − → ∪ − − − − − − − − − − − − − − − → consistent sc order actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order sc happens-before ∧ data-dependency ⊆ sequenced-before ( location ( Lock l ) = Some l ) ∧ consistent modification order actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency sc modification-order happens-before ∧ ( location ( Unlock l ) = Some l ) ∧ well formed reads from mapping actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf ∧ all sc actions = all sc actions = ( location ( Atomic load l ) = Some l ) ∧ consistent reads from mapping actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf sc modification-order happens-before visible-side-e ff ect visible-sequences-of-side-e ff ects ) well formed reads from mapping = well formed reads from mapping = { a . ( is seq cst a ∨ is lock a ∨ is unlock a ) } ( location ( Atomic store l ) = Some l ) ∧ ( location ( Atomic rmw ) = Some l ) ∧ rf l relation over actions ( − → ) ∧ ( location ( Load l ) = Some l ) ∧ rf rf ( ∀ a . ∀ a � . ∀ b . a → b ∧ a � − → b = − ⇒ ( a = a � )) ∧ indeterminate reads actions threads = indeterminate reads = ( location ( Store l ) = Some l ) ∧ consistent sc order = consistent sc order = rf rf ( ∀ ( a , b ) ∈ → . − { b . is read b ∧ ¬ ( ∃ a . a − → b ) } ( location ( Fence ) = None ) happens-before let sc happens before = − − − − − − − − → | all sc actions in same location a b ∧ modification-order ( value read b = value written a ) ∧ let sc mod order = − − − − − − − − − − → | all sc actions in sc ( a ̸ = b ) ∧ strict total order over all sc actions ( − → ) ∧ unsequenced races = unsequenced races = { ( a , b ) . ( value read ( Atomic load v ) = Some v ) ∧ ( is at mutex location a = ⇒ sc happens before sc − − − − − − − − − − − → ⊆ − → ∧ ( a ̸ = b ) ∧ same location a b ∧ ( is write a ∨ is write b ) ∧ ( value read ( Atomic rmw v ) = Some v ) ∧ is unlock a ∧ is lock b ) ∧ sc mod order sc same thread a b ∧ ( value read ( Load v ) = Some v ) ∧ − − − − − − − − → ⊆ − → ( is at non atomic location a = ⇒ ( value read = None ) sequenced-before sequenced-before ¬ ( a − − − − − − − − − → b ∨ b − − − − − − − − − → a ) } is store a ∧ is load b ) ∧ ( is at atomic location a = ⇒ consistent modification order = consistent modification order = ( is atomic store a ∨ is atomic rmw a ∨ is store a ) ( value written ( Atomic store v ) = Some v ) ∧ modification-order data races = data races = { ( a , b ) . ∧ ( is atomic load b ∨ is atomic rmw b ∨ is load b ))) ( ∀ a . ∀ b . a − − − − − − − − − − → b = ⇒ same location a b ) ∧ ( value written ( Atomic rmw v ) = Some v ) ∧ ( a ̸ = b ) ∧ same location a b ∧ ( is write a ∨ is write b ) ∧ ( ∀ l ∈ locations of actions . case location-kind l of ( value written ( Store v ) = Some v ) ∧ ¬ same thread a b ∧ Atomic → ( ( value written = None ) let actions at l = { a . ( location a = Some l ) } in ¬ ( is atomic action a ∧ is atomic action b ) ∧ all lock or unlock actions at lopt as = happens-before happens-before let writes at l = { a at l . ( is store a ∨ { a ∈ as . is lock or unlock a ∧ ( location a = lopt ) } ¬ ( a − − − − − − − − → b ∨ b − − − − − − − − → a ) } is atomic store a ∨ is atomic rmw a ) } in is lock a = strict total order over writes at l case a of Lock → T � → F modification-order data races � actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc = consistent locks = consistent locks = ( − − − − − − − − − − → | actions at l ) ∧ (* happens-before at the writes of l is a subset of mo for l *) ∀ l ∈ locations of actions . ( location-kind l = Mutex ) = ⇒ ( let release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in let lock unlock actions = happens-before modification-order let hypothetical-release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in − − − − − − − − → | writes at l ⊆ − − − − − − − − − − → ∧ is unlock a = all lock or unlock actions at ( Some l ) actions in let synchronizes-with = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence in (* Mo seq cst fences impose modification order *) case a of Unlock → T � → F sc let lock order = − → | lock unlock actions in let carries-a-dependency-to = carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf in sequenced-before sc sequenced-before ( − − − − − − − − − → ◦ ( − → | is fence ) ◦ − − − − − − − − − → | writes at l ) (* 30.4.1:5 - The implementation shall serialize those (lock and unlock) operations. *) let dependency-ordered-before = dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to in modification-order ⊆ − − − − − − − − − − → ) strict total order over lock unlock actions lock order ∧ let inter-thread-happens-before = inter thread happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency synchronizes-with dependency-ordered-before in is atomic load a = � → ( let happens-before = happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency inter-thread-happens-before in case a of Atomic load → T � → F let actions at l = { a . ( location a = Some l ) } in data races actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before (* 30.4.1:1 A thread owns a mutex from the time it successfully calls one of the lock functions until modification-order it calls unlock.*) ( − − − − − − − − − − → | actions at l ) = {} )) (* 30.4.1:20 Requires: The calling thread shall own the mutex. *) is atomic store a = (* 30.4.1:21 E ff ects: Releases the calling threads ownership of the mutex.*) cpp memory model opsem ( p ∈ � program ) = ( ∀ a u ∈ lock unlock actions . is unlock a u = ⇒ case a of Atomic store → T � → F visible-side-e ff ect let executions = { ( actions , threads , location-kind , sequenced-before , additional-synchronized-with , data-dependency , control-dependency , rf , modification-order , sc ) . visible side e ff ect = a − − − − − − − − − → b = ( ∃ a l ∈ lock unlock actions . happens-before opsem p actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ∧ consistent execution actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc } in − − − − − − − − → b ∧ a | lock order a l − − − − − − → a u ∧ same thread a l a u ∧ is lock a l )) ∧ if ∃ ( actions , threads , location-kind , sequenced-before , additional-synchronized-with , data-dependency , control-dependency , rf , modification-order , sc ) ∈ executions . is write a ∧ is read b ∧ same location a b ∧ is atomic rmw a = ( indeterminate reads actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf ̸ = {} ) ∨ ¬ ( ∃ c . ( c ̸ = a ) ∧ ( c ̸ = b ) ∧ ( unsequenced races actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ̸ = {} ) ∨ case a of Atomic rmw → T � → F (* 30.4.1:7 E ff ects: Blocks the calling thread until ownership of the mutex can be obtained for the is write c ∧ same location c b ∧ ( data races � actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc ̸ = {} ) calling thread.*) happens-before happens-before a − − − − − − − − → c − − − − − − − − → b ) (* 30.4.1:8 Postcondition: The calling thread owns the mutex. *) then {} ( ∀ a l ∈ lock unlock actions . is lock a l = ⇒ else executions is load a = case a of Load → T � → F ( ∀ a u ∈ lock unlock actions . | lock order − − − − − − → a l = ⇒ is unlock a u ))) a u Monday 11 May 15 24
The full model is store a = case a of Store → T � → F r a − → b = ( a , b ) ∈ r visible side e ff ect set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before = is fence a = case a of Fence → T � → F { ab ∈ happens-before . let ( a , b ) = ab in rs element rs head a = visible side e ff ect actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before a b } same thread a rs head ∨ is atomic rmw a a r b = ( a , b ) ∈ r is lock or unlock a = is lock a ∨ is unlock a release-sequence visible sequence of side e ff ects tail = visible sequence of side e ff ects tail vsse head b = release sequence = a rel − − − − − − − − − → b = r a ̸ − → b = ( a , b ) / ∈ r modification-order is at atomic location b ∧ { c . vsse head − − − − − − − − − − → c ∧ is atomic action a = is release a rel ∧ ( happens-before ¬ ( b − − − − − − − − → c ) ∧ is atomic load a ∨ is atomic store a ∨ is atomic rmw a ( b = a rel ) ∨ modification-order modification-order r ( ∀ a . vsse head − − − − − − − − − − → a − − − − − − − − − − → c → = r − modification-order ( rs element a rel b ∧ a rel − − − − − − − − − − → b ∧ happens-before = ⇒ ¬ ( b − − − − − − − − → a )) } modification-order modification-order ( ∀ c . a rel − − − − − − − − − − → c − − − − − − − − − − → b = ⇒ is load or store a = is load a ∨ is store a rs element a rel c ))) r s r s a − → b − → c = a → b ∧ b − → c − myimage f s = { y . ∃ x ∈ s . ( y = f x ) } is read a = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order = relation over s rel = domain rel ⊆ s ∧ range rel ⊆ s is atomic load a ∨ is atomic rmw a ∨ is load a visible sequences of side e ff ects = visible sequences of side e ff ects = release sequence actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order a b } λ ( vsse head , b ) . rel − → | s = rel ∩ ( s × s ) ( b , if is at atomic location b then is write a = is atomic store a ∨ is atomic rmw a ∨ is store a { vsse head } ∪ hypothetical-release-sequence hypothetical release sequence = a − − − − − − − − − − − − − − − − → b = visible sequence of side e ff ects tail vsse head b is at atomic location b ∧ ( else rel | s = rel ∩ ( s × s ) ( b = a ) ∨ {} ) is acquire a = modification-order ( rs element a b ∧ a − − − − − − − − − − → b ∧ ( case memory order a of modification-order modification-order ( ∀ c . a − − − − − − − − − − → c − − − − − − − − − − → b = ⇒ rel Some mem ord → − → | s = rel ∩ ( s × s ) rs element a c ))) visible sequences of side e ff ects set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect = ( mem ord ∈ myimage ( visible sequences of side e ff ects actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect ) visible-side-e ff ect { Mo acquire , Mo acq rel , Mo seq cst } ∧ ( is read a ∨ is fence a )) ∨ rel | s = rel ∩ ( s × s ) (* 29.8:5 states that consume fences are acquire fences. *) hypothetical release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order = (( mem ord = Mo consume ) ∧ is fence a ) consistent reads from mapping = consistent reads from mapping = � None → is lock a ) hypothetical release sequence actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order a b } ( ∀ b . ( is read b ∧ is at non atomic location b ) = ⇒ strict preorder ord = irreflexive ord ∧ trans ord visible-side-e ff ect ( if ( ∃ a vse . a vse − − − − − − − − − → b ) visible-side-e ff ect rf then ( ∃ a vse . a vse − − − − − − − − − → b ∧ a vse − → b ) synchronizes-with is consume a = synchronizes with = a − − − − − − − − − − → b = rf total over s ord = else ¬ ( ∃ a . a − → b ))) ∧ is read a ∧ ( memory order a = Some Mo consume ) (* – additional synchronization, from thread create etc. – *) relation over s ord ∧ additional-synchronized-with a − − − − − − − − − − − − − − − − → b ∨ ord ord ( ∀ b . ( is read b ∧ is at atomic location b ) = ⇒ ( ∀ x ∈ s . ∀ y ∈ s . x − − → y ∨ y − − → x ∨ ( x = y )) ( if ( ∃ ( b � , vsse ) ∈ visible-sequences-of-side-e ff ects . ( b � = b )) is release a = ( same location a b ∧ a ∈ actions ∧ b ∈ actions ∧ ( then ( ∃ ( b � , vsse ) ∈ visible-sequences-of-side-e ff ects . ( case memory order a of (* – mutex synchronization – *) ( b � = b ) ∧ ( ∃ c ∈ vsse . c rf − → b )) strict total order over s ord = Some mem ord → sc ( is unlock a ∧ is lock b ∧ a − → b ) ∨ rf strict preorder ord ∧ total over s ord else ¬ ( ∃ a . a − → b ))) ∧ mem ord ∈ { Mo release , Mo acq rel , Mo seq cst } ∧ ( is write a ∨ is fence a ) (* – release/acquire synchronization – *) � None → is unlock a ) rf ( is release a ∧ is acquire b ∧ ¬ same thread a b ∧ ( ∀ ( x , a ) ∈ − → . | ord x − − → pred y = release-sequence rf rf ( ∃ c . a − − − − − − − − − → c − → b )) ∨ ∀ ( y , b ) ∈ − → . ord ord ord pred x ∧ x − − → y ∧ ¬ ( ∃ z . pred z ∧ x − − → z − − → y ) happens-before − − − − − − − − → b ∧ a is seq cst a = ( memory order a = Some Mo seq cst ) (* – fence synchronization – *) same location a b ∧ is at atomic location b ( is fence a ∧ is release a ∧ is fence b ∧ is acquire b ∧ modification-order = ⇒ ( x = y ) ∨ x − − − − − − − − − − → y ) ∧ | ord x − − → y = ( ∃ x . ∃ y . same location x y ∧ (* new CoWR *) location kind = ord ord ord is atomic action x ∧ is atomic action y ∧ is write x ∧ x − − → y ∧ ¬ ( ∃ z . x − − → z − − → y ) happens-before Mutex ( ∀ ( a , b ) ∈ − − − − − − − − → . sequenced-before sequenced-before a − − − − − − − − − → x ∧ y − − − − − − − − − → b ∧ | Non atomic ∀ c . hypothetical-release-sequence rf ( ∃ z . x − − − − − − − − − − − − − − − − → z − → y ))) ∨ rf | Atomic → b ∧ − c well founded r = wf r is write a ∧ same location a b ∧ is at atomic location b ( is fence a ∧ is release a ∧ modification-order = ⇒ ( c = a ) ∨ a − − − − − − − − − − → c ) ∧ is atomic action b ∧ is acquire b ∧ actions respect location kinds = actions respect location kinds = (* new CoRW *) type abbrev action id : string ( ∃ x . same location x b ∧ ∀ a . happens-before is atomic action x ∧ is write x ∧ ( ∀ ( a , b ) ∈ − − − − − − − − → . case location a of Some l → sequenced-before ∀ c . ( case location-kind l of a − − − − − − − − − → x ∧ rf type abbrev thread id : string c − → a ∧ Mutex → is lock or unlock a hypothetical-release-sequence rf ( ∃ z . x − − − − − − − − − − − − − − − − → z − → b ))) ∨ is write b ∧ same location a b ∧ is at atomic location a � Non atomic → is load or store a modification-order � Atomic → is load or store a ∨ is atomic action a ) ( is atomic action a ∧ is release a ∧ = ⇒ c − − − − − − − − − − → b ) ∧ type abbrev location : string � None → T is fence b ∧ is acquire b ∧ ( ∃ x . same location a x ∧ is atomic action x ∧ rf ( ∀ ( a , b ) ∈ − → . is atomic rmw b sequenced-before ⇒ a | modification-order x − − − − − − − − − → b ∧ = − − − − − − − − − − → b ) ∧ type abbrev val : string is at location kind = is at location kind = release-sequence rf ( ∃ z . a − − − − − − − − − → z − → x ))))) case location a of rf ( ∀ ( a , b ) ∈ − → . is seq cst b Some l → ( location-kind l = lk0 ) | sc modification-order � None → F = ⇒ ( ¬ is seq cst a ∧ ( ∀ x . x − → λ c . is write c ∧ same location b c b = ⇒ x − − − − − − − − − − → a )) ∨ memory order enum = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence = a | sc − → λ c . is write c ∧ same location b c b ) ∧ Mo seq cst | Mo relaxed synchronizes with actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence a b } | Mo release is at mutex location a = (* -Fence restrictions- *) | Mo acquire is at location kind a Mutex (* 29.3:3 *) | Mo consume carries-a-dependency-to | Mo acq rel carries a dependency to = a − − − − − − − − − − − − − → b = sequenced-before ( ∀ a . ∀ ( x , b ) ∈ − − − − − − − − − → . ∀ y . rf sequenced-before data-dependency → ) + b is at non atomic location a = a (( − → ∩ − − − − − − − − − → ) ∪ − − − − − − − − − ( is fence x ∧ is seq cst x ∧ is atomic action b ∧ is at location kind a Non atomic is write a ∧ same location a b ∧ action = a | sc rf − → x ∧ y − → b ) Lock of action id thread id location carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf = modification-order = ⇒ ( y = a ) ∨ a − − − − − − − − − − → y ) ∧ | Unlock of action id thread id location is at atomic location a = | Atomic load of action id thread id memory order enum location val carries a dependency to actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf a b } is at location kind a Atomic (* 29.3:4 *) | Atomic store of action id thread id memory order enum location val sequenced-before rf ( ∀ ( a , x ) ∈ − − − − − − − − − → . ∀ ( y , b ) ∈ → . − | Atomic rmw of action id thread id memory order enum location val val | Load of action id thread id location val ( is atomic action a ∧ is fence x ∧ is seq cst x ∧ dependency-ordered-before same thread a b = ( thread id of a = thread id of b ) dependency ordered before = a − − − − − − − − − − − − − − − → d = is write a ∧ same location a b ∧ | Store of action id thread id location val a ∈ actions ∧ d ∈ actions ∧ sc | Fence of action id thread id memory order enum x − → b ∧ is atomic action b ) ( ∃ b . is release a ∧ is consume b ∧ modification-order release-sequence rf = ⇒ ( y = a ) ∨ a − − − − − − − − − − → y ) ∧ threadwise relation over s rel = ( ∃ e . a − − − − − − − − − → e − → b ) ∧ relation over s rel ∧ ( ∀ ( a , b ) ∈ rel . same thread a b ) carries-a-dependency-to ( b − − − − − − − − − − − − − → d ∨ ( b = d ))) ( action id of ( Lock aid ) = aid ) ∧ (* 29.3:5 *) ( action id of ( Unlock aid ) = aid ) ∧ sequenced-before sequenced-before ( ∀ ( a , x ) ∈ − − − − − − − − − → . ∀ ( y , b ) ∈ − − − − − − − − − → . ∀ z . ( action id of ( Atomic load aid ) = aid ) ∧ ( is atomic action a ∧ is fence x ∧ is seq cst x ∧ same location a b = ( location a = location b ) dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to = ( action id of ( Atomic store aid ) = aid ) ∧ is write a ∧ is fence y ∧ is seq cst y ∧ ( action id of ( Atomic rmw aid ) = aid ) ∧ is atomic action b ∧ same location a b ∧ ( action id of ( Load aid ) = aid ) ∧ dependency ordered before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to a b } sc rf x − → y ∧ z → b ) − ( action id of ( Store aid ) = aid ) ∧ locations of actions = { l . ∃ a . ( location a = Some l ) } modification-order = ⇒ ( z = a ) ∨ a − − − − − − − − − − → z ) ( action id of ( Fence aid ) = aid ) simple happens before simple happens before = − − − − − − − − − − − − − → = well formed action a = sequenced-before synchronizes-with ( − − − − − − − − − → ∪ − − − − − − − − − − → ) + all data dependency all data dependency = − − − − − − − − − − − − → = ( thread id of ( Lock tid ) = tid ) ∧ case a of → mem ord ∈ rf carries-a-dependency-to ( thread id of ( Unlock tid ) = tid ) ∧ Atomic load mem ord ( − → ∪ − − − − − − − − − − − − − → ) + { Mo relaxed , Mo acquire , Mo seq cst , Mo consume } ( thread id of ( Atomic load tid ) = tid ) ∧ consistent simple happens before shb = ( thread id of ( Atomic store ) = tid ) ∧ � Atomic store mem ord → mem ord ∈ tid shb irreflexive ( − − → ) ( thread id of ( Atomic rmw tid ) = tid ) ∧ { Mo relaxed , Mo release , Mo seq cst } consistent control dependency = consistent control dependency = ( thread id of ( Load tid ) = tid ) ∧ � Atomic rmw mem ord → mem ord ∈ control-dependency all data dependency irreflexive (( − − − − − − − − − − − → ∪ − − − − − − − − − − − − → ) + ) { Mo relaxed , Mo release , Mo acquire , Mo acq rel , Mo seq cst , Mo consume } ( thread id of ( Store tid ) = tid ) ∧ inter-thread-happens-before � → T inter thread happens before = − − − − − − − − − − − − − − − → = ( thread id of ( Fence tid ) = tid ) synchronizes-with let r = − − − − − − − − − − → ∪ consistent execution actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc = dependency-ordered-before − − − − − − − − − − − − − − − → ∪ well formed threads actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ∧ well formed threads = well formed threads = ( memory order ( Atomic load mem ord ) = synchronizes-with sequenced-before ( − − − − − − − − − − → ◦ − − − − − − − − − → ) in consistent locks actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency sc ∧ ( inj on action id of ( actions ) ∧ Some mem ord ) ∧ r sequenced-before r let release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in ( memory order ( Atomic store ) = ( ∀ a . well formed action a ) ∧ ( → ∪ ( − − − − − − − − − − → ◦ − → )) + mem ord let hypothetical-release-sequence = hypothetical release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in Some mem ord ) ∧ threadwise relation over actions sequenced-before ∧ We can reason about C concurrency! let synchronizes-with = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence in ( memory order ( Atomic rmw mem ord ) = threadwise relation over actions data-dependency ∧ let carries-a-dependency-to = carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf in threadwise relation over actions control-dependency ∧ Some mem ord ) ∧ consistent inter thread happens before = consistent inter thread happens before = let dependency-ordered-before = dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to in strict preorder sequenced-before ∧ ( memory order ( Fence mem ord ) = inter-thread-happens-before irreflexive ( − − − − − − − − − − − − − − − → ) let inter-thread-happens-before = inter thread happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency synchronizes-with dependency-ordered-before in Some mem ord ) ∧ strict preorder data-dependency ∧ let happens-before = happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency inter-thread-happens-before in ( memory order = strict preorder control-dependency ∧ let visible-side-e ff ect = visible side e ff ect set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before in None ) relation over actions additional-synchronized-with ∧ happens-before happens before = − − − − − − − − → = let visible-sequences-of-side-e ff ects = visible sequences of side e ff ects set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order happens-before visible-side-e ff ect in ( ∀ a . thread id of a ∈ threads ) ∧ sequenced-before inter-thread-happens-before consistent inter thread happens before inter-thread-happens-before ∧ actions respect location kinds ∧ − − − − − − − − − → ∪ − − − − − − − − − − − − − − − → consistent sc order actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order sc happens-before ∧ data-dependency ⊆ sequenced-before ( location ( Lock l ) = Some l ) ∧ consistent modification order actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency sc modification-order happens-before ∧ ( location ( Unlock l ) = Some l ) ∧ well formed reads from mapping actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf ∧ all sc actions = all sc actions = ( location ( Atomic load l ) = Some l ) ∧ consistent reads from mapping actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf sc modification-order happens-before visible-side-e ff ect visible-sequences-of-side-e ff ects ) well formed reads from mapping = well formed reads from mapping = { a . ( is seq cst a ∨ is lock a ∨ is unlock a ) } ( location ( Atomic store l ) = Some l ) ∧ ( location ( Atomic rmw ) = Some l ) ∧ rf l relation over actions ( − → ) ∧ ( location ( Load l ) = Some l ) ∧ rf rf ( ∀ a . ∀ a � . ∀ b . a − → b ∧ a � − → b = ⇒ ( a = a � )) ∧ indeterminate reads actions threads = indeterminate reads = ( location ( Store l ) = Some l ) ∧ consistent sc order = consistent sc order = rf rf ( ∀ ( a , b ) ∈ → . − { b . is read b ∧ ¬ ( ∃ a . a − → b ) } ( location ( Fence ) = None ) happens-before let sc happens before = − − − − − − − − → | all sc actions in same location a b ∧ modification-order ( value read b = value written a ) ∧ let sc mod order = − − − − − − − − − − → | all sc actions in sc ( a ̸ = b ) ∧ strict total order over all sc actions ( − → ) ∧ unsequenced races = unsequenced races = { ( a , b ) . ( value read ( Atomic load v ) = Some v ) ∧ ( is at mutex location a = ⇒ sc happens before sc − − − − − − − − − − − → ⊆ − → ∧ ( a ̸ = b ) ∧ same location a b ∧ ( is write a ∨ is write b ) ∧ ( value read ( Atomic rmw v ) = Some v ) ∧ is unlock a ∧ is lock b ) ∧ sc mod order sc same thread a b ∧ ( value read ( Load v ) = Some v ) ∧ − − − − − − − − → ⊆ − → ( is at non atomic location a = ⇒ ( value read = None ) sequenced-before sequenced-before ¬ ( a − − − − − − − − − → b ∨ b − − − − − − − − − → a ) } is store a ∧ is load b ) ∧ ( is at atomic location a = ⇒ consistent modification order = consistent modification order = ( is atomic store a ∨ is atomic rmw a ∨ is store a ) ( value written ( Atomic store v ) = Some v ) ∧ modification-order data races = data races = { ( a , b ) . ∧ ( is atomic load b ∨ is atomic rmw b ∨ is load b ))) ( ∀ a . ∀ b . a − − − − − − − − − − → b = ⇒ same location a b ) ∧ ( value written ( Atomic rmw v ) = Some v ) ∧ ( a ̸ = b ) ∧ same location a b ∧ ( is write a ∨ is write b ) ∧ ( ∀ l ∈ locations of actions . case location-kind l of ( value written ( Store v ) = Some v ) ∧ ¬ same thread a b ∧ Atomic → ( ( value written = None ) let actions at l = { a . ( location a = Some l ) } in ¬ ( is atomic action a ∧ is atomic action b ) ∧ all lock or unlock actions at lopt as = happens-before happens-before let writes at l = { a at l . ( is store a ∨ { a ∈ as . is lock or unlock a ∧ ( location a = lopt ) } ¬ ( a − − − − − − − − → b ∨ b − − − − − − − − → a ) } is atomic store a ∨ is atomic rmw a ) } in is lock a = strict total order over writes at l case a of Lock → T � → F modification-order data races � actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc = consistent locks = consistent locks = ( − − − − − − − − − − → | actions at l ) ∧ (* happens-before at the writes of l is a subset of mo for l *) ∀ l ∈ locations of actions . ( location-kind l = Mutex ) = ⇒ ( let release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in let lock unlock actions = happens-before modification-order let hypothetical-release-sequence = release sequence set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency modification-order in − − − − − − − − → | writes at l ⊆ − − − − − − − − − − → ∧ is unlock a = all lock or unlock actions at ( Some l ) actions in let synchronizes-with = synchronizes with set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc release-sequence hypothetical-release-sequence in (* Mo seq cst fences impose modification order *) case a of Unlock → T � → F sc let lock order = − → | lock unlock actions in let carries-a-dependency-to = carries a dependency to set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf in sequenced-before sc sequenced-before ( − − − − − − − − − → ◦ ( − → | is fence ) ◦ − − − − − − − − − → | writes at l ) (* 30.4.1:5 - The implementation shall serialize those (lock and unlock) operations. *) let dependency-ordered-before = dependency ordered before set actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order release-sequence carries-a-dependency-to in modification-order ⊆ − − − − − − − − − − → ) strict total order over lock unlock actions lock order ∧ let inter-thread-happens-before = inter thread happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency synchronizes-with dependency-ordered-before in is atomic load a = � → ( let happens-before = happens before actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency inter-thread-happens-before in case a of Atomic load → T � → F let actions at l = { a . ( location a = Some l ) } in data races actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency happens-before (* 30.4.1:1 A thread owns a mutex from the time it successfully calls one of the lock functions until modification-order it calls unlock.*) ( − − − − − − − − − − → | actions at l ) = {} )) (* 30.4.1:20 Requires: The calling thread shall own the mutex. *) is atomic store a = (* 30.4.1:21 E ff ects: Releases the calling threads ownership of the mutex.*) cpp memory model opsem ( p ∈ � program ) = ( ∀ a u ∈ lock unlock actions . is unlock a u = ⇒ case a of Atomic store → T � → F visible-side-e ff ect let executions = { ( actions , threads , location-kind , sequenced-before , additional-synchronized-with , data-dependency , control-dependency , rf , modification-order , sc ) . visible side e ff ect = a − − − − − − − − − → b = ( ∃ a l ∈ lock unlock actions . happens-before opsem p actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ∧ consistent execution actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc } in − − − − − − − − → b ∧ a | lock order a l − − − − − − → a u ∧ same thread a l a u ∧ is lock a l )) ∧ if ∃ ( actions , threads , location-kind , sequenced-before , additional-synchronized-with , data-dependency , control-dependency , rf , modification-order , sc ) ∈ executions . is write a ∧ is read b ∧ same location a b ∧ is atomic rmw a = ( indeterminate reads actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf ̸ = {} ) ∨ ¬ ( ∃ c . ( c ̸ = a ) ∧ ( c ̸ = b ) ∧ ( unsequenced races actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency ̸ = {} ) ∨ case a of Atomic rmw → T � → F (* 30.4.1:7 E ff ects: Blocks the calling thread until ownership of the mutex can be obtained for the is write c ∧ same location c b ∧ ( data races � actions threads location-kind sequenced-before additional-synchronized-with data-dependency control-dependency rf modification-order sc ̸ = {} ) calling thread.*) happens-before happens-before a − − − − − − − − → c − − − − − − − − → b ) (* 30.4.1:8 Postcondition: The calling thread owns the mutex. *) then {} ( ∀ a l ∈ lock unlock actions . is lock a l = ⇒ else executions is load a = case a of Load → T � → F ( ∀ a u ∈ lock unlock actions . | lock order − − − − − − → a l = ⇒ is unlock a u ))) a u Monday 11 May 15 24
Shared memory int a = 1; int b = 0; Thread 1 Thread 2 int s; b = 42; for (s=0; s!=4; s++) { printf("%d\n", b); if (a==1) return NULL; for (b=0; b>=26; ++b) ; } Thread 2 is not affected by Thread 1 and vice-versa This program is data-race free This program must print 42 Monday 11 May 15 25
Shared memory int a = 1; int b = 0; This is a concurrency compiler bug compiler bug Thread 1 Thread 2 int s; b = 42; for (s=0; s!=4; s++) { printf("%d\n", b); if (a==1) return NULL; for (b=0; b>=26; ++b) ; } Thread 2 is not affected by Thread 1 and vice-versa This program is data-race free This program must print 42 Monday 11 May 15 25
Shared memory int a = 1; int b = 0; This is a concurrency compiler bug concurrency compiler bug Thread 1 Thread 2 int s; b = 42; for (s=0; s!=4; s++) { printf("%d\n", b); if (a==1) return NULL; for (b=0; b>=26; ++b) ; } Thread 2 is not affected by Thread 1 and vice-versa This program is data-race free This program must print 42 Monday 11 May 15 25
Compiler testing: state of the art Yang, Chen, Eide, Regehr - PLDI 2011 Monday 11 May 15 26
Compiler testing: state of the art Yang, Chen, Eide, Regehr - PLDI 2011 Reported hundreds of bugs on various versions of gcc, clang and other compilers Monday 11 May 15 26
Compiler testing: state of the art Yang, Chen, Eide, Regehr - PLDI 2011 Reported hundreds of bugs on various versions of gcc, clang and other compilers Cannot catch concurrency compiler bugs Monday 11 May 15 26
Hunting concurrency compiler bugs? How to deal with non-determinism? How to generate non-racy interesting programs? How to capture all the behaviours of concurrent programs? A compiler can optimise away behaviours: how to test for correctness? limit case : two compilers generate correct code with disjoint final states Monday 11 May 15 27
Idea C/C++ compilers support separate compilation Functions can be called in arbitrary non-racy concurrent contexts C/C++ compilers can only apply transformations sound with respect to an arbitrary non-racy concurrent context Hunt concurrency compiler bugs = search for transformations of sequential code not sound in an arbitrary non-racy context Monday 11 May 15 28
SEQUENTIAL PROGRAM optimising compiler under test reference semantics EXECUTABLE tracing REFERENCE MEMORY MEMORY TRACE TRACE Check : only transformations sound in any concurrent non-racy context Monday 11 May 15 29
Soundness of compiler optimisations in the C11/C++11 memory model Monday 11 May 15 30
What is an optimisation? Compiler Writer Semanticist Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Fancy algorithms Source code or IR Operations on AST Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Fancy algorithms Source code or IR Operations on AST for (int i=0; i<2; i++) { z = i; y+1 x[i] += ; } Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Fancy algorithms Source code or IR Operations on AST y+1 tmp = ; for (int i=0; i<2; i++) { z = i; tmp x[i] += ; } Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Elimination of run-time events Fancy algorithms Reordering of run-time events Source code or IR Introduction of run-time events Operations on AST Operations on sets of events y+1 tmp = ; for (int i=0; i<2; i++) { z = i; tmp x[i] += ; } Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Elimination of run-time events Fancy algorithms Reordering of run-time events Source code or IR Introduction of run-time events Operations on AST Operations on sets of events ...assuming initially y=42 ... Store z 0 y+1 tmp = ; Load y 42 for (int i=0; i<2; i++) { Store x[0] 43 z = i; Store z 1 tmp x[i] += ; Load y 42 } Store x[1] 43 Monday 11 May 15 31
What is an optimisation? Compiler Writer Semanticist Sophisticated program analyses Elimination of run-time events Fancy algorithms Reordering of run-time events Source code or IR Introduction of run-time events Operations on AST Operations on sets of events ...assuming initially y=42 ... Load y 42 Store z 0 y+1 tmp = ; for (int i=0; i<2; i++) { Store x[0] 43 z = i; Store z 1 tmp x[i] += ; Load y 42 } Store x[1] 43 Monday 11 May 15 31
Elimination of overwritten writes Store g 1 Under which conditions is it sb correct to eliminate the first store? ... sb Store g 2 Monday 11 May 15 32
A same-thread release-acquire pair is a pair of a release action followed by an acquire action in program order. An action is a release if it is a possible source of a synchronisation unlock mutex, release or seq_cst atomic write An action is an acquire if it is a possible target of a synchronisation lock mutex, acquire or seq_cst atomic read Monday 11 May 15 33
Elimination of overwritten writes Store g 1 It is safe to eliminate the first store if there are: sb no access to g 1. no intervening accesses to g no st rel/acq pair 2. no intervening same-thread release-acquire pair sb Store g 2 Monday 11 May 15 34
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 g = 1; f1.store(1,RELEASE); while(f2.load(ACQUIRE)==0); g = 2; Monday 11 May 15 35
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 candidate overwritten write g = 1; f1.store(1,RELEASE); while(f2.load(ACQUIRE)==0); g = 2; Monday 11 May 15 35
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 candidate overwritten write g = 1; f1.store(1,RELEASE); same-thread release-acquire pair while(f2.load(ACQUIRE)==0); g = 2; Monday 11 May 15 35
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); while(f2.load(ACQUIRE)==0); f2.store(1,RELEASE); g = 2; Monday 11 May 15 36
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); while(f2.load(ACQUIRE)==0); f2.store(1,RELEASE); s y n c g = 2; Thread 2 is non-racy Monday 11 May 15 36
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); while(f2.load(ACQUIRE)==0); f2.store(1,RELEASE); s y n c g = 2; Thread 2 is non-racy The program should only print 1 Monday 11 May 15 36
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); while(f2.load(ACQUIRE)==0); f2.store(1,RELEASE); s y n c g = 2; Thread 2 is non-racy The program should only print 1 If we perform overwritten write elimination it prints 0 Monday 11 May 15 36
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); while(f2.load(ACQUIRE)==0); f2.store(1,RELEASE); g = 2; Monday 11 May 15 37
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); f2.store(1,RELEASE); g = 2; Monday 11 May 15 37
The soundness condition Shared memory g = 0; atomic f1 = f2 = 0; Thread 1 Thread 2 g = 1; sync while(f1.load(ACQUIRE)==0); f1.store(1,RELEASE); printf(“%d”, g); f2.store(1,RELEASE); data race g = 2; If only a release (or acquire) is present, then all discriminating contexts are racy . It is sound to optimise the overwritten write. Monday 11 May 15 37
Eliminations: bestiary Store g v 1 Read g v Store g v Read g v Store g v 1 sb sb sb sb sb no access to g no access to g no access to g no access to g no access to g no rel/acq pair no rel/acq pair no rel/acq pair no rel/acq pair no rel/acq pair sb sb sb sb sb Write-after-Read Store g v 2 Store g v 1 Read g v Read g v Store g v Overwritten-Write Write-after-Write Read-after-Read Read-after-Write Write-after-Read Reads which are not used (via data or control dependencies) to decide a write or synchronisation event are also eliminable ( irrelevant reads ). Monday 11 May 15 38
Also correctness statements for Eliminations: bestiary reorderings , merging , and introductions of events. Store g v 1 Read g v Store g v Read g v Store g v 1 sb sb sb sb sb no access to g no access to g no access to g no access to g no access to g no rel/acq pair no rel/acq pair no rel/acq pair no rel/acq pair no rel/acq pair sb sb sb sb sb Write-after-Read Store g v 2 Store g v 1 Read g v Read g v Store g v Overwritten-Write Write-after-Write Read-after-Read Read-after-Write Write-after-Read Reads which are not used (via data or control dependencies) to decide a write or synchronisation event are also eliminable ( irrelevant reads ). Monday 11 May 15 38
From theory to the Cmmtest tool Monday 11 May 15 39
SEQUENTIAL PROGRAM optimising compiler under test reference semantics EXECUTABLE tracing REFERENCE MEMORY MEMORY TRACE TRACE Check : only transformations sound in any concurrent non-racy context Monday 11 May 15 40
CSmith SEQUENTIAL extended with locks PROGRAM and atomics optimising compiler under test reference semantics EXECUTABLE tracing REFERENCE MEMORY MEMORY TRACE TRACE Check : only transformations sound in any concurrent non-racy context Monday 11 May 15 40
CSmith SEQUENTIAL extended with locks PROGRAM and atomics optimising compiler under test reference semantics EXECUTABLE binary tracing instrumentation REFERENCE MEMORY MEMORY TRACE TRACE Check : only transformations sound in any concurrent non-racy context Monday 11 May 15 40
CSmith SEQUENTIAL extended with locks PROGRAM and atomics optimising compiler under test gcc/clang -O0 EXECUTABLE EXECUTABLE binary binary instrumentation tracing instrumentation REFERENCE MEMORY MEMORY TRACE TRACE Check : only transformations sound in any concurrent non-racy context Monday 11 May 15 41
CSmith SEQUENTIAL extended with locks PROGRAM and atomics optimising compiler under test gcc/clang -O0 EXECUTABLE EXECUTABLE binary binary instrumentation tracing instrumentation REFERENCE MEMORY MEMORY TRACE TRACE OCaml tool Check : only transformations sound 1. analyse the traces to detect eliminable actions in any concurrent non-racy context 2. match reference and optimised traces Monday 11 May 15 41
Recommend
More recommend