SlideShare a Scribd company logo
Carnegie Mellon
1
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Synchronization: Basics
15-213/14-513/15-513: Introduction to Computer Systems
24th Lecture, July 28, 2022
Instructor: Abi Kim
Carnegie Mellon
2
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Today
 Threads
 Sharing
 Mutual exclusion
Carnegie Mellon
3
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Traditional View of a Process
 Process = process context + code, data, and stack
Program context:
Data registers
Condition codes
Stack pointer (SP)
Program counter (PC)
Code, data, and stack
Stack
SP
Shared libraries
Run-time heap
0
Read/write data
Read-only code/data
PC
brk
Process context
Kernel context:
VM structures
Descriptor table
brk pointer
Carnegie Mellon
4
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Alternate View of a Process
 Process = thread + (code, data, and kernel context)
Shared libraries
Run-time heap
0
Read/write data
Thread context:
Data registers
Condition codes
Stack pointer (SP)
Program counter (PC)
Code, data, and kernel context
Read-only code/data
Stack
SP
PC
brk
Thread (main thread)
Kernel context:
VM structures
Descriptor table
brk pointer
Carnegie Mellon
5
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
A Process With Multiple Threads
 Multiple threads can be associated with a process
 Each thread has its own logical control flow
 Each thread shares the same code, data, and kernel context
 Each thread has its own stack for local variables
 but not protected from other threads
 Each thread has its own thread id (TID)
Thread 1 context:
Data registers
Condition codes
SP1
PC1
stack 1
Thread 1 (main thread)
shared libraries
run-time heap
0
read/write data
Shared code and data
read-only code/data
Kernel context:
VM structures
Descriptor table
brk pointer
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
Carnegie Mellon
6
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Memory is shared between all threads
Don’t let the picture confuse you!
stack 1
Thread 1 (main thread)
shared libraries
run-time heap
0
read/write data
Shared code and data
read-only code/data
Kernel context:
VM structures
Descriptor table
brk pointer
Thread 1 context:
Data registers
Condition codes
SP1
PC1
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2 (peer thread)
Carnegie Mellon
7
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Benefits of Threads
 Threads have lighter overhead
 Easier to share memory in concurrent programs using
threads
 Threads are faster due to multi-core CPUs allowing
multiple threads to execute at once
Carnegie Mellon
8
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Today
 Threads review
 Sharing
 Mutual exclusion
 Semaphores
Carnegie Mellon
9
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shared Variables in Threaded C Programs
 Question: Which variables in a threaded C program are
shared?
 The answer is not as simple as “global variables are shared” and
“stack variables are private”
 Def: A variable x is shared if and only if multiple threads
reference some instance of x.
 Requires answers to the following questions:
 What is the memory model for threads?
 How are instances of variables mapped to memory?
 How many threads might reference each of these instances?
Carnegie Mellon
10
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Threads Memory Model: Conceptual
 Multiple threads run within the context of a single process
 Each thread has its own separate thread context
 Thread ID, stack, stack pointer, PC, condition codes, and GP registers
 All threads share the remaining process context
 Code, data, heap, and shared library segments of the process virtual address space
 Open files and installed handlers
Thread 1 context:
Data registers
Condition codes
SP1
PC1
stack 1
Thread 1
(private) Shared code and data
shared libraries
run-time heap
read/write data
read-only code/data
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2
(private)
Carnegie Mellon
11
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Threads Memory Model: Actual
 Separation of data is not strictly enforced:
 Register values are truly separate and protected, but…
 Any thread can read and write the stack of any other thread
The mismatch between the conceptual and operation model
is a source of confusion and errors
Thread 1 context:
Data registers
Condition codes
SP1
PC1
stack 1
Thread 1
(private)
Shared code and data
shared libraries
run-time heap
read/write data
read-only code/data
Thread 2 context:
Data registers
Condition codes
SP2
PC2
stack 2
Thread 2
(private)
Virtual Address Space
Carnegie Mellon
12
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Passing an argument to a thread - Pedantic
int hist[N] = {0};
int main(int argc, char *argv[]) {
long i;
pthread_t tids[N];
for (i = 0; i < N; i++) {
long* p = Malloc(sizeof(long));
*p = i;
Pthread_create(&tids[i],
NULL,
thread,
(void *)p);
}
for (i = 0; i < N; i++)
Pthread_join(tids[i], NULL);
check();
}
void *thread(void *vargp)
{
hist[*(long *)vargp] += 1;
Free(vargp);
return NULL;
}
void check(void) {
for (int i=0; i<N; i++) {
if (hist[i] != 1) {
printf("Failed at %dn", i);
exit(-1);
}
}
printf("OKn");
}
Carnegie Mellon
13
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Passing an argument to a thread - Pedantic
int hist[N] = {0};
int main(int argc, char *argv[]) {
long i;
pthread_t tids[N];
for (i = 0; i < N; i++) {
long* p = Malloc(sizeof(long));
*p = i;
Pthread_create(&tids[i],
NULL,
thread,
(void *)p);
}
for (i = 0; i < N; i++)
Pthread_join(tids[i], NULL);
check();
}
void *thread(void *vargp)
{
hist[*(long *)vargp] += 1;
Free(vargp);
return NULL;
}
• Use malloc to create a per
thread heap allocated
place in memory for the
argument
• Remember to free in
thread!
• Producer-consumer
pattern
Carnegie Mellon
14
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
int hist[N] = {0};
int main(int argc, char *argv[]) {
long i;
pthread_t tids[N];
for (i = 0; i < N; i++)
Pthread_create(&tids[i],
NULL,
thread,
(void *)i);
for (i = 0; i < N; i++)
Pthread_join(tids[i], NULL);
check();
}
void *thread(void *vargp)
{
hist[(long)vargp] += 1;
return NULL;
}
Passing an argument to a thread – Also OK!
• Ok to Use cast since
sizeof(long) <= sizeof(void*)
• Cast does NOT change bits
Carnegie Mellon
15
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
int hist[N] = {0};
int main(int argc, char *argv[]) {
long i;
pthread_t tids[N];
for (i = 0; i < N; i++)
Pthread_create(&tids[i],
NULL,
thread,
(void *)&i);
for (i = 0; i < N; i++)
Pthread_join(tids[i], NULL);
check();
}
void *thread(void *vargp)
{
hist[*(long*)vargp] += 1;
return NULL;
}
Passing an argument to a thread – WRONG!
• &i points to same location
for all threads!
• Creates a data race!
Carnegie Mellon
16
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Three Ways to Pass Thread Arg
 Malloc/free
 Producer malloc’s space, passes pointer to pthread_create
 Consumer dereferences pointer
 Ptr to stack slot
 Producer passes address to producer’s stack in pthread_create
 Consumer dereferences pointer
 Cast of int
 Producer casts an int/long to address in pthread_create
 Consumer casts void* argument back to int/long
Carnegie Mellon
17
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Example Program to Illustrate Sharing
char **ptr; /* global var */
int main(int argc, char *argv[])
{
long i;
pthread_t tid;
char *msgs[2] = {
"Hello from foo",
"Hello from bar"
};
ptr = msgs;
for (i = 0; i < 2; i++)
Pthread_create(&tid,
NULL,
thread,
(void *)i);
Pthread_exit(NULL);
}
void *thread(void *vargp)
{
long myid = (long)vargp;
static int cnt = 0;
printf("[%ld]: %s (cnt=%d)n",
myid, ptr[myid], ++cnt);
return NULL;
}
Peer threads reference main thread’s stack
indirectly through global ptr variable
sharing.c
A common way to pass a single
argument to a thread routine
Carnegie Mellon
18
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shared Variables in Threaded C Programs
 Question: Which variables in a threaded C program are
