Concurrency II: Synchronization Nov 14, 2000: "The Course That Gives CMU Its Zip!"
Concurrency II: Synchronization Nov 14, 2000: "The Course That Gives CMU Its Zip!"
class23.ppt
Head (Hi)
Load ctr (Li) Update ctr (Ui) Store ctr (Si) Tail (Ti)
class23.ppt
CS 213 F00
Concurrent execution
Key thread idea: In general, any sequentially consistent interleaving is possible, but some are incorrect!
Ii denotes that thread i executes instruction I %eaxi is the contents of %eax in thread is context
i (thread) 1 1 1 1 2 2 2 2 2 1 instri H1 L1 U1 S1 H2 L2 U2 S2 T2 T1 %eax1 0 1 1 1 %eax2 1 2 2 2 ctr 0 0 0 1 1 1 1 2 2 2
OK
CS 213 F00
class23.ppt
i (thread) 1 1 1 2 2 1 1 2 2 2
instri H1 L1 U1 H2 L2 S1 T1 U2 S2 T2
%eax1 0 1 1 1 -
%eax2 0 1 1 1
ctr 0 0 0 0 0 1 1 1 1 1
Oops!
class23.ppt
CS 213 F00
We can clarify our understanding of concurrent execution with the help of the progress graph
class23.ppt 6 CS 213 F00
Progress graphs
Thread 2 T2 S2 (L1, S2)
A progress graph depicts the discrete execution state space of concurrent threads. Each axis corresponds to the sequential order of instructions in a thread. Each point corresponds to a possible execution state (Inst1, Inst2). E.g., (L1, S2) denotes state where thread 1 has completed L1 and thread 2 has completed S2.
U2 L2 H2
(O,O)
H1
L1
U1
S1
T1
Thread 1
class23.ppt
CS 213 F00
Key point: Always reason about concurrent threads as if each thread had its own CPU.
class23.ppt 8 CS 213 F00
Trajectorie s
Thread 2 T2 S2
A trajectory is a sequence of legal state transitions that describes one possible concurrent execution of the threads. Example:
U2 L2 H2
(O,O)
H1
L1
U1
S1
T1
Thread 1
class23.ppt
CS 213 F00
L, U, and S form a critical section with respect to the shared variable ctr. Instructions in critical sections (wrt to some shared variable) should not be interleaved. Sets of states where such interleaving occurs form unsafe regions.
U2 L2 H2
(O,O)
H1
L1
U1
S1
T1
Thread 1
Thread 2 T2
Safe trajectories
Unsafe region Def: A safe trajectory is a sequence of legal transitions that does not touch any states in an unsafe region. Claim: Any safe trajectory results in a correct value for the shared variable ctr.
S2
critical section
U2 L2 H2
(O,O)
H1
L1
U1
S1
T1
Thread 1
critical section
class23.ppt 11 CS 213 F00
Unsafe trajectories
Thread 2 T2
Touching a state of type x is always incorrect. Touching a state of type y may or may not be OK: correct because store completes before load. incorrect because order of load and store are indeterminate. Moral: be conservative and disallow all unsafe trajectories.
Unsafe region
S2 y x x y x x y y
critical section
U2 L2 H2
(O,O)
H1
L1
U1
S1
T1 Thread 1
critical section
class23.ppt 12 CS 213 F00
Semaphore operations
Question: How can we guarantee a safe trajectory?
We must synchronize the threads so that they never enter an unsafe state.
class23.ppt
13
CS 213 F00
s=1
V(s)
s=0
s=1
ctr++; P(s) H2
s=0
s = -1 (forbidden s = 0 region)
Provide mutually exclusive access to shared variable by surrounding critical section with P and V operations on semaphore s (initially set to 1). Semaphore invariant creates a forbidden region that encloses unsafe region and is never touched by any trajectory. Semaphore used in this way is often called a mutex (mutual exclusion).
s=1
s=0
s=1
(O,O)
H1
Initially, s = 1
class23.ppt
T1 Thread 1
14
CS 213 F00
class23.ppt
15
CS 213 F00
/* counter thread */ void *count(void *arg) { int i; for (i=0; i<NITERS; i++) { P(&shared.mutex); shared.ctr++; V(&shared.mutex); } return NULL; }
P(m)
V(m)
P(m)
V(m)
P(m)
V(m)
Thread 1
Initially, mutex = 1
class23.ppt 17 CS 213 F00
Deadloc k
Thread 2 V(s) forbidden region for s V(t)
deadlock state
Semaphores introduce the potential for deadlock: waiting for a condition that will never be true. Any trajectory that enters the deadlock region will eventually reach the deadlock state, waiting for either s or t to become nonzero. Other trajectories luck out and skirt the deadlock region.
P(s)
P(t)
V(s)
V(t) Thread 1
Initially, s=t=1
class23.ppt 18
A deterministic deadlock
Thread 2 V(t) f.r. for t P(t) f.r. for t f.r. for s P(s) deadlock region deadlock state
... Sometimes though, we get "lucky" and the deadlock is deterministic. Here is an example of a deterministic deadlock caused by improperly initializing semaphore t. Problem: correct this program and draw the resulting forbidden regions.
P(t) V(t) Thread 1
...
V(s)
P(s)
V(s)
Initially, s = 1, t = 0.
class23.ppt 19 CS 213 F00
Examples
Multimedia processing: producer creates MPEG video frames, consumer renders the frames Graphical user interfaces producer detects mouse clicks, mouse movements, and keyboard hits and inserts corresponding events in buffer. consumer retrieves events from buffer and paints the display.
class23.ppt
20
CS 213 F00
Producer-consumer (1buffer)
/* buf1.c - producer-consumer on 1-element buffer */ #include <ics.h> #define NITERS 5 void *producer(void *arg); void *consumer(void *arg); struct { int buf; /* shared var */ sem_t full; /* sems */ sem_t empty; } shared; int main() { pthread_t tid_producer; pthread_t tid_consumer; /* initialize the semaphores */ Sem_init(&shared.empty, 0, 1); Sem_init(&shared.full, 0, 0); /* create threads and wait */ Pthread_create(&tid_producer, NULL, producer, NULL); Pthread_create(&tid_consumer, NULL, consumer, NULL); Pthread_join(tid_producer, NULL); Pthread_join(tid_consumer, NULL); exit(0); }
class23.ppt
21
CS 213 F00
/* consumer thread */ void *consumer(void *arg) { int i, item; for (i=0; i<NITERS; i++) { /* read item from buf */ P(&shared.full); item = shared.buf; V(&shared.empty); /* consume item */ printf("consumed %d\n", item); } return NULL; }
The forbidden regions prevent the producer from writing into a full buffer. They also prevent the consumer from reading an empty buffer. Problem: Write version for n-element buffer with multiple producers and consumers.
P(e) V(f) P(e) V(f) P(e) V(f) Producer
23 CS 213 F00
Limitations of semaphores
Semaphores are sound and fundamental, but they have limitations.
Difficult to broadcast a signal to a group of threads. e.g., barrier synchronization: no thread returns from the barrier function until every other thread has called the barrier function. Impossible to do timeout waiting. e.g., wait for at most 1 second for a condition to become true.
class23.ppt
24
CS 213 F00
Initializes a mutex variable (mutex) with some attributes (attr). attributes are usually NULL. like initializing a mutex semaphore to 1.
int pthread_mutex_lock(pthread_mutex_t *mutex)
Indivisibly waits for mutex to be unlocked and then locks it. like P(mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
Initializes a condition variable (cond) with some attributes (attr). attributes are usually NULL.
int pthread_cond_signal(pthread_cond_t *cond)
Awakens one thread waiting on condition cond. if no threads waiting on condition, then it does nothing. key point: signals are not queued!
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
Indivisibly unlocks mutex and waits for signal on condition cond When awakened, indivisibly locks mutex.
class23.ppt
26
CS 213 F00
Awakens all threads waiting on condition cond. if no threads waiting on condition, then it does nothing.
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *abstime)
Waits for condition cond until absolute wall clock time is abstime Unlocks mutex on entry, locks mutex on awakening. Use of absolute time rather than relative time is strange.
class23.ppt
27
CS 213 F00
A mutex is always associated with a condition variable. Guarantees that the condition cannot be signaled (and thus ignored) in the interval when the waiter locks the mutex and waits on the condition.
class23.ppt
28
CS 213 F00
#include <ics.h> static static static static pthread_mutex_t mutex; pthread_cond_t cond; int nthreads; int barriercnt = 0;
Barrier synchronization
Call to barrier will not return until every other thread has also called barrier. Needed for tightlycoupled parallel applications that proceed in phases. E.g., physical simulations.
void barrier_init(int n) { nthreads = n; Pthread_mutex_init(&mutex, NULL); Pthread_cond_init(&cond, NULL); } void barrier() { Pthread_mutex_lock(&mutex); if (++barriercnt == nthreads) { barriercnt = 0; Pthread_cond_broadcast(&cond); } else Pthread_cond_wait(&cond, &mutex); Pthread_mutex_unlock(&mutex); }
class23.ppt
29
CS 213 F00
class23.ppt
30
CS 213 F00
timebomb.c (cont)
A routine for building a timeout structure for pthread_cond_timewait.
/* * maketimeout - builds a timeout object that times out * in secs seconds */ struct timespec *maketimeout(int secs) { struct timeval now; struct timespec *tp = (struct timespec *)malloc(sizeof(struct timespec)); gettimeofday(&now, NULL); tp->tv_sec = now.tv_sec + secs; tp->tv_nsec = now.tv_usec * 1000; return tp; }
class23.ppt
31
CS 213 F00
class23.ppt
33
CS 213 F00