lecture 2: operating system structureheechul/courses/eecs678/f17/slides/w4.sync.pdf · peterson’s...
TRANSCRIPT
Synchronization
1
Disclaimer: some slides are adopted from the book authors’ slides with permission
Recap: Thread
• What is it?
– Independent flow of control
• What does it need (thread private)?
– Stack
• What for?
– Lightweight programming construct for concurrent activities
• How to implement?
– Kernel thread vs. user thread
2
Recap: Process vs. Thread
3
Figure source: https://computing.llnl.gov/tutorials/pthreads/
Recap: Multi-threads vs. Multi-processes
• Multi-processes
– (+) protection
– (-) performance (?)
• Multi-threads
– (+) performance
– (-) protection
4
Process-per-tab
Single-process multi-threads
Agenda
• Mutual exclusion
– Peterson’s algorithm (Software)
– Synchronization instructions (Hardware)
• Spinlock
• High-level synchronization mechanisms
– Mutex
– Semaphore
– Monitor
5
Producer/Consumer
6
Buffer[10]ProducerThread
ConsumerThread
Producer/Consumer
7
while (true){
/* wait if buffer full */while (counter == 10);
/* produce data */buffer[in] = sdata;in = (in + 1) % 10;
/* update number of items in buffer */
counter++;}
while (true){
/* wait if buffer empty */while (counter == 0);
/* consume data */sdata = buffer[out];out = (out + 1) % 10;
/* update number of items in buffer */
counter--;}
Producer Consumer
Producer/Consumer
8
while (true){
/* wait if buffer full */while (counter == 10);
/* produce data */buffer[in] = sdata;in = (in + 1) % 10;
/* update number of items in buffer */
R1 = load (counter);R1 = R1 + 1;counter = store (R1);
}
while (true){
/* wait if buffer empty */while (counter == 0);
/* consume data */sdata = buffer[out];out = (out + 1) % 10;
/* update number of items in buffer */
R2 = load (counter);R2 = R2 – 1;counter = store (R2);
}
Producer Consumer
Check Yourself
int count = 0;int main(){
count = count + 1;return count;
}
$ gcc –O2 –S sync.c
…movl count(%rip), %eaxaddl $1, %eaxmovl %eax, count(%rip)…
Race Condition
• What are the possible outcome?
10
R1 = load (counter);R1 = R1 + 1;counter = store (R1);
R2 = load (counter);R2 = R2 – 1;counter = store (R2);
Thread 1 Thread 2
Initial condition: counter = 5
Race Condition
• Why this happens?
11
R1 = load (counter);R1 = R1 + 1;counter = store (R1);R2 = load (counter);R2 = R2 – 1;counter = store (R2);
R1 = load (counter);R1 = R1 + 1;R2 = load (counter);R2 = R2 – 1;counter = store (R1);counter = store (R2);
R1 = load (counter);R1 = R1 + 1;R2 = load (counter);R2 = R2 – 1;counter = store (R2);counter = store (R1);
counter = 5 counter = 4 counter = 6
Initial condition: counter = 5
Race Condition
• A situation when two or more threads read and write shared data at the same time
• Correctness depends on the execution order
• How to prevent race conditions?
12
R1 = load (counter);R1 = R1 + 1;counter = store (R1);
R2 = load (counter);R2 = R2 – 1;counter = store (R2);
Thread 1 Thread 2read
write
Critical Section
• Code sections of potential race conditions
13
Do something..R1 = load (counter);R1 = R1 + 1;counter = store (R1);...Do something
Do something..R2 = load (counter);R2 = R2 – 1;counter = store (R2);..Do something
Thread 1 Thread 2
Critical sections
Solution Requirements
• Mutual Exclusion
– If a thread executes its critical section, no other threads can enter their critical sections
• Progress
– If no one executes a critical section, someone can enter its critical section
• Bounded waiting
– Waiting (time/number) must be bounded
14
Simple Solution (?): Use a Flag
• Mutual exclusion is not guaranteed
15
// wait while (in_cs)
;// enter critical sectionin_cs = true;
Do something
// exit critical sectionin_cs = false;
T1 T2while(in_cs){};
while(in_cs){};in_cs = true;
in_cs = true;//enter //enter
Peterson’s Solution
• Software solution (no h/w support)
• Two process solution
– Multi-process extension exists
• The two processes share two variables:
– int turn;
• The variable turn indicates whose turn it is to enter the critical section
– Boolean flag[2]
• The flag array is used to indicate if a process is ready to enter the critical section.
16
Peterson’s Solution
• Solution meets all three requirements– Mutual exclusion: P0 and P1 cannot be in the critical section at the same time
– Progress: if P0 does not want to enter critical region, P1 does no waiting
– Bounded waiting: process waits for at most one turn
17
do { flag[0] = TRUE; turn = 1; while (flag[1] && turn==1){}; // critical section
flag[0] = FALSE;
// remainder section} while (TRUE)
do { flag[1] = TRUE; turn = 0; while (flag[0] && turn==0){}; // critical section
flag[1] = FALSE;
// remainder section} while (TRUE)
Thread 1 Thread 2
Peterson’s Solution
• Limitations
– Only supports two processes• generalizing for more than two processes has been achieved, but
not very efficient
– Assumes LOAD and STORE instructions are atomic• In reality, no guarantees
– Assumes memory accesses are not reordered• compiler re-orders instructions (gcc –O2, -O3, …)
• Out-of-order processor re-orders instructions
18
Reordering by the CPU
• Possible values of R1 and R2?– 0,1
– 1,0
– 1,1
– 0,0 possible on PC
19
Thread 0
X = 1R1 = Y
Thread 1
Y = 1R2 = X
Thread 0
R1 = Y
X = 1
Thread 1
R2 = X
Y = 1
Initially X = Y = 0
Summary
• Peterson’s algorithm
– Software-only solution
• Turn based
– Pros
• No hardware support
• Satisfy all requirements– Mutual exclusion, progress, bounded waiting
– Cons
• Complicated
• Assume program order
• May not work on out-of-order processors20
Recap
• Race condition
– A situation when two or more threads read and write shared data at the same time
– Correctness depends on the execution order
• Critical section
– Code sections of potential race conditions
• Mutual exclusion
– If a thread executes its critical section, no other threads can enter their critical sections
21
Recap
• Peterson’s algorithm
– Software-only solution
• Turn based
– Pros
• No hardware support
• Satisfy all requirements– Mutual exclusion, progress, bounded waiting
– Cons
• Complicated
• Assume program order
• May not work on out-of-order processors22
Today
• Hardware support
– Synchronization instructions
• Lock
– Spinlock
–Mutex
23
Lock
• General solution
– Protect critical section via a lock
– Acquire on enter, release on exit
24
do {acquire lock;
critical section
release lock;
remainder section
} while(TRUE);
How to Implement a Lock?
• Unicore processor
– No true concurrencyone thread at a time
– Threads are interrupted by the OS• scheduling events: timer interrupt, device interrupts
• Disabling interrupt
– Threads can’t be interrupted
25
do {
disable interrupts;critical section
enable interrupts;
remainder section} while(TRUE);
How to Implement a Lock?
• Multicore processor
– True concurrency• More than one active threads sharing memory
– Disabling interrupts don’t solve the problem• More than one threads are executing at a time
• Hardware support
– Synchronization instructions• Atomic test&set instruction
• Atomic compare&swap instruction
• What do we mean by atomic?
– All or nothing
26
TestAndSet Instruction
• Pseudo code
27
boolean TestAndSet (boolean *target){
boolean rv = *target;*target = TRUE;return rv:
}
Spinlock using TestAndSet
28
int mutex;init_lock (&mutex);
do {
lock (&mutex);critical section
unlock (&mutex);
remainder section} while(TRUE);
void init_lock (int *mutex){
*mutex = 0;}
void lock (int *mutex){
while(TestAndSet(mutex));
}
void unlock (int *mutex){
*mutex = 0;}
CAS (Compare & Swap) Instruction
• Pseudo code
29
int CAS(int *value, int oldval, int newval) {
int temp = *value; if (*value == oldval)
*value = newval; return temp;
}
Spinlock using CAS
30
int mutex;init_lock (&mutex);
do {
lock (&mutex);critical section
unlock (&mutex);
remainder section} while(TRUE);
void init_lock (int *mutex) {*mutex = 0;
}
void lock (int *mutex) {while(CAS(&mutex, 0, 1) != 0);
}
void unlock (int *mutex) {*mutex = 0;
}
What’s Wrong With Spinlocks?
• Very wasteful
– Waiting thread continues to use CPU cycles
– While doing absolutely nothing but wait
– 100% CPU utilization, but no useful work done
– Power consumption, fan noise, …
• Useful when
– You hold the lock only briefly
• Otherwise
– A better solution is needed
31
Mutex – Blocking Lock
• Instead of spinning
– Let the thread sleep
• There can be multiple waiting threads
– In the meantime, let other threads use the CPU
– When the lock is released, wake-up one thread
• Pick one if there multiple threads were waiting
32
33
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){…
while(TestAndSet(&lock->value)) {……………
}… }
void mutex_unlock (mutex_t *lock){…
lock->value = 0;………}
Thread waiting list
More reading: mutex.c in Linux
To protect waiting list
34
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){…
while(TestAndSet(&lock->value)) {current->state = WAITING;list_add(&lock->wait_list, current);
…schedule();
…}
…}
void mutex_unlock (mutex_t *lock){…
lock->value = 0;………}
Thread waiting list
Sleep or schedule another thread
Thread state changeAdd the current thread to the waiting list
More reading: mutex.c in Linux
To protect waiting list
35
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){
spin_lock(&lock->wait_lock);while(TestAndSet(&lock->value)) {
current->state = WAITING;list_add(&lock->wait_list, current);
…schedule();
…}spin_unlock(&lock->wait_lock);
}
void mutex_unlock (mutex_t *lock){…
lock->value = 0;………}
Thread waiting list
Sleep or schedule another thread
Thread state changeAdd the current thread to the waiting list
More reading: mutex.c in Linux
To protect waiting list
36
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){
spin_lock(&lock->wait_lock);while(TestAndSet(&lock->value)) {
current->state = WAITING;list_add(&lock->wait_list, current);spin_unlock(&lock->wait_lock);schedule();spin_lock(&lock->wait_lock);
}spin_unlock(&lock->wait_lock);
}
void mutex_unlock (mutex_t *lock){…
lock->value = 0;………}
Thread waiting list
Sleep or schedule another thread
Thread state changeAdd the current thread to the waiting list
More reading: mutex.c in Linux
To protect waiting list
37
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){
spin_lock(&lock->wait_lock);while(TestAndSet(&lock->value)) {
current->state = WAITING;list_add(&lock->wait_list, current);spin_unlock(&lock->wait_lock);schedule();spin_lock(&lock->wait_lock);
}spin_unlock(&lock->wait_lock);
}
void mutex_unlock (mutex_t *lock){…
lock->value = 0;if (!list_empty(&lock->wait_list))
wake_up_process(&lock->wait_list)…}
Thread waiting list
Sleep or schedule another thread
Thread state changeAdd the current thread to the waiting list
Someone is waiting for the lockWake-up a waiting thread
More reading: mutex.c in Linux
To protect waiting list
38
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){
spin_lock(&lock->wait_lock);while(TestAndSet(&lock->value)) {
current->state = WAITING;list_add(&lock->wait_list, current);spin_unlock(&lock->wait_lock);schedule();spin_lock(&lock->wait_lock);
}spin_unlock(&lock->wait_lock);
}
void mutex_unlock (mutex_t *lock){
spin_lock(&lock->wait_lock);lock->value = 0;if (!list_empty(&lock->wait_list))
wake_up_process(&lock->wait_list)spin_unlock(&lock->wait_lock);
}
Thread waiting list
Sleep or schedule another thread
Thread state changeAdd the current thread to the waiting list
Someone is waiting for the lockWake-up a waiting thread
More reading: mutex.c in Linux
To protect waiting list
void mutex_init (mutex_t *lock){
lock->value = 0;list_init(&lock->wait_list);spin_lock_init(&lock->wait_lock);
}
void mutex_lock (mutex_t *lock){
while(TestAndSet(&lock->value)) {current->state = WAITING;spin_lock(&lock->wait_lock);list_add(&lock->wait_list, current);spin_unlock(&lock->wait_lock);schedule();
}}
void mutex_unlock (mutex_t *lock){
spin_lock(&lock->wait_lock);lock->value = 0;if (!list_empty(&lock->wait_list))
wake_up_process(&lock->wait_list)spin_unlock(&lock->wait_lock);
}
More reading: mutex.c in Linux
T2
mutex_locklock->value = 1
spin_lock()list_add()spin_unlock()schedule()
T1
mutex_unlock
spin_lock()lock->value = 0// list is empty// do nothingspin_unlock()Correct?
Q. Who will wake-up T2?A. Nobody!!!
Summary
• Synchronization instructions
– test&set, compare&swap
– All or nothing
• Spinlock
– Spin on wait
– Good for short critical section but can be wasteful
• Mutex
– Block (sleep) on wait
– Good for long critical section but bad for short one
40