shared?
 The answer is not as simple as “global variables are shared” and
“stack variables are private”
 Def: A variable x is shared if and only if multiple threads
reference some instance of x.
 Requires answers to the following questions:
 What is the memory model for threads?
 How are instances of variables mapped to memory?
 How many threads might reference each of these instances?
Carnegie Mellon
19
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Mapping Variable Instances to Memory
 Global variables
 Def: Variable declared outside of a function
 Virtual memory contains exactly one instance of any global variable
 Local variables
 Def: Variable declared inside function without static attribute
 Each thread stack contains one instance of each local variable
 Local static variables
 Def: Variable declared inside function with the static attribute
 Virtual memory contains exactly one instance of any local static
variable.
Carnegie Mellon
20
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
char **ptr; /* global var */
int main(int main, char *argv[])
{
long i;
pthread_t tid;
char *msgs[2] = {
"Hello from foo",
"Hello from bar"
};
ptr = msgs;
for (i = 0; i < 2; i++)
Pthread_create(&tid,
NULL,
thread,
(void *)i);
Pthread_exit(NULL);
}
void *thread(void *vargp)
{
long myid = (long)vargp;
static int cnt = 0;
printf("[%ld]: %s (cnt=%d)n",
myid, ptr[myid], ++cnt);
return NULL;
}
Mapping Variable Instances to Memory
sharing.c
Carnegie Mellon
21
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
char **ptr; /* global var */
int main(int main, char *argv[])
{
long i;
pthread_t tid;
char *msgs[2] = {
"Hello from foo",
"Hello from bar"
};
ptr = msgs;
for (i = 0; i < 2; i++)
Pthread_create(&tid,
NULL,
thread,
(void *)i);
Pthread_exit(NULL);
}
void *thread(void *vargp)
{
long myid = (long)vargp;
static int cnt = 0;
printf("[%ld]: %s (cnt=%d)n",
myid, ptr[myid], ++cnt);
return NULL;
}
Mapping Variable Instances to Memory
Global var: 1 instance (ptr [data])
Local static var: 1 instance (cnt [data])
Local vars: 1 instance (i.m, msgs.m, tid.m)
Local var: 2 instances (
myid.p0 [peer thread 0’s stack],
myid.p1 [peer thread 1’s stack]
)
sharing.c
Carnegie Mellon
22
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shared Variable Analysis
 Which variables are shared?
 Answer: A variable x is shared iff multiple threads
reference at least one instance of x. Thus:
 ptr, cnt, and msgs are shared
 i and myid are not shared
Variable Referenced by Referenced by Referenced by
instance main thread? peer thread 0? peer thread 1?
ptr
cnt
i.m
msgs.m
myid.p0
myid.p1
yes yes yes
no yes yes
yes no no
yes yes yes
no yes no
no no yes
char **ptr; /* global var */
int main(int main, char *argv[]) {
long i; pthread_t tid;
char *msgs[2] = {"Hello from foo",
"Hello from bar" };
ptr = msgs;
for (i = 0; i < 2; i++)
Pthread_create(&tid,
NULL, thread,(void *)i);
Pthread_exit(NULL);}
void *thread(void *vargp)
{
long myid = (long)vargp;
static int cnt = 0;
printf("[%ld]: %s (cnt=%d)n",
myid, ptr[myid], ++cnt);
return NULL;
}
Carnegie Mellon
23
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shared Variable Analysis
 Which variables are shared?
 Answer: A variable x is shared iff multiple threads
reference at least one instance of x. Thus:
 ptr, cnt, and msgs are shared
 i and myid are not shared
Variable Referenced by Referenced by Referenced by
instance main thread? peer thread 0? peer thread 1?
ptr
cnt
i.m
msgs.m
myid.p0
myid.p1
yes yes yes
no yes yes
yes no no
yes yes yes
no yes no
no no yes
Carnegie Mellon
24
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Synchronizing Threads
 Shared variables are handy...
 …but introduce the possibility of nasty synchronization
errors.
Carnegie Mellon
25
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
badcnt.c: Improper Synchronization
/* Global shared variable */
volatile long cnt = 0; /* Counter */
int main(int argc, char **argv)
{
long niters;
pthread_t tid1, tid2;
niters = atoi(argv[1]);
Pthread_create(&tid1, NULL,
thread, &niters);
Pthread_create(&tid2, NULL,
thread, &niters);
Pthread_join(tid1, NULL);
Pthread_join(tid2, NULL);
/* Check result */
if (cnt != (2 * niters))
printf("BOOM! cnt=%ldn", cnt);
else
printf("OK cnt=%ldn", cnt);
exit(0);
}
/* Thread routine */
void *thread(void *vargp)
{
long i, niters =
*((long *)vargp);
for (i = 0; i < niters; i++)
cnt++;
return NULL;
}
linux> ./badcnt 10000
OK cnt=20000
linux> ./badcnt 10000
BOOM! cnt=13051
linux>
cnt should equal 20,000.
What went wrong?
badcnt.c
Carnegie Mellon
26
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Assembly Code for Counter Loop
for (i = 0; i < niters; i++)
cnt++;
C code for counter loop in thread i
movq (%rdi), %rcx
testq %rcx,%rcx
jle .L2
movl $0, %eax
.L3:
movq cnt(%rip),%rdx
addq $1, %rdx
movq %rdx, cnt(%rip)
addq $1, %rax
cmpq %rcx, %rax
jne .L3
.L2:
Hi : Head
Ti : Tail
Li : Load cnt
Ui : Update cnt
Si : Store cnt
Asm code for thread i
Carnegie Mellon
27
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Concurrent Execution
 Key idea: In general, any sequentially consistent*
interleaving is possible, but some give an unexpected result!
 Ii denotes that thread i executes instruction I
 %rdxi is the content of %rdx in thread i’s context
H1
L1
U1
S1
H2
L2
U2
S2
T2
T1
1
1
1
1
2
2
2
2
2
1
-
0
1
1
-
-
-
-
-
1
0
0
0
1
1
1
1
2
2
2
i (thread) instri cnt
%rdx1
OK
-
-
-
-
-
1
2
2
2
-
%rdx2
*For now. In reality, on x86 even non-sequentially consistent interleavings are possible
Carnegie Mellon
28
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Concurrent Execution
 Key idea: In general, any sequentially consistent interleaving
is possible, but some give an unexpected result!
 Ii denotes that thread i executes instruction I
 %rdxi is the content of %rdx in thread i’s context
H1
L1
U1
S1
H2
L2
U2
S2
T2
T1
1
1
1
1
2
2
2
2
2
1
-
0
1
1
-
-
-
-
-
1
0
0
0
1
1
1
1
2
2
2
i (thread) instri cnt
%rdx1
OK
-
-
-
-
-
1
2
2
2
-
%rdx2
Thread 1
critical section
Thread 2
critical section
Carnegie Mellon
29
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Concurrent Execution (cont)
 Incorrect ordering: two threads increment the counter,
