103 lines
4.3 KiB
Markdown
103 lines
4.3 KiB
Markdown
Critical sections (locks) have drawbacks:
|
||
- if not put at the right level of granularity, they unnecessarily reduce concurrency
|
||
- delays of one process may affect the whole system
|
||
|
||
**MUTEX-freedom:** the only atomicity is the one provided by the primitives themselves (no wrapping of code into CSs)
|
||
the liveness properties used so far cannot be used anymore, since they rely on CSs.
|
||
(example: if we have only atomic R/W registers, these are the only atomic things that we have. But we may also have atomic primitives like *test&set*, *compare&swap* ecc.).
|
||
|
||
#### Liveness properties
|
||
We have four new liveness properties
|
||
1. **Obstruction freedom:** every time an operation is run in isolation (no overlap with any other operation on the same object), it terminates
|
||
2. **Non-blocking:** whenever an operation is invoked on an object, eventually one operation on that object terminates
|
||
- reminds deadlock-freedom in MUTEX-based concurrency
|
||
3. **Wait freedom:** whenever an operation is invoked on an object, it eventually terminates
|
||
- reminds starvation-freedom in MUTEX-based concurrency
|
||
4. **Bounded wait freedom:** wait freedom + a bound on the number of steps needed to terminate
|
||
- reminds bounded bypass in MUTEX-based concurrency
|
||
|
||
*REMARK:* these notions naturally cope with (crash) failures. Fail stop is another way of terminating, there is no way of distinguishing a failure from an arbitrary long sleep (because of asynchrony).
|
||
|
||
### A wait-free Splitter
|
||
Assume we have atomic R/W registers.
|
||
|
||
A **splitter** is a concurrent object that provides a single operation **dir** such that:
|
||
1. (*validity*) it returns L, R or S (left, right, stop)
|
||
2. (*concurrency*) in case of n simultaneous invocations of **dir**
|
||
1. at most n-1 L are returned
|
||
2. at most n-1 R are returned
|
||
3. at most 1 S is returned
|
||
3. (*wait freedom*) it eventually terminates.
|
||
|
||
Idea:
|
||
- not all processes obtain the same value
|
||
- in a solo execution (i.e., without concurrency) the invoking process must stop (0 L && 0 R && at most 1 S)
|
||
|
||
We have:
|
||
- DOOR: MRMW boolean atomic register initialized at 1
|
||
- LAST: MRMW atomic register initialized at whatever process index
|
||
|
||
```
|
||
dir(i) :=
|
||
LAST <- i
|
||
if DOOR = 0 then
|
||
return R
|
||
else
|
||
DOOR <- 0
|
||
if LAST = i then
|
||
return S
|
||
else
|
||
return L
|
||
```
|
||
With 2 processes, we can have:
|
||
- one goes left and one goes right
|
||
- one goes left and the other stops
|
||
- one goes right and the other stops
|
||
|
||
#### Soundness theorem
|
||
this implementation satisfies the three requirements for the splitter
|
||
|
||
*Proof:*
|
||
1. Not all processes can obtain R
|
||
- the door must have been closed and who closed the door cannot obtain R
|
||
2. not all processes can obtain L
|
||
- let us consider the last process that writes into LAST (this is an atomic register, so this is meaningful)
|
||
- if the door is closed, it receives R and √
|
||
3. let $p_i$ be the first process that receives $S \to LAST=i$ in its second if
|
||
![[Pasted image 20250324091452.png]]
|
||
|
||
### An Obstruction-free Timestamp Generator
|
||
A **timestamp generator** is a concurrent object that provides a single operation get_ts such that:
|
||
1. (*validity*) not two invocations of get_ts return the same value
|
||
2. (*consistency*) if one process terminates its invocation of get_ts before another one starts, the first receives a timestamp that is smaller than the one received by the second one
|
||
3. (*obstruction freedom*) if run in isolation, it eventually terminates
|
||
|
||
Idea: use something like a splitter for possible timestamp, so that only the process that receives S (if any) can get that timestamp.
|
||
|
||
We have:
|
||
- `DOOR[i]`: MRMW boolean atomic register initialized at 1, for all i
|
||
- `LAST[i]`: MRMW atomic register initialized at whatever process index, for all i
|
||
- NEXT: integer initialized at 1
|
||
```
|
||
get_ts(i) :=
|
||
k <- NEXT
|
||
while true do
|
||
LAST[k] <- i
|
||
if DOOR[k] = 1 then
|
||
DOOR[k] <- 0
|
||
if LAST[k] = i then
|
||
NEXT++
|
||
return k
|
||
k++
|
||
```
|
||
|
||
#### Soundness theorem (yes, again)
|
||
this implementation satisfies the three properties of the timestamp generator
|
||
1. Validity holds because of property 2.c of the splitter
|
||
2. For consistency, the invocation that terminates increased the value of NEXT before terminating
|
||
- every process that starts after its termination will find NEXT to a greater value (NEXT never decreases!)
|
||
3. Obstruction freedom is trivial
|
||
|
||
**REMARK:** this implementation doesn’t satisfy the non-blocking property:![[Pasted image 20250324092633.png]]
|
||
|
||
### A Wait-free Stack
|