Concurrent Programming Without Locks
description
Transcript of Concurrent Programming Without Locks
![Page 1: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/1.jpg)
Concurrent Programming Without Locks
Keir Fraser & Tim Harris
![Page 2: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/2.jpg)
Motivation
• Locking introduces dependencies among threads
• Non-blocking solutions keep threads independent, but– they are complicated to program– or depend on unrealistic instructions (CAS2)
• Need a practical and general non-blocking solution
![Page 3: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/3.jpg)
Solutions?
• Only use data structures that can be implemented with CAS?– Limiting
• Build MCAS in software using CAS• Build Transactional Memory in software
using CAS
![Page 4: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/4.jpg)
Goals
• Concreteness• Linearizability• Non-blocking progress guarantee• Disjoint access parallelism• Read parallelism• Dynamicity• Practicable space costs• Composability
![Page 5: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/5.jpg)
Definitions• Obstruction freedom – a thread will make
progress as long as it doesn’t contend with other threads access to any location
• Lock-freedom – The system as a whole will make progress
• Wait-freedom – Every thread makes progress
Focus is on Lock-free designWhole transactions are lock-free, not just the sub-
components
![Page 6: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/6.jpg)
The Basic Problem
• How can we conditionally update multiple locations atomically – using “real” instructions that can only update a single location atomically?
• The trick– Introduce a level of indirection– Use descriptors to access values indirectly
![Page 7: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/7.jpg)
How do we use indirection?
• Memory locations involved in a transaction or MCAS have– old and new uncommitted values stored in a
descriptor– a status field determines which value to use– we must be careful how status is updated!
• Memory locations not involved in a transaction can hold their value directly– requires tidying up after transactions commit
![Page 8: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/8.jpg)
100
101
102
103
104
105
106
107
789456106123123105200100102
New ValueOld ValueAddressStatus
Memory
Descriptor
Indirect Memory Access
![Page 9: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/9.jpg)
Direct or Indirect?
• How do we know if the value in a location should be used directly or indirectly?– we can reserve some low order bits– interpret them on each access– but this limits the use of the approach to
aligned pointers
![Page 10: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/10.jpg)
Using Descriptors in TM• Commit operation atomically updates status field
– we have to do it with CAS to avoid races• Once a descriptor is made visible, only the
status field changes– Why?– How?
• Once a transaction’s outcome is decided, the status value doesn’t change– Retries use a new descriptor … why?
• Descriptors are managed via garbage collection
![Page 11: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/11.jpg)
Other requirements
• Descriptors must be able to own locations– one transaction must not unlink another– why?– so what should be done on a conflict, wait?
• But doesn’t this introduce blocking?– not necessarily – contending threads could
help the owner complete
![Page 12: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/12.jpg)
Uncontended Commits
• To be obstruction free, uncontended commits must succeed
• The phases:– Prepare the transaction descriptor (use CCAS
for each location accessed) to atomically link locations while outcome is undecided
– Decide the transaction’s outcome and update the status field (using CAS)
– Update memory (using CAS) and mark the descriptor for collection
![Page 13: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/13.jpg)
Contended Commits
• Contended Commits must make progress– If status is decided, but not complete
• Help the other thread complete
– If status is undecided, either• Abort contending transactions
– needs contention management to prevent live-lock• Help contending transactions
– need some way to ensure success of at least one transaction
– Read-check, used in WSTM or OSTM to ensure read set is still current:
• Abort at least one contender• Help, and ensure progress by ordering transactions
![Page 14: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/14.jpg)
Three STM Implementations
MCAS Multiple Compare And Swap
WSTM Word Software Transactional Memory
OSTM Object Software Transactional Memory
![Page 15: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/15.jpg)
MCASCAS( word *address, // actual value
word expected_value,word new_value);
MCAS( int count,word *address[], // actual valuesword expected_value[],word new_value[]);
… but an extra indirection is added because pointers must indirect through the descriptor
![Page 16: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/16.jpg)
MCAS
• Operates only on aligned pointers– enables use of 2 low order bits to distinguish
values from descriptors• Descriptors contain
– status {Success, Failure, Undecided}– N– address[ ]– expected[ ]– new_value[ ]
![Page 17: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/17.jpg)
Data Access Examples
200100101
New Value
Old ValueAddressStatus: SUCCESS
descriptor
value
descriptor
300
200100107
New Value
Old ValueAddressStatus: UNDECIDED
![Page 18: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/18.jpg)
The Prepare Phase
• Create MCAS descriptor• Insert descriptor address in each location
– don’t overwrite other concurrent attempts– don’t keep working if another thread has already
helped you succeed or fail• use CAS conditional on undecided status (CCAS)
– MCAS descriptor must not become visible until its fully initialized
• link CCAS descriptors in each location first then swap for MCAS descriptor using CCAS
![Page 19: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/19.jpg)
CCASConditional CAS built from CAS - takes effect only if condition == undecided - used to insert descriptor references in two phases
CCAS( word *address,word expected_value,word new_value,word *condition);
return original value of *address
![Page 20: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/20.jpg)
CCAS
• Create a new private CCAS descriptor• Copy CCAS parameter values into it• Try to link it into the target location (using
CAS)• On failure try to help whoever succeeded
by using their CCAS descriptor– again using CAS– then retry your own
![Page 21: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/21.jpg)
word *CCAS(word **a, word *e, word *n, word *cond) {
ccas_descriptor *d = new ccas_descriptor();word *v;(d->a, d->e, d->n, d->cond) = (a,e,n,cond);while ( (v = CAS(d->a, d->e, d)) != d->e ) {
if ( IsCCASDesc(v) ) CCASHelp( (ccas_descriptor *)v);
elsereturn v;
}CCASHelp(d);return v;
}void CCASHelp(ccas_descriptor *d) {
bool success = (*d->cond == UNDECIDED);CAS(d->a, d, success ? d->n : d->e);
}
![Page 22: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/22.jpg)
Cost in terms of CAS
• CCAS takes at least 2 CAS to link the MCAS descriptor into each location– 2N CAS for N locatons
• But we still have not committed the MCAS– at least 1 CAS required to set MCAS status – at least N CAS required to update the memory
locations with the new values from the MCAS descriptor
![Page 23: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/23.jpg)
Reading
• We can’t simply read values anymore!• CCASRead must be used for reading• It must be able to read values directly and
indirectly through CCAS descriptors– detect which situation it is in– function correctly in the presence of
concurrent updates
![Page 24: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/24.jpg)
CCASRead
• Copy address to be read to local• Test to see if it’s a value or a descriptor• If it’s a descriptor help the thread whose
descriptor it is complete – requires more CAS
![Page 25: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/25.jpg)
word *CCASRead(word **a) {word *v = *a;while ( IsCCASDesc(v) ) {
CCASHelp( (ccas_descriptor *)v);v = *a;
}return v;
}
void CCASHelp(ccas_descriptor *d) {bool success = (*d->cond == UNDECIDED);CAS(d->a, d, success ? d->n : d->e);
}
![Page 26: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/26.jpg)
Reading
• We also need an MCASRead to read locations subject to MCAS
• MCASRead used CCASRead to read the contents of the location– if its an MCAS descriptor it must find the
address in the descriptor and determine whether to use the old or new values
– this requires more CCAS
![Page 27: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/27.jpg)
Putting it all together
• Example
MCAS (3, {a,b,c}, {1,2,3}, {4,5,6})
![Page 28: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/28.jpg)
MCAS(3, {a,b,c}, {1,2,3}, {4,5,6})
1
2
3
a
b
c
![Page 29: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/29.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
1
2
3 63c52b41a
3UNDECIDEDa
b
c
![Page 30: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/30.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
1
2
3 63c52b41a
3UNDECIDEDa
b
c
CCAS Descr
a
1
&MCAS_Descr
&mcas->status
![Page 31: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/31.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
2
3 63c52b41a
3UNDECIDEDa
b
c
CCAS Descr
a
1
&MCAS_Descr
&mcas->status
![Page 32: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/32.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
2
3 63c52b41a
3UNDECIDEDa
b
c
CCAS Descr
a
1
&MCAS_Descr
&mcas->status
![Page 33: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/33.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
63c52b41a
3UNDECIDEDa
b
c
![Page 34: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/34.jpg)
MCAS(3, {a,c,b}, {1,3,2}, {4,6,5})
63c52b41a
3SUCCESSa
b
c
![Page 35: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/35.jpg)
1
2
3
MCAS(3, {a,b,c}, {1,2,3}, {4,5,6})
1
2
3 63c52b41a
3SUCCESS4
5
6
a
b
c
![Page 36: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/36.jpg)
1
2
3
MCAS(3, {a,b,c}, {1,2,3}, {4,5,6})
1
2
3
4
5
6
a
b
c
![Page 37: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/37.jpg)
bool MCAS(int N, word **a[], word *e[], word *n[])
{mcas_descriptor *d =
new mcas_descriptor();d->N = N; d->status = UNDECIDED;for (int i=0; i<N; i++) {
d->a[i] = a[i]; d->e[i] = e[i]; d->n[i] = n[i];
}address_sort(d);return mcas_help(d);
}
![Page 38: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/38.jpg)
bool mcas_help(mcas_descriptor *d){
word *v, desired = FAILED;bool success;
// Phase 1: acquirefor (int i=0; i<d->N; i++) {while (TRUE){v = CCAS(d->a[i], d->e[i], d,
&d->status);if (v = d->e[i] || v == d) break;if (IsMCASDesc(v) ) mcas_help( (mcas_descriptor *)v );elsegoto decision_point;}}desired = SUCCESS;
decision_point:
![Page 39: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/39.jpg)
mcas_help continued
// PHASE 2: read – not used by MCAS
decision_point:CAS(&d->status, UNDECIDED, desired);
// PHASE 3: clean upsuccess = (d->status == SUCCESS);for (int i=0; i<d->N; i++) {
CAS(d->a[i], d, success ? d->n[i] : d->e[i]);}
return success;}
![Page 40: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/40.jpg)
Word *MCASRead(word **addr){
word *v;retry_read:
v = CCASRead(addr);if ( !IsMCASDesc(v)) return v;
for (int i=0; i<v->N; i++) {if (v->addr[i] == addr) {
if (v->status == SUCCESS)if (CCASRead(addr) == v)
return v->new[i]else
goto retry_read;else // FAILED or UNDECIDED
if (CCASRead(addr) == v)return v->expected[i];
elsegoto retry_read;
}}return v;
}
![Page 41: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/41.jpg)
Conflicts
200100102789456104777999108
New Value
Old ValueAddressStatus: UNDECIDED
102
104
108
200999108
New Value
Old ValueAddressStatus: UNDECIDED
![Page 42: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/42.jpg)
bool mcas_help(mcas_descriptor *d){
word *v, desired = FAILED;bool success;
// Phase 1: acquirefor (int i=0; i<d->N; i++) {
while (TRUE){v = CCAS(d->a[i], d->e[i], d, &d->status);if (v = d->e[i] || v == d) break;if (IsMCASDesc(v) )
mcas_help( (mcas_descriptor *)v );else
goto decision_point;}
}desired = SUCCESS;
decision_point:
![Page 43: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/43.jpg)
Conflicts
200100102789456104777999108
New Value
Old ValueAddressStatus: UNDECIDED
102
104
108
200999108
New Value
Old ValueAddressStatus: UNDECIDED
![Page 44: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/44.jpg)
Conflicts
200100102789456104777999108
New Value
Old ValueAddressStatus: UNDECIDED
102
104
108 200
![Page 45: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/45.jpg)
bool mcas_help(mcas_descriptor *d){
word *v, desired = FAILED;bool success;
// Phase 1: acquirefor (int i=0; i<d->N; i++) {
while (TRUE){v = CCAS(d->a[i], d->e[i], d, &d-
>status);if (v = d->e[i] || v == d) break;if (!IsMCASDesc(v) ) goto
decision_point;mcas_help( (mcas_descriptor *)v );
}}
desired = SUCCESS;decision_point:
![Page 46: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/46.jpg)
Conflicts
200100102789456104777200108
New Value
Old ValueAddressStatus: UNDECIDED
102
104
108
123456104200999108
New Value
Old ValueAddressStatus: UNDECIDED
![Page 47: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/47.jpg)
mcas_help continued
// PHASE 2: read – not used by MCAS
decision_point:CAS(&d->status, UNDECIDED, desired);
// PHASE 3: clean upsuccess = (d->status == SUCCESS);for (int i=0; i<d->N; i++) {
CAS(d->a[i], d, success ? d->n[i] : d->e[i]);
}
return success;}
![Page 48: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/48.jpg)
Conflicts
200100102789456104777200108
New Value
Old ValueAddressStatus: SUCCESS
102
104
108
123456104200999108
New Value
Old ValueAddressStatus: UNDECIDED
![Page 49: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/49.jpg)
mcas_help continued
// PHASE 2: read – not used by MCAS
decision_point:CAS(&d->status, UNDECIDED, desired);
// PHASE 3: clean upsuccess = (d->status == SUCCESS);for (int i=0; i<d->N; i++) {
CAS(d->a[i], d, success ? d->n[i] : d->e[i]);
}
return success;}
![Page 50: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/50.jpg)
Conflicts
200100102789456104777200108
New Value
Old ValueAddressStatus: SUCCESS
200
789
777
123456104200999108
New Value
Old ValueAddressStatus: UNDECIDED
![Page 51: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/51.jpg)
Failure Modes• Can fail during any of the CAS attempts
– CCAS– CCASHelp
![Page 52: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/52.jpg)
CCAS “failure modes”• Someone helped us with the CCAS
– call CCASHelp with our own descriptor– next time around, return MCAS descriptor– MCAS continues
• Someone else beat us to CCAS– help them with their CCAS– next time around, return their MCAS descriptor– Help with their MCAS– Our MCAS likely aborts
• Source value changed– return new value– MCAS aborts
![Page 53: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/53.jpg)
word *CCAS(word **a, word *e, word *n, word *cond) {
ccas_descriptor *d = new ccas_descriptor();word *v;(d->a, d->e, d->n, d->cond) = (a,e,n,cond);while ( (v = CAS(d->a, d->e, d)) != d->e ) {
if ( !IsCASDesc(v) ) return v;CCASHelp( (ccas_descriptor *)v);
}CCASHelp(d);return v;
}void CCASHelp(ccas_descriptor *d) {
bool success = (*d->cond == UNDECIDED);CAS(d->a, d, success ? d->n : d->e);
}
![Page 54: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/54.jpg)
CCASHelp “failure modes”• MCAS aborted so status isn’t UNDECIDED
– old value put back in place• MCAS aborted, CCASHelp doesn’t restore value
– MCAS cleanup will put old value back in place• Race: status switches to SUCCESS between
check and CAS– CAS will fail because CCAS descriptor already
removed– CCAS return will not cause MCAS failure
• Race: status switches to FAILURE between check and CAS– CAS will always fail because for MCAS to fail,
someone must have read beyond us
![Page 55: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/55.jpg)
Cost
• Minimum of 3N + 1 CAS instructions for N locations
– many more CAS under heavy contention !
• With no contention the three batches of N CAS all act on the same N locations
• “[improvements] may be useful if there are systems in which CAS operates substantially more slowly than an ordinary write.”
![Page 56: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/56.jpg)
Deep Breath
![Page 57: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/57.jpg)
WSTM
• Remove requirement for space reserved in values being updated– hash addr to find ownership record
• Caller need not keep track of locations– read and write sets stored in transaction
descriptor• Provides read parallelism• Obstruction free, not lock free nor wait free
![Page 58: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/58.jpg)
Data Structures
100
200
300
400
version 52
Status: Undecideda1: (100,15) -> (200,16)
a2: (200,52) -> (100,53)
Orecs
![Page 59: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/59.jpg)
Logical contents
• Orec contains a version number:– value comes direct from memory
• Orec contains a descriptor reference– descriptor contains address
• value comes from descriptor based on status– descriptor does not contain address
• value comes direct from memory
![Page 60: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/60.jpg)
Transaction Process
• Call WSTMRead/WSTMWrite to gather/change data– Builds transaction data structure, but it’s NOT
visible• WSTMCommitTransaction
– Get ownership – update ORecs– Read-Check – check version numbers– Decide– Clean up
![Page 61: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/61.jpg)
version 52
version 15
version 53
version 16
Data Structures
100
200
300
400
Status: UNKNOWNa1: (100,15) -> (200,16)
a2: (200,52) -> (200,52)a2: (200,52) -> (100,53)
200
100 Status: SUCCESS
![Page 62: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/62.jpg)
Complications
• Fixed number of Orecs• Hash collisions lead to false sharing
![Page 63: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/63.jpg)
Issues• Orec ownership acts like a lock, so simple
scheme is not even obstruction free• Can’t help with “cleanup” because might
overwrite newer data• Can’t determine value during READCHECK, so
we’re forced to shoot down• force_decision() might be circular causing live
lock• helping requires <complicated> stealing of
transactions
• Uncontended cost is N+2 CAS for N locations
![Page 64: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/64.jpg)
OSTM
• Objects are represented as opaque handles– can’t use pointers directly– must rewrite data structures
• Get accessible pointers via OSTMOpenForReading/OSTMOpenForWriting
• Eliminates need for Orecs/aliasing
![Page 65: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/65.jpg)
Evaluation
• “We use … reference-counting garbage collection”
• Evaluated with one thread/CPU• “Since we know the number of threads
participating in our experiments…”
![Page 66: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/66.jpg)
Uncontended Performance
![Page 67: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/67.jpg)
Contended Locks
![Page 68: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/68.jpg)
Data Contention
![Page 69: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/69.jpg)
Data/Lock Contention
![Page 70: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/70.jpg)
Spare Slides
![Page 71: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/71.jpg)
word WSTMRead(wstm_transaction *tx, word *addr) {if (entry_exists) return entry->new_value;
if (orec->type != descriptor) create entry [current value, orec version]
else {force_decision(descriptor); // can’t be ours: not in commitif (descriptor contains our address)
if (status == SUCCESS) create entry [descr.new_val, descr.new_ver]
else create entry [descr.old_val, descr.old_ver]
else create entry [current value, descr.aliased.new_ver]
}
if (aliased) {if (entry->old_version != aliased->old_version)
status = FAILED;
entry->old_version = aliased->old_version;entry->new_version = aliased->new_version;
}
return entry->new_value;}
![Page 72: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/72.jpg)
void WSTMWrite(wstm_transaction *tx, word *addr, word
new_value {
get entry using WSTMRead logic
entry->new_value = new_value;
for each aliased entry {entry->new_version++;
}}
![Page 73: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/73.jpg)
bool WSTMCommit(wstm_transaction *tx) {
if (tx->status == FAILED) return false;
sort descriptor entriesdesired_status = FAILED;
for each updateif (!acquire_orec) goto decision_point;
CAS(status, UNDECIDED, READ_CHECK);for each read
if (!read_check) goto decision_point;
desired_status = SUCCESS;
decision_point:
![Page 74: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/74.jpg)
decision_point:status = tx->status;while (status != FAILED && status != SUCCESS) {
CAS(tx->status, status, desired_status);status = tx->status;
}
if (tx->status == SUCCESS)for each update
*addr = entry->new_value;
for each updaterelease_orec
return (tx->status == SUCCESS);}
![Page 75: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/75.jpg)
bool read_check(wstm_transaction *tx, wstm_entry *entry)
{if (orec is WSTM_descriptor) {
force_decision()if (SUCCESS)
version = new_version;else
version = old_version} else {
version = orec_version;}
return (version == entry->old_version);}
![Page 76: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/76.jpg)
Data Structures
100
200
300
400
version 52
Status: Undecideda1: (100,15) -> (200,16)
a2: (200,52) -> (100,53)
a3: (300,15) -> (300,16)
Orecsa1
a2
a3
![Page 77: Concurrent Programming Without Locks](https://reader035.fdocuments.us/reader035/viewer/2022062316/5681675c550346895ddc24bb/html5/thumbnails/77.jpg)
Caveats
• “It remains possible for a thread to see a mutually inconsistent view of shared memory if it performs a series of [read] calls.”
• In other words there is not complete isolation between transactions– a thread may crash due to concurrency prior
to having its transaction abort and retry