but the result is 1 instead of 2
H1
L1
U1
H2
L2
S1
T1
U2
S2
T2
1
1
1
2
2
1
1
2
2
2
-
0
1
-
-
1
1
-
-
-
0
0
0
0
0
1
1
1
1
1
i (thread) instri cnt
%rdx1
-
-
-
-
0
-
-
1
1
1
%rdx2
Oops!
Carnegie Mellon
30
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Concurrent Execution (cont)
 How about this ordering?
 We can analyze the behavior using a progress graph
H1
L1
H2
L2
U2
S2
U1
S1
T1
T2
1
1
2
2
2
2
1
1
1
2
i (thread) instri cnt
%rdx1 %rdx2
0
0
0
1
1 1
1
1 1
1 Oops!
1
Carnegie Mellon
31
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Progress Graphs
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.
(L1, S2)
H1 L1 U1 S1 T1
H2
L2
U2
S2
T2
Thread 1
Thread 2
Carnegie Mellon
32
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Trajectories in Progress Graphs
A trajectory is a sequence of legal
state transitions that describes one
possible concurrent execution of the
threads.
Example:
H1, L1, U1, H2, L2, S1, T1, U2, S2, T2
H1 L1 U1 S1 T1
H2
L2
U2
S2
T2
Thread 1
Thread 2
Carnegie Mellon
33
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Trajectories in Progress Graphs
A trajectory is a sequence of legal
state transitions that describes one
possible concurrent execution of the
threads.
Example:
H1, L1, U1, H2, L2, S1, T1, U2, S2, T2
H1 L1 U1 S1 T1
H2
L2
U2
S2
T2
Thread 1
Thread 2
Carnegie Mellon
34
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Critical Sections and Unsafe Regions
L, U, and S form a critical
section with respect to the
shared variable cnt
Instructions in critical
sections (wrt some shared
variable) should not be
interleaved
Sets of states where such
interleaving occurs form
unsafe regions
H1 L1 U1 S1 T1
H2
L2
U2
S2
T2
Thread 1
Thread 2
critical section wrt cnt
critical
section
wrt
cnt
Unsafe region
Carnegie Mellon
35
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Critical Sections and Unsafe Regions
H1 L1 U1 S1 T1
H2
L2
U2
S2
T2
Thread 1
Thread 2
critical section wrt cnt
critical
section
wrt
cnt
Unsafe region
Def: A trajectory is safe iff it does
not enter any unsafe region
Claim: A trajectory is correct (wrt
cnt) iff it is safe
unsafe
safe
Carnegie Mellon
36
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
badcnt.c: Improper Synchronization
/* Global shared variable */
volatile long cnt = 0; /* Counter */
int main(int argc, char **argv)
{
long niters;
pthread_t tid1, tid2;
niters = atoi(argv[1]);
Pthread_create(&tid1, NULL,
thread, &niters);
Pthread_create(&tid2, NULL,
thread, &niters);
Pthread_join(tid1, NULL);
Pthread_join(tid2, NULL);
/* Check result */
if (cnt != (2 * niters))
printf("BOOM! cnt=%ldn", cnt);
else
printf("OK cnt=%ldn", cnt);
exit(0);
}
/* Thread routine */
void *thread(void *vargp)
{
long i, niters =
*((long *)vargp);
for (i = 0; i < niters; i++)
cnt++;
return NULL;
}
badcnt.c
Variable main thread1 thread2
cnt
niters.m
tid1.m
i.1
i.2
niters.1
niters.2
Carnegie Mellon
37
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
badcnt.c: Improper Synchronization
/* Global shared variable */
volatile long cnt = 0; /* Counter */
int main(int argc, char **argv)
{
long niters;
pthread_t tid1, tid2;
niters = atoi(argv[1]);
Pthread_create(&tid1, NULL,
thread, &niters);
Pthread_create(&tid2, NULL,
thread, &niters);
Pthread_join(tid1, NULL);
Pthread_join(tid2, NULL);
/* Check result */
if (cnt != (2 * niters))
printf("BOOM! cnt=%ldn", cnt);
else
printf("OK cnt=%ldn", cnt);
exit(0);
}
/* Thread routine */
void *thread(void *vargp)
{
long i, niters =
*((long *)vargp);
for (i = 0; i < niters; i++)
cnt++;
return NULL;
}
badcnt.c
Variable main thread1 thread2
cnt yes* yes yes
niters.m yes no no
tid1.m yes no no
i.1 no yes no
i.2 no no yes
niters.1 no yes no
niters.2 no no yes
Carnegie Mellon
38
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Today
 Threads review
 Sharing
 Mutual exclusion
Carnegie Mellon
39
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Enforcing Mutual Exclusion
 Question: How can we guarantee a safe trajectory?
 Answer: We must synchronize the execution of the threads so
that they can never have an unsafe trajectory.
 i.e., need to guarantee mutually exclusive access for each critical
section.
 Classic solution:
 Mutex (pthreads)
 Semaphores (Edsger Dijkstra)
 Other approaches (out of our scope)
 Condition variables (pthreads)
 Monitors (Java)
Carnegie Mellon
40
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Semaphores
 Semaphore: non-negative global integer synchronization variable.
Manipulated by P and V operations.
 P(s)
 If s is nonzero, then decrement s by 1 and return immediately.
 Test and decrement operations occur atomically (indivisibly)
 If s is zero, then suspend thread until s becomes nonzero and the thread is
restarted by a V operation.
 After restarting, the P operation decrements s and returns control to the
caller.
 V(s):
 Increment s by 1.
 Increment operation occurs atomically
 If there are any threads blocked in a P operation waiting for s to become non-
zero, then restart exactly one of those threads, which then completes its P
operation by decrementing s.
 Semaphore invariant: (s >= 0)
Carnegie Mellon
41
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Semaphores
 Semaphore: non-negative global integer synchronization
variable
 Manipulated by P and V operations:
 P(s): [ while (s == 0) wait(); s--; ]
 Dutch for "Proberen" (test)
 V(s): [ s++; ]
 Dutch for "Verhogen" (increment)
 OS kernel guarantees that operations between brackets [ ] are
executed indivisibly
 Only one P or V operation at a time can modify s.
 When while loop in P terminates, only that P can decrement s
 Semaphore invariant: (s >= 0)
Carnegie Mellon
42
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
C Semaphore Operations
Pthreads functions:
#include <semaphore.h>
int sem_init(sem_t *s, 0, unsigned int val);} /* s = val */
int sem_wait(sem_t *s); /* P(s) */
int sem_post(sem_t *s); /* V(s) */
CS:APP wrapper functions:
#include "csapp.h”
void P(sem_t *s); /* Wrapper function for sem_wait */
void V(sem_t *s); /* Wrapper function for sem_post */
Carnegie Mellon
43
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
badcnt.c: Improper Synchronization
/* Global shared variable */
volatile long cnt = 0; /* Counter */
int main(int argc, char **argv)
{
long niters;
pthread_t tid1, tid2;
niters = atoi(argv[1]);
Pthread_create(&tid1, NULL,
thread, &niters);
Pthread_create(&tid2, NULL,
thread, &niters);
Pthread_join(tid1, NULL);
Pthread_join(tid2, NULL);
/* Check result */
if (cnt != (2 * niters))
printf("BOOM! cnt=%ldn", cnt);
else
printf("OK cnt=%ldn", cnt);
exit(0);
}
/* Thread routine */
void *thread(void *vargp)
{
long i, niters =
*((long *)vargp);
for (i = 0; i < niters; i++)
cnt++;
return NULL;
}
How can we fix this using
synchronization?
badcnt.c
Carnegie Mellon
44
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
MUTual EXclusion (mutex)
 Mutex: boolean synchronization variable
 enum {locked = 0, unlocked = 1}
 lock(m)
 If the mutex is currently not locked, lock it and return
 Otherwise, wait (spinning, yielding, etc) and retry
 unlock(m)
 Update the mutex state to unlocked
