Based on Chapter 4 of [Hai19]
(Usage hints for this presentation)
Computer Structures and Operating Systems 2023
Dr. Jens Lechtenbörger (License Information)
You have seen this advice before. It is repeated here for emphasis:
The above is covered in this introduction to transaction processing.
“Ferrari Kiss” by Antoine Valentini under CC BY-SA 2.0; from flickr
Assert(false)
),
you demonstrate a race conditionConsider the following piece of Java code (from Sec. 4.2 of [Hai19]) to sell tickets as long as seats are available. (That code is embedded in this sample program, which you can execute to see races yourself.)
if (seatsRemaining > 0) { dispenseTicket(); seatsRemaining = seatsRemaining - 1; } else displaySorrySoldOut();
Inspired by the Deadlock Empire, find and explain a race condition
involving the counter seatsRemaining
, which leads to single
tickets being sold several times (to be revisited in exercises).
i += 1
are not atomic
“Interleaved execution of threads with MX for code from Sec. 4.2 of book by Max Hailperin, CC BY-SA 3.0.” by Jens Lechtenbörger under CC BY-SA 4.0; from GitLab
synchronized
turns Java method into CS with MX!seatsRemaining
Mutexes
Figure © 2016 Julia Evans, all rights reserved; from julia's drawings. Displayed here with personal permission.
lock()
or acquire()
: Lock/acquire/take the object
lock()
’ed by one thread at a timelock()
need to wait for unlock()
unlock()
or release()
: Unlock/release the object
lock()
’ed again afterwards
“Figure 4.4 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
seatlock.lock(); if (seatsRemaining > 0) { dispenseTicket(); seatsRemaining = seatsRemaining - 1; } else displaySorrySoldOut(); seatlock.unlock();
acquire
(wait, sleep, down, P [passeren, proberen]): entry into CS
release
(signal, wakeup, up, V [vrijgeven, verlaten]): exit
from CS
acquire()
before CS, release()
afterseatSem
initialized to 1
(leading to MX behavior):
seatSem.acquire(); if (seatsRemaining > 0) { dispenseTicket(); seatsRemaining = seatsRemaining - 1; } else displaySorrySoldOut(); seatSem.release();
acquire()
on bouncer/semaphore to enterrelease()
on bouncer/semaphore to leaveSemaphoreBoundedBuffer
Play the following games at https://deadlockempire.github.io/
Enter()
and Exit()
represent lock()
and unlock()
if (queue.Count == 0)
is meant to avoid removal
attempts from empty queues. However, PulseAll()
wakes up all
waiting (blocked) threads (similarly to notifyAll()
for Java
later on)exchange
, which exchanges contents of register and
memory locationmutex
1
: unlocked0
: lockedunlock()
: Store 1
into mutex
lock()
: Atomically check for 1
and store 0
as follows
([Hai19]):to lock mutex: let temp = 0 repeat atomically exchange temp and mutex until temp = 1
See [Hai19] for a cache-conscious spinlock variant (beyond scope of class).
lock()
fails on a locked mutex, OS blocks threadunlock()
import java.util.concurrent.Semaphore; /* This code is based on Figure 4.18 of the following book: Max Hailperin, Operating Systems and Middleware – Supporting Controlled Interaction, revised edition 1.3, 2017. https://gustavus.edu/mcs/max/os-book/ In Figure 4.18, synchronizedList() is used, whereas here a plain LinkedList is used, which is protected by the additional semaphore mutex. Also, the class here is renamed and implements a new interface. */ public class SemaphoreBoundedBuffer implements BoundedBuffer { private java.util.List<Object> buffer = new java.util.LinkedList<Object>(); private static final int SIZE = 20; // arbitrary private Semaphore mutex = new Semaphore(1); private Semaphore occupiedSem = new Semaphore(0); private Semaphore freeSem = new Semaphore(SIZE); /* invariant: occupiedSem + freeSem = SIZE buffer.size() = occupiedSem buffer contains entries from oldest to youngest */ public void insert(Object o) throws InterruptedException { // Called by producer thread freeSem.acquire(); mutex.acquire(); buffer.add(o); mutex.release(); occupiedSem.release(); } public Object retrieve() throws InterruptedException { // Called by consumer thread occupiedSem.acquire(); mutex.acquire(); Object retrieved = buffer.remove(0); mutex.release(); freeSem.release(); return retrieved; } public int size() { return buffer.size(); } }
up()
and down()
man futex
This document is part of an Open Educational Resource (OER) course on Operating Systems. Source code and source files are available on GitLab under free licenses.
Except where otherwise noted, the work “OS05: Mutual Exclusion”, © 2017-2023 Jens Lechtenbörger, is published under the Creative Commons license CC BY-SA 4.0.
In particular, trademark rights are not licensed under this license. Thus, rights concerning third party logos (e.g., on the title slide) and other (trade-) marks (e.g., “Creative Commons” itself) remain with their respective holders.