Carnegie Mellon
46
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Using Semaphores for Mutual Exclusion
⬛ Basic idea:
▪ Associate a unique semaphore mutex, initially 1, with each shared
variable (or related set of shared variables).
▪ Surround corresponding critical sections with P(mutex) and
V(mutex) operations.
⬛ Terminology:
▪ Binary semaphore: semaphore whose value is always 0 or 1
▪ Mutex: binary semaphore used for mutual exclusion
▪ P operation: “locking” the mutex
▪ V operation: “unlocking” or “releasing” the mutex
▪ “Holding” a mutex: locked and not yet unlocked.
▪ Counting semaphore: used as a counter for set of available
resources.
Carnegie Mellon
47
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
goodcnt.c: Proper Synchronization
⬛ Define and initialize a mutex for the shared variable cnt:
volatile long cnt = 0; /* Counter */
sem_t mutex; /* Semaphore that protects cnt */
sem_init(&mutex, 0, 1); /* mutex = 1 */
⬛ Surround critical section with P and V:
for (i = 0; i < niters; i++) {
P(&mutex);
cnt++;
V(&mutex);
}
linux> ./goodcnt 10000
OK cnt=20000
linux> ./goodcnt 10000
OK cnt=20000
linux>
Warning: It’s orders of magnitude slower
than badcnt.c.
goodcnt.c
Carnegie Mellon
48
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
goodcnt.c: Proper Synchronization
⬛ Define and initialize a mutex for the shared variable cnt:
volatile long cnt = 0; /* Counter */
sem_t mutex; /* Semaphore that protects cnt */
sem_init(&mutex, 0, 1); /* mutex = 1 */
⬛ Surround critical section with P and V:
for (i = 0; i < niters; i++) {
P(&mutex);
cnt++;
V(&mutex);
}
linux> ./goodcnt 10000
OK cnt=20000
linux> ./goodcnt 10000
OK cnt=20000
linux>
Warning: It’s orders of magnitude slower
than badcnt.c.
goodcnt.c
OK cnt=2000000 BOOM! cnt=1036525 Slowdown
real 0m0.138s 0m0.007s 20X
user 0m0.120s 0m0.008s 15X
sys 0m0.108s 0m0.000s NaN
And slower means much slower!
Carnegie Mellon
49
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Why Mutexes Work
Provide mutually exclusive
access to shared variable by
surrounding critical section
with lock and unlock
operations
H1 lo(m) un(m) T1
Thread 1
Thread 2
L1 U1 S1
H2
lo(m)
un(m)
T2
L2
U2
S2
Initially
m = 1
1 0 0 0
0
-1
Unsafe region
Carnegie Mellon
50
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Why Mutexes Work
Provide mutually exclusive
access to shared variable by
surrounding critical section
with lock and unlock
operations
Mutex invariant creates a
forbidden region that encloses
unsafe region and that cannot
be entered by any trajectory.
H1 lo(m) un(m) T1
Thread 1
Thread 2
L1 U1 S1
H2
lo(m)
un(m)
T2
L2
U2
S2
Initially
m = 1
1 0 0 0
0
-1
Unsafe region
Carnegie Mellon
51
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Why Mutexes Work
Provide mutually exclusive
access to shared variable by
surrounding critical section
with lock and unlock
operations
Mutex invariant creates a
forbidden region that encloses
unsafe region and that cannot
be entered by any trajectory.
H1 lo(m) un(m) T1
Thread 1
Thread 2
L1 U1 S1
H2
lo(m)
un(m)
T2
L2
U2
S2
Initially
m = 1
1 0 0 0
0
Unsafe region
0 1
0
Carnegie Mellon
52
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Unsafe region
Why Mutexes Work
Provide mutually exclusive
access to shared variable by
surrounding critical section
with lock and unlock
operations
Mutex invariant creates a
forbidden region that encloses
unsafe region and that cannot
be entered by any trajectory.
H1 lo(m) un(m) T1
Thread 1
Thread 2
L1 U1 S1
H2
lo(m)
un(m)
T2
L2
U2
S2
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
0 0 -1 -1 -1 -1 0 0
0 0
-1 -1 -1 -1
0 0
0 0
-1 -1 -1 -1
0 0
0 0
-1 -1 -1 -1
0 0
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
Initially
m = 1
Forbidden region
Carnegie Mellon
53
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Summary
 Programmers need a clear model of how variables are
shared by threads.
 Variables shared by multiple threads must be protected
to ensure mutually exclusive access.
 Semaphores are a fundamental mechanism for enforcing
mutual exclusion.
Carnegie Mellon
54
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Binary Semaphores vs.
Mutexes (not test material, just fyi!)
 Binary semaphore: semaphore initialized with a value of 1
 Both binary semaphores and mutexes can be used to
guarantee mutual exclusion
 Main difference is ownership
 Mutexes must be unlocked by the thread who owned them
previously
 Binary semaphores can be signaled/incremented (V) by a thread
who did not decrement (P) them
 As long as you use binary semaphores in the following
way in all threads, they can be used as a mutex
P(&sem);
// critical section
V(&sem); They are also implemented
differently but that’s out of
the scope for this class…
(covered in 15-410)

More Related Content

PPTX
CS345 09 - Ch04 Threads operating system1.pptx
RichaAgnihotri13
 
PPTX
Engineeering Operating systemsOS UNIT 3 Threads.pptx
ppkmurthy2006
 
PDF
Threads lecture slides for operating systems
amirtarek401
 
PDF
The Thread Chapter 4 of Operating System
mohammadhaidarayoobi
 
PPTX
WEEK07operatingsystemdepartmentofsoftwareengineering.pptx
babayaga920391
 
PDF
Sucet os module_2_notes
SRINIVASUNIVERSITYEN
 
PPTX
Operating Systems R20 Unit 2.pptx
Prudhvi668506
 
PDF
Process Control Block (PCB) print 4.pdf
fentahunmuluye23
 
CS345 09 - Ch04 Threads operating system1.pptx
RichaAgnihotri13
 
Engineeering Operating systemsOS UNIT 3 Threads.pptx
ppkmurthy2006
 
Threads lecture slides for operating systems
amirtarek401
 
The Thread Chapter 4 of Operating System
mohammadhaidarayoobi
 
WEEK07operatingsystemdepartmentofsoftwareengineering.pptx
babayaga920391
 
Sucet os module_2_notes
SRINIVASUNIVERSITYEN
 
Operating Systems R20 Unit 2.pptx
Prudhvi668506
 
Process Control Block (PCB) print 4.pdf
fentahunmuluye23
 

Similar to 24-sync-basic.pptx (20)

PDF
Threads operating system slides easy understand
shamsulhuda34
 
PPTX
Networking threads
Nilesh Pawar
 
PPT
Operating System 4
tech2click
 
PPT
Operating System 4 1193308760782240 2
mona_hakmy
 
PDF
Thread
Mohd Arif
 
PDF
Multithreaded Programming Part- II.pdf
Harika Pudugosula
 
PPT
chapter4-processes nd processors in DS.ppt
aakarshsiwani1
 
PDF
OSTEP Chapter2 Introduction
Shuya Osaki
 
PDF
posix.pdf
ssuserf39414
 
PPT
Treads
nayanashetty7
 
PPTX
1 Multithreading basics.pptx
YojanaFegade
 
PPTX
Os
DeepaR42
 
PPT
Shared Memory Programming with Pthreads (1).ppt
MALARMANNANA1
 
PPTX
Threads, signal and socket system calls.pptx
JohnNderi1
 
PPTX
OS Module-2.pptx
bleh23
 
PPTX
Threads and multi threading
Antonio Cesarano
 
PDF
GPU Programming on CPU - Using C++AMP
Miller Lee
 
PPTX
Lecture 3 threads
Kumbirai Junior Muzavazi
 
PDF
CH04.pdf
ImranKhan880955
 
PPTX
Topic 4- processes.pptx
DanishMahmood23
 
Threads operating system slides easy understand
shamsulhuda34
 
Networking threads
Nilesh Pawar
 
Operating System 4
tech2click
 
Operating System 4 1193308760782240 2
mona_hakmy
 
Thread
Mohd Arif
 
Multithreaded Programming Part- II.pdf
Harika Pudugosula
 
chapter4-processes nd processors in DS.ppt
aakarshsiwani1
 
OSTEP Chapter2 Introduction
Shuya Osaki
 
posix.pdf
ssuserf39414
 
1 Multithreading basics.pptx
YojanaFegade
 
Shared Memory Programming with Pthreads (1).ppt
MALARMANNANA1
 
Threads, signal and socket system calls.pptx
JohnNderi1
 
OS Module-2.pptx
bleh23
 
Threads and multi threading
Antonio Cesarano
 
GPU Programming on CPU - Using C++AMP
Miller Lee
 
Lecture 3 threads
Kumbirai Junior Muzavazi
 
CH04.pdf
ImranKhan880955
 
Topic 4- processes.pptx
DanishMahmood23
 

Recently uploaded (20)

PDF
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PDF
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
PPTX
ChatGPT's Deck on The Enduring Legacy of Fax Machines
Greg Swan
 
PDF
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
PDF
A Day in the Life of Location Data - Turning Where into How.pdf
Precisely
 
PPTX
Comunidade Salesforce São Paulo - Desmistificando o Omnistudio (Vlocity)
Francisco Vieira Júnior
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
PDF
Unlocking the Future- AI Agents Meet Oracle Database 23ai - AIOUG Yatra 2025.pdf
Sandesh Rao
 
PDF
Software Development Company | KodekX
KodekX
 
PDF
The Evolution of KM Roles (Presented at Knowledge Summit Dublin 2025)
Enterprise Knowledge
 
PDF
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
PDF
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
PDF
Brief History of Internet - Early Days of Internet
sutharharshit158
 
PDF
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
codernjn73
 
PDF
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
PPTX
IoT Sensor Integration 2025 Powering Smart Tech and Industrial Automation.pptx
Rejig Digital
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
PDF
Security features in Dell, HP, and Lenovo PC systems: A research-based compar...
Principled Technologies
 
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
ChatGPT's Deck on The Enduring Legacy of Fax Machines
Greg Swan
 
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
A Day in the Life of Location Data - Turning Where into How.pdf
Precisely
 
Comunidade Salesforce São Paulo - Desmistificando o Omnistudio (Vlocity)
Francisco Vieira Júnior
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
Unlocking the Future- AI Agents Meet Oracle Database 23ai - AIOUG Yatra 2025.pdf
Sandesh Rao
 
Software Development Company | KodekX
KodekX
 
The Evolution of KM Roles (Presented at Knowledge Summit Dublin 2025)
Enterprise Knowledge
 
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
Brief History of Internet - Early Days of Internet
sutharharshit158
 
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
codernjn73
 
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
IoT Sensor Integration 2025 Powering Smart Tech and Industrial Automation.pptx
Rejig Digital
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
Security features in Dell, HP, and Lenovo PC systems: A research-based compar...
Principled Technologies
 

24-sync-basic.pptx

  • 1. Carnegie Mellon 1 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Synchronization: Basics 15-213/14-513/15-513: Introduction to Computer Systems 24th Lecture, July 28, 2022 Instructor: Abi Kim
  • 2. Carnegie Mellon 2 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Today  Threads  Sharing  Mutual exclusion
  • 3. Carnegie Mellon 3 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Traditional View of a Process  Process = process context + code, data, and stack Program context: Data registers Condition codes Stack pointer (SP) Program counter (PC) Code, data, and stack Stack SP Shared libraries Run-time heap 0 Read/write data Read-only code/data PC brk Process context Kernel context: VM structures Descriptor table brk pointer
  • 4. Carnegie Mellon 4 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Alternate View of a Process  Process = thread + (code, data, and kernel context) Shared libraries Run-time heap 0 Read/write data Thread context: Data registers Condition codes Stack pointer (SP) Program counter (PC) Code, data, and kernel context Read-only code/data Stack SP PC brk Thread (main thread) Kernel context: VM structures Descriptor table brk pointer
  • 5. Carnegie Mellon 5 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition A Process With Multiple Threads  Multiple threads can be associated with a process  Each thread has its own logical control flow  Each thread shares the same code, data, and kernel context  Each thread has its own stack for local variables  but not protected from other threads  Each thread has its own thread id (TID) Thread 1 context: Data registers Condition codes SP1 PC1 stack 1 Thread 1 (main thread) shared libraries run-time heap 0 read/write data Shared code and data read-only code/data Kernel context: VM structures Descriptor table brk pointer Thread 2 context: Data registers Condition codes SP2 PC2 stack 2 Thread 2 (peer thread)
  • 6. Carnegie Mellon 6 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Memory is shared between all threads Don’t let the picture confuse you! stack 1 Thread 1 (main thread) shared libraries run-time heap 0 read/write data Shared code and data read-only code/data Kernel context: VM structures Descriptor table brk pointer Thread 1 context: Data registers Condition codes SP1 PC1 Thread 2 context: Data registers Condition codes SP2 PC2 stack 2 Thread 2 (peer thread)
  • 7. Carnegie Mellon 7 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Benefits of Threads  Threads have lighter overhead  Easier to share memory in concurrent programs using threads  Threads are faster due to multi-core CPUs allowing multiple threads to execute at once
  • 8. Carnegie Mellon 8 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Today  Threads review  Sharing  Mutual exclusion  Semaphores
  • 9. Carnegie Mellon 9 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Shared Variables in Threaded C Programs  Question: Which variables in a threaded C program are shared?  The answer is not as simple as “global variables are shared” and “stack variables are private”  Def: A variable x is shared if and only if multiple threads reference some instance of x.  Requires answers to the following questions:  What is the memory model for threads?  How are instances of variables mapped to memory?  How many threads might reference each of these instances?
  • 10. Carnegie Mellon 10 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Threads Memory Model: Conceptual  Multiple threads run within the context of a single process  Each thread has its own separate thread context  Thread ID, stack, stack pointer, PC, condition codes, and GP registers  All threads share the remaining process context  Code, data, heap, and shared library segments of the process virtual address space  Open files and installed handlers Thread 1 context: Data registers Condition codes SP1 PC1 stack 1 Thread 1 (private) Shared code and data shared libraries run-time heap read/write data read-only code/data Thread 2 context: Data registers Condition codes SP2 PC2 stack 2 Thread 2 (private)
  • 11. Carnegie Mellon 11 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Threads Memory Model: Actual  Separation of data is not strictly enforced:  Register values are truly separate and protected, but…  Any thread can read and write the stack of any other thread The mismatch between the conceptual and operation model is a source of confusion and errors Thread 1 context: Data registers Condition codes SP1 PC1 stack 1 Thread 1 (private) Shared code and data shared libraries run-time heap read/write data read-only code/data Thread 2 context: Data registers Condition codes SP2 PC2 stack 2 Thread 2 (private) Virtual Address Space
  • 12. Carnegie Mellon 12 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Passing an argument to a thread - Pedantic int hist[N] = {0}; int main(int argc, char *argv[]) { long i; pthread_t tids[N]; for (i = 0; i < N; i++) { long* p = Malloc(sizeof(long)); *p = i; Pthread_create(&tids[i], NULL, thread, (void *)p); } for (i = 0; i < N; i++) Pthread_join(tids[i], NULL); check(); } void *thread(void *vargp) { hist[*(long *)vargp] += 1; Free(vargp); return NULL; } void check(void) { for (int i=0; i<N; i++) { if (hist[i] != 1) { printf("Failed at %dn", i); exit(-1); } } printf("OKn"); }
  • 13. Carnegie Mellon 13 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Passing an argument to a thread - Pedantic int hist[N] = {0}; int main(int argc, char *argv[]) { long i; pthread_t tids[N]; for (i = 0; i < N; i++) { long* p = Malloc(sizeof(long)); *p = i; Pthread_create(&tids[i], NULL, thread, (void *)p); } for (i = 0; i < N; i++) Pthread_join(tids[i], NULL); check(); } void *thread(void *vargp) { hist[*(long *)vargp] += 1; Free(vargp); return NULL; } • Use malloc to create a per thread heap allocated place in memory for the argument • Remember to free in thread! • Producer-consumer pattern
  • 14. Carnegie Mellon 14 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition int hist[N] = {0}; int main(int argc, char *argv[]) { long i; pthread_t tids[N]; for (i = 0; i < N; i++) Pthread_create(&tids[i], NULL, thread, (void *)i); for (i = 0; i < N; i++) Pthread_join(tids[i], NULL); check(); } void *thread(void *vargp) { hist[(long)vargp] += 1; return NULL; } Passing an argument to a thread – Also OK! • Ok to Use cast since sizeof(long) <= sizeof(void*) • Cast does NOT change bits
  • 15. Carnegie Mellon 15 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition int hist[N] = {0}; int main(int argc, char *argv[]) { long i; pthread_t tids[N]; for (i = 0; i < N; i++) Pthread_create(&tids[i], NULL, thread, (void *)&i); for (i = 0; i < N; i++) Pthread_join(tids[i], NULL); check(); } void *thread(void *vargp) { hist[*(long*)vargp] += 1; return NULL; } Passing an argument to a thread – WRONG! • &i points to same location for all threads! • Creates a data race!
  • 16. Carnegie Mellon 16 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Three Ways to Pass Thread Arg  Malloc/free  Producer malloc’s space, passes pointer to pthread_create  Consumer dereferences pointer  Ptr to stack slot  Producer passes address to producer’s stack in pthread_create  Consumer dereferences pointer  Cast of int  Producer casts an int/long to address in pthread_create  Consumer casts void* argument back to int/long
  • 17. Carnegie Mellon 17 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Example Program to Illustrate Sharing char **ptr; /* global var */ int main(int argc, char *argv[]) { long i; pthread_t tid; char *msgs[2] = { "Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); } void *thread(void *vargp) { long myid = (long)vargp; static int cnt = 0; printf("[%ld]: %s (cnt=%d)n", myid, ptr[myid], ++cnt); return NULL; } Peer threads reference main thread’s stack indirectly through global ptr variable sharing.c A common way to pass a single argument to a thread routine
  • 18. Carnegie Mellon 18 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Shared Variables in Threaded C Programs  Question: Which variables in a threaded C program are shared?  The answer is not as simple as “global variables are shared” and “stack variables are private”  Def: A variable x is shared if and only if multiple threads reference some instance of x.  Requires answers to the following questions:  What is the memory model for threads?  How are instances of variables mapped to memory?  How many threads might reference each of these instances?
  • 19. Carnegie Mellon 19 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Mapping Variable Instances to Memory  Global variables  Def: Variable declared outside of a function  Virtual memory contains exactly one instance of any global variable  Local variables  Def: Variable declared inside function without static attribute  Each thread stack contains one instance of each local variable  Local static variables  Def: Variable declared inside function with the static attribute  Virtual memory contains exactly one instance of any local static variable.
  • 20. Carnegie Mellon 20 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition char **ptr; /* global var */ int main(int main, char *argv[]) { long i; pthread_t tid; char *msgs[2] = { "Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); } void *thread(void *vargp) { long myid = (long)vargp; static int cnt = 0; printf("[%ld]: %s (cnt=%d)n", myid, ptr[myid], ++cnt); return NULL; } Mapping Variable Instances to Memory sharing.c
  • 21. Carnegie Mellon 21 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition char **ptr; /* global var */ int main(int main, char *argv[]) { long i; pthread_t tid; char *msgs[2] = { "Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); } void *thread(void *vargp) { long myid = (long)vargp; static int cnt = 0; printf("[%ld]: %s (cnt=%d)n", myid, ptr[myid], ++cnt); return NULL; } Mapping Variable Instances to Memory Global var: 1 instance (ptr [data]) Local static var: 1 instance (cnt [data]) Local vars: 1 instance (i.m, msgs.m, tid.m) Local var: 2 instances ( myid.p0 [peer thread 0’s stack], myid.p1 [peer thread 1’s stack] ) sharing.c
  • 22. Carnegie Mellon 22 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Shared Variable Analysis  Which variables are shared?  Answer: A variable x is shared iff multiple threads reference at least one instance of x. Thus:  ptr, cnt, and msgs are shared  i and myid are not shared Variable Referenced by Referenced by Referenced by instance main thread? peer thread 0? peer thread 1? ptr cnt i.m msgs.m myid.p0 myid.p1 yes yes yes no yes yes yes no no yes yes yes no yes no no no yes char **ptr; /* global var */ int main(int main, char *argv[]) { long i; pthread_t tid; char *msgs[2] = {"Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread,(void *)i); Pthread_exit(NULL);} void *thread(void *vargp) { long myid = (long)vargp; static int cnt = 0; printf("[%ld]: %s (cnt=%d)n", myid, ptr[myid], ++cnt); return NULL; }
  • 23. Carnegie Mellon 23 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Shared Variable Analysis  Which variables are shared?  Answer: A variable x is shared iff multiple threads reference at least one instance of x. Thus:  ptr, cnt, and msgs are shared  i and myid are not shared Variable Referenced by Referenced by Referenced by instance main thread? peer thread 0? peer thread 1? ptr cnt i.m msgs.m myid.p0 myid.p1 yes yes yes no yes yes yes no no yes yes yes no yes no no no yes
  • 24. Carnegie Mellon 24 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Synchronizing Threads  Shared variables are handy...  …but introduce the possibility of nasty synchronization errors.
  • 25. Carnegie Mellon 25 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition badcnt.c: Improper Synchronization /* Global shared variable */ volatile long cnt = 0; /* Counter */ int main(int argc, char **argv) { long niters; pthread_t tid1, tid2; niters = atoi(argv[1]); Pthread_create(&tid1, NULL, thread, &niters); Pthread_create(&tid2, NULL, thread, &niters); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL); /* Check result */ if (cnt != (2 * niters)) printf("BOOM! cnt=%ldn", cnt); else printf("OK cnt=%ldn", cnt); exit(0); } /* Thread routine */ void *thread(void *vargp) { long i, niters = *((long *)vargp); for (i = 0; i < niters; i++) cnt++; return NULL; } linux> ./badcnt 10000 OK cnt=20000 linux> ./badcnt 10000 BOOM! cnt=13051 linux> cnt should equal 20,000. What went wrong? badcnt.c
  • 26. Carnegie Mellon 26 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Assembly Code for Counter Loop for (i = 0; i < niters; i++) cnt++; C code for counter loop in thread i movq (%rdi), %rcx testq %rcx,%rcx jle .L2 movl $0, %eax .L3: movq cnt(%rip),%rdx addq $1, %rdx movq %rdx, cnt(%rip) addq $1, %rax cmpq %rcx, %rax jne .L3 .L2: Hi : Head Ti : Tail Li : Load cnt Ui : Update cnt Si : Store cnt Asm code for thread i
  • 27. Carnegie Mellon 27 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Concurrent Execution  Key idea: In general, any sequentially consistent* interleaving is possible, but some give an unexpected result!  Ii denotes that thread i executes instruction I  %rdxi is the content of %rdx in thread i’s context H1 L1 U1 S1 H2 L2 U2 S2 T2 T1 1 1 1 1 2 2 2 2 2 1 - 0 1 1 - - - - - 1 0 0 0 1 1 1 1 2 2 2 i (thread) instri cnt %rdx1 OK - - - - - 1 2 2 2 - %rdx2 *For now. In reality, on x86 even non-sequentially consistent interleavings are possible
  • 28. Carnegie Mellon 28 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Concurrent Execution  Key idea: In general, any sequentially consistent interleaving is possible, but some give an unexpected result!  Ii denotes that thread i executes instruction I  %rdxi is the content of %rdx in thread i’s context H1 L1 U1 S1 H2 L2 U2 S2 T2 T1 1 1 1 1 2 2 2 2 2 1 - 0 1 1 - - - - - 1 0 0 0 1 1 1 1 2 2 2 i (thread) instri cnt %rdx1 OK - - - - - 1 2 2 2 - %rdx2 Thread 1 critical section Thread 2 critical section
  • 29. Carnegie Mellon 29 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Concurrent Execution (cont)  Incorrect ordering: two threads increment the counter, but the result is 1 instead of 2 H1 L1 U1 H2 L2 S1 T1 U2 S2 T2 1 1 1 2 2 1 1 2 2 2 - 0 1 - - 1 1 - - - 0 0 0 0 0 1 1 1 1 1 i (thread) instri cnt %rdx1 - - - - 0 - - 1 1 1 %rdx2 Oops!
  • 30. Carnegie Mellon 30 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Concurrent Execution (cont)  How about this ordering?  We can analyze the behavior using a progress graph H1 L1 H2 L2 U2 S2 U1 S1 T1 T2 1 1 2 2 2 2 1 1 1 2 i (thread) instri cnt %rdx1 %rdx2 0 0 0 1 1 1 1 1 1 1 Oops! 1
  • 31. Carnegie Mellon 31 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Progress Graphs 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. (L1, S2) H1 L1 U1 S1 T1 H2 L2 U2 S2 T2 Thread 1 Thread 2
  • 32. Carnegie Mellon 32 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Trajectories in Progress Graphs A trajectory is a sequence of legal state transitions that describes one possible concurrent execution of the threads. Example: H1, L1, U1, H2, L2, S1, T1, U2, S2, T2 H1 L1 U1 S1 T1 H2 L2 U2 S2 T2 Thread 1 Thread 2
  • 33. Carnegie Mellon 33 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Trajectories in Progress Graphs A trajectory is a sequence of legal state transitions that describes one possible concurrent execution of the threads. Example: H1, L1, U1, H2, L2, S1, T1, U2, S2, T2 H1 L1 U1 S1 T1 H2 L2 U2 S2 T2 Thread 1 Thread 2
  • 34. Carnegie Mellon 34 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Critical Sections and Unsafe Regions L, U, and S form a critical section with respect to the shared variable cnt Instructions in critical sections (wrt some shared variable) should not be interleaved Sets of states where such interleaving occurs form unsafe regions H1 L1 U1 S1 T1 H2 L2 U2 S2 T2 Thread 1 Thread 2 critical section wrt cnt critical section wrt cnt Unsafe region
  • 35. Carnegie Mellon 35 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Critical Sections and Unsafe Regions H1 L1 U1 S1 T1 H2 L2 U2 S2 T2 Thread 1 Thread 2 critical section wrt cnt critical section wrt cnt Unsafe region Def: A trajectory is safe iff it does not enter any unsafe region Claim: A trajectory is correct (wrt cnt) iff it is safe unsafe safe
  • 36. Carnegie Mellon 36 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition badcnt.c: Improper Synchronization /* Global shared variable */ volatile long cnt = 0; /* Counter */ int main(int argc, char **argv) { long niters; pthread_t tid1, tid2; niters = atoi(argv[1]); Pthread_create(&tid1, NULL, thread, &niters); Pthread_create(&tid2, NULL, thread, &niters); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL); /* Check result */ if (cnt != (2 * niters)) printf("BOOM! cnt=%ldn", cnt); else printf("OK cnt=%ldn", cnt); exit(0); } /* Thread routine */ void *thread(void *vargp) { long i, niters = *((long *)vargp); for (i = 0; i < niters; i++) cnt++; return NULL; } badcnt.c Variable main thread1 thread2 cnt niters.m tid1.m i.1 i.2 niters.1 niters.2
  • 37. Carnegie Mellon 37 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition badcnt.c: Improper Synchronization /* Global shared variable */ volatile long cnt = 0; /* Counter */ int main(int argc, char **argv) { long niters; pthread_t tid1, tid2; niters = atoi(argv[1]); Pthread_create(&tid1, NULL, thread, &niters); Pthread_create(&tid2, NULL, thread, &niters); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL); /* Check result */ if (cnt != (2 * niters)) printf("BOOM! cnt=%ldn", cnt); else printf("OK cnt=%ldn", cnt); exit(0); } /* Thread routine */ void *thread(void *vargp) { long i, niters = *((long *)vargp); for (i = 0; i < niters; i++) cnt++; return NULL; } badcnt.c Variable main thread1 thread2 cnt yes* yes yes niters.m yes no no tid1.m yes no no i.1 no yes no i.2 no no yes niters.1 no yes no niters.2 no no yes
  • 38. Carnegie Mellon 38 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Today  Threads review  Sharing  Mutual exclusion
  • 39. Carnegie Mellon 39 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Enforcing Mutual Exclusion  Question: How can we guarantee a safe trajectory?  Answer: We must synchronize the execution of the threads so that they can never have an unsafe trajectory.  i.e., need to guarantee mutually exclusive access for each critical section.  Classic solution:  Mutex (pthreads)  Semaphores (Edsger Dijkstra)  Other approaches (out of our scope)  Condition variables (pthreads)  Monitors (Java)
  • 40. Carnegie Mellon 40 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Semaphores  Semaphore: non-negative global integer synchronization variable. Manipulated by P and V operations.  P(s)  If s is nonzero, then decrement s by 1 and return immediately.  Test and decrement operations occur atomically (indivisibly)  If s is zero, then suspend thread until s becomes nonzero and the thread is restarted by a V operation.  After restarting, the P operation decrements s and returns control to the caller.  V(s):  Increment s by 1.  Increment operation occurs atomically  If there are any threads blocked in a P operation waiting for s to become non- zero, then restart exactly one of those threads, which then completes its P operation by decrementing s.  Semaphore invariant: (s >= 0)
  • 41. Carnegie Mellon 41 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Semaphores  Semaphore: non-negative global integer synchronization variable  Manipulated by P and V operations:  P(s): [ while (s == 0) wait(); s--; ]  Dutch for "Proberen" (test)  V(s): [ s++; ]  Dutch for "Verhogen" (increment)  OS kernel guarantees that operations between brackets [ ] are executed indivisibly  Only one P or V operation at a time can modify s.  When while loop in P terminates, only that P can decrement s  Semaphore invariant: (s >= 0)
  • 42. Carnegie Mellon 42 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition C Semaphore Operations Pthreads functions: #include <semaphore.h> int sem_init(sem_t *s, 0, unsigned int val);} /* s = val */ int sem_wait(sem_t *s); /* P(s) */ int sem_post(sem_t *s); /* V(s) */ CS:APP wrapper functions: #include "csapp.h” void P(sem_t *s); /* Wrapper function for sem_wait */ void V(sem_t *s); /* Wrapper function for sem_post */
  • 43. Carnegie Mellon 43 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition badcnt.c: Improper Synchronization /* Global shared variable */ volatile long cnt = 0; /* Counter */ int main(int argc, char **argv) { long niters; pthread_t tid1, tid2; niters = atoi(argv[1]); Pthread_create(&tid1, NULL, thread, &niters); Pthread_create(&tid2, NULL, thread, &niters); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL); /* Check result */ if (cnt != (2 * niters)) printf("BOOM! cnt=%ldn", cnt); else printf("OK cnt=%ldn", cnt); exit(0); } /* Thread routine */ void *thread(void *vargp) { long i, niters = *((long *)vargp); for (i = 0; i < niters; i++) cnt++; return NULL; } How can we fix this using synchronization? badcnt.c
  • 44. Carnegie Mellon 44 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition MUTual EXclusion (mutex)  Mutex: boolean synchronization variable  enum {locked = 0, unlocked = 1}  lock(m)  If the mutex is currently not locked, lock it and return  Otherwise, wait (spinning, yielding, etc) and retry  unlock(m)  Update the mutex state to unlocked
  • 45. Carnegie Mellon 46 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Using Semaphores for Mutual Exclusion ⬛ Basic idea: ▪ Associate a unique semaphore mutex, initially 1, with each shared variable (or related set of shared variables). ▪ Surround corresponding critical sections with P(mutex) and V(mutex) operations. ⬛ Terminology: ▪ Binary semaphore: semaphore whose value is always 0 or 1 ▪ Mutex: binary semaphore used for mutual exclusion ▪ P operation: “locking” the mutex ▪ V operation: “unlocking” or “releasing” the mutex ▪ “Holding” a mutex: locked and not yet unlocked. ▪ Counting semaphore: used as a counter for set of available resources.
  • 46. Carnegie Mellon 47 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition goodcnt.c: Proper Synchronization ⬛ Define and initialize a mutex for the shared variable cnt: volatile long cnt = 0; /* Counter */ sem_t mutex; /* Semaphore that protects cnt */ sem_init(&mutex, 0, 1); /* mutex = 1 */ ⬛ Surround critical section with P and V: for (i = 0; i < niters; i++) { P(&mutex); cnt++; V(&mutex); } linux> ./goodcnt 10000 OK cnt=20000 linux> ./goodcnt 10000 OK cnt=20000 linux> Warning: It’s orders of magnitude slower than badcnt.c. goodcnt.c
  • 47. Carnegie Mellon 48 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition goodcnt.c: Proper Synchronization ⬛ Define and initialize a mutex for the shared variable cnt: volatile long cnt = 0; /* Counter */ sem_t mutex; /* Semaphore that protects cnt */ sem_init(&mutex, 0, 1); /* mutex = 1 */ ⬛ Surround critical section with P and V: for (i = 0; i < niters; i++) { P(&mutex); cnt++; V(&mutex); } linux> ./goodcnt 10000 OK cnt=20000 linux> ./goodcnt 10000 OK cnt=20000 linux> Warning: It’s orders of magnitude slower than badcnt.c. goodcnt.c OK cnt=2000000 BOOM! cnt=1036525 Slowdown real 0m0.138s 0m0.007s 20X user 0m0.120s 0m0.008s 15X sys 0m0.108s 0m0.000s NaN And slower means much slower!
  • 48. Carnegie Mellon 49 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Why Mutexes Work Provide mutually exclusive access to shared variable by surrounding critical section with lock and unlock operations H1 lo(m) un(m) T1 Thread 1 Thread 2 L1 U1 S1 H2 lo(m) un(m) T2 L2 U2 S2 Initially m = 1 1 0 0 0 0 -1 Unsafe region
  • 49. Carnegie Mellon 50 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Why Mutexes Work Provide mutually exclusive access to shared variable by surrounding critical section with lock and unlock operations Mutex invariant creates a forbidden region that encloses unsafe region and that cannot be entered by any trajectory. H1 lo(m) un(m) T1 Thread 1 Thread 2 L1 U1 S1 H2 lo(m) un(m) T2 L2 U2 S2 Initially m = 1 1 0 0 0 0 -1 Unsafe region
  • 50. Carnegie Mellon 51 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Why Mutexes Work Provide mutually exclusive access to shared variable by surrounding critical section with lock and unlock operations Mutex invariant creates a forbidden region that encloses unsafe region and that cannot be entered by any trajectory. H1 lo(m) un(m) T1 Thread 1 Thread 2 L1 U1 S1 H2 lo(m) un(m) T2 L2 U2 S2 Initially m = 1 1 0 0 0 0 Unsafe region 0 1 0
  • 51. Carnegie Mellon 52 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Unsafe region Why Mutexes Work Provide mutually exclusive access to shared variable by surrounding critical section with lock and unlock operations Mutex invariant creates a forbidden region that encloses unsafe region and that cannot be entered by any trajectory. H1 lo(m) un(m) T1 Thread 1 Thread 2 L1 U1 S1 H2 lo(m) un(m) T2 L2 U2 S2 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1 0 0 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 Initially m = 1 Forbidden region
  • 52. Carnegie Mellon 53 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Summary  Programmers need a clear model of how variables are shared by threads.  Variables shared by multiple threads must be protected to ensure mutually exclusive access.  Semaphores are a fundamental mechanism for enforcing mutual exclusion.
  • 53. Carnegie Mellon 54 Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition Appendix: Binary Semaphores vs. Mutexes (not test material, just fyi!)  Binary semaphore: semaphore initialized with a value of 1  Both binary semaphores and mutexes can be used to guarantee mutual exclusion  Main difference is ownership  Mutexes must be unlocked by the thread who owned them previously  Binary semaphores can be signaled/incremented (V) by a thread who did not decrement (P) them  As long as you use binary semaphores in the following way in all threads, they can be used as a mutex P(&sem); // critical section V(&sem); They are also implemented differently but that’s out of the scope for this class… (covered in 15-410)