0% found this document useful (0 votes)
38 views74 pages

Lect5 424 002

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views74 pages

Lect5 424 002

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 74

CS162

Operating Systems and


Systems Programming
Lecture 5

Concurrency and Mutual Exclusion


February 4th, 202
Prof. John Kubiatowic
http://cs162.eecs.Berkeley.edu

Acknowledgments: Lecture slides are from the Operating Systems course


taught by John Kubiatowicz at Berkeley, with few minor updates/changes.
When slides are obtained from other sources, a reference will be noted on the
bottom of that slide, in which case a full list of references is provided on the last
slide.



0



Recall: Fork, Wait, and (optional) Exec
cpid = fork()
if (cpid > 0) { // Parent Proces
mypid = getpid()
printf("[%d] parent of [%d]\n", mypid, cpid)
tcpid = wait(&status);
printf("[%d] bye %d\n", mypid, tcpid)
} else if (cpid == 0) { // Child Process
mypid = getpid()
printf("[%d] child\n", mypid)
execl(filename,(char *)0); // Opt: start new progra
} else { // Error! }
• Return value from Fork: intege
– When > 0: return value is pid of new child (Running in Parent
– When = 0: Running in new Child proces
– When < 0: Error! Must handle someho
• Wait() system call: wait for next child to exi
– Return value is PID of terminating chil
– Argument is pointer to integer variable to hold exit statu
• Exec() family of calls: replace process with new executable
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 2
;

Recall: Internal Events


• Blocking on I/
– The act of requesting I/O implicitly yields the CP
• Waiting on a “signal” from other threa
– Thread asks to wait and thus yields the CP
• Thread executes a yield(
– Thread volunteers to give up CP

computePI() {
while(TRUE) {
ComputeNextDigit();
yield();
}
}

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 3




Recall: Stack for Yielding Thread


ComputePI

Stack growth
yield
Trap to OS
kernel_yield
run_new_thread
switch

• How do we run a new thread


run_new_thread() {
newThread = PickNewThread();
switch(curThread, newThread);
ThreadHouseKeeping(); /* Do any cleanup */
}
• How does dispatcher switch to a new thread
– Save anything next thread may trash: PC, regs, stack pointe
– Maintain isolation for each thread
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 4








Recall: Multithreaded Stack Switching


• Consider the following code
blocks
proc A() { Thread S Thread T

B(); A A

Stack growth
}
B(while) B(while)
proc B() {
while(TRUE) { yield yield
yield();
run_new_thread run_new_thread
}
} switch switch
• Suppose we have 2 threads
– Threads S and T
Thread S's switch returns to Thread
T's (and vice versa)
1/30/20 Kubiatowicz CS162 ©UCB Spring 2020 5















:

Goals for Today


• Finish discussion of Thread
• Concurrency and need for Synchronization Operation
• Basic Synchronization through Lock
• Initial Lock Implementations

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 6


s

What happens when thread blocks on I/O?

CopyFile

Stack growth
read
Trap to OS
kernel_read
run_new_thread
switch

• What happens when a thread requests a block of data from the


file system
– User code invokes a system cal
– Read operation is initiate
– Run new thread/switc
• Thread communication simila
– Wait for Signal/Joi
– Networking
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 7
?

External Events
• What happens if thread never does any I/O, never waits, and
never yields control
– Could the ComputePI program grab all resources and never
release the processor
» What if it didn’t print to console
– Must find way that dispatcher can regain control

• Answer: utilize external event


– Interrupts: signals from hardware or software that stop the
running code and jump to kerne
– Timer: like an alarm clock that goes off every some millisecond

• If we make sure that external events occur frequently enough,


can ensure dispatcher runs

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 8


?

Interrupt Controller

Interrupt Mask
Priority Encoder
IntID
CPU
Interrupt Int Disable

Timer
Softwar Control
Interrupt NMI
Network
• Interrupts invoked with interrupt lines from device
• Interrupt controller chooses interrupt request to hono
– Interrupt identity specified with ID line
– Mask enables/disables interrupt
– Priority encoder picks highest enabled interrupt
– Software Interrupt Set/Cleared by Softwar
• CPU can disable all interrupts with internal fla
• Non-Maskable Interrupt line (NMI) can’t be disabled
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 9
e

Example: Network Interrupt


Raise priority
(set mask
... ved Int Reenable All Int
a l e
s Al d Save registers
P ble l Mo
C
add $r1,$r2,$r3
External Interrupt

“Interrupt Handler”
isa rne
subi $r4,$r1,#4 Dispatch to Handler
slli $r4,$r4,#2 D e …
K Transfer Network
...
Packet from
Pipeline Flush hardware

...
to Kernel Buffers
l $r2,0($r4) …
l $r3,4($r4) Restore registers
Re able r Mo
En Use
sto al de
ad $r2,$r2,$r3 Clear current In
re l In
s 8($r4),$r2 Disable All Int
PC t
... Restore priority
(clear Mask
RTI
• An interrupt is a hardware-invoked context switc
– No separate step to choose what to run nex
– Always run the interrupt handler immediately
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 10
w
w
w

Use of Timer Interrupt to Return Control


• Solution to our dispatcher proble
– Use the timer interrupt to force scheduling decision
Some Routine

Stack growth
Interrupt
TimerInterrupt
run_new_thread
switch

• Timer Interrupt routine

TimerInterrupt() {

DoPeriodicHouseKeeping();

run_new_thread();

}

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 11



:

Hardware context switch support in x86


• Syscall/Intr (U ! K
– PL 3 ! 0;
– TSS " EFLAGS, CS:EIP;
– SS:SP " k-thread stack (TSS PL 0);
– push (old) SS:ESP onto (new) k-stack
– push (old) eflags, cs:eip, <err>
– CS:EIP " <k target handler>
• The
– Handler then saves other regs, et
– Does all its works, possibly choosing other threads,
changing PTBR (CR3)

– kernel thread has set up user GPRs


• iret (K ! U)
– PL 0 ! 3;
– Eflags, CS:EIP " popped off k-stack
– SS:SP " user thread stack (TSS PL 3);

pg 2,942 of 4,922 of x86 reference manual Pintos: tss.c, intr-stubs.S

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 12


n

Pintos: Kernel Crossing on Syscall or Interrupt


Time
user
user stack
code

cs:eip cs:eip cs:eip cs:eip cs:eip


ss:esp ss:esp ss:esp ss:esp ss:esp

PTBR

ready to resume
PTBR PTBR PTBR PTBR
syscall / interrupt

cs:eip cs:eip cs:eip


ss:esp ss:esp ss:esp

processing
kernel

saves

code TCB
TCB TCB TCB TCB

iret
kernel
thread
stack
1/30/20 Kubiatowicz CS162 ©UCB Spring 2020 13

Pintos: Context Switch – Scheduling


Time
user user’
user stack stack
code

Schedule
cs:eip cs:eip cs:eip cs:eip cs:eip
ss:esp ss:esp ss:esp ss:esp’ ss:esp

PTBR’

ready to resume
PTBR PTBR PTBR PTBR’
syscall / interrupt

cs:eip cs:eip cs:eip’


ss:esp ss:esp ss:esp’

processing
kernel

saves

code TCB
TCB TCB TCB TCB

iret
kernel
thread switch kernel threads
stack
Pintos: switch.S
1/30/20 Kubiatowicz CS162 ©UCB Spring 2020 14

ThreadFork(): Create a New Thread

• ThreadFork() is a user-level procedure that creates a new


thread and places it on ready queue

• Arguments to ThreadFork(
– Pointer to application routine (fcnPtr
– Pointer to array of arguments (fcnArgPtr
– Size of stack to allocate

• Implementatio
– Sanity check argument
– Enter Kernel-mode and Sanity Check arguments agai
– Allocate new Stack and TC
– Initialize TCB and place on ready list (Runnable)

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 15


n


B


)

How do we initialize TCB and Stack?


• Initialize Register fields of TC
– Stack pointer made to point at stac
– PC return address ⇒ OS (asm) routine ThreadRoot()
– Two arg registers (say rdi and rsi for x86) initialized to fcnPtr and
fcnArgPtr, respectivel
• Initialize stack data
– No. Important part of stack frame is in registers (ra
– Think of stack frame as just before body of ThreadRoot() really gets
started
ThreadRoot stub

Stack growth
Initial Stack
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 16
?

How does Thread get started?


Other Thread
ThreadRoot

Stack growth A

B(while)

yield

run_new_thread New Thread


switch ThreadRoot stub

• Need to construct a new kernel thread that is ready to run when


switch goes to i
• Note that switch doesn’t know any difference between new or
preexisting thread!
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 17
t

How does a thread get started?

Other Thread
SetupNewThread(tNew)
ThreadRoot

A TCB[tNew].regs.sp = newStackPtr
Stack growth

TCB[tNew].regs.retpc =
B(while) &ThreadRoot
TCB[tNew].regs.r0 = fcnPtr

yield TCB[tNew].regs.r1 = fcnArgPt
}
run_new_thread
New Thread
switch ThreadRoot stub

• How do we make a new thread


– Setup TCB/kernel thread to point at new user stack and ThreadRoot cod
– Put pointers to start function and args in register
– This depends heavily on the calling convention (i.e. RISC-V vs x86
• Eventually, run_new_thread() will select this TCB and return into beginning of
ThreadRoot()
– This really starts the new thread
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 18

What does ThreadRoot() look like?


• ThreadRoot() is the root for the thread routine
ThreadRoot(fcnPTR,fcnArgPtr)
DoStartupHousekeeping()
UserModeSwitch(); /* enter user mode *
Call fcnPtr(fcnArgPtr)
ThreadFinish() ThreadRoot
Thread Code


Stack growth
• Startup Housekeeping *fcnPtr()

– Includes things like recording


start time of threa
– Other statistics Running Stack
• Stack will grow and shrink with
execution of thread
• Final return from thread returns into ThreadRoot()
which calls ThreadFinish(
– ThreadFinish() wake up sleeping threads
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 19
}


)


;


/

Administrivia
• anything?

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 20


Kernel-Supported Threads
• Each thread has a thread control bloc
– CPU registers, including PC, pointer to stac
– Scheduling info: priority, etc
– Pointer to Process control bloc
• OS scheduler uses TCBs, not PCBs

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 21


.

Kernel-Supported User Threads

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 22


User-level Multithreading: pthreads
• int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg)
– thread is created executing start_routine with arg as its sole
argument. (return is implicit call to pthread_exit
• void pthread_exit(void *value_ptr)
– terminates and makes value_ptr available to any successful joi
• int pthread_join(pthread_t thread, void **value_ptr)
– suspends execution of the calling thread until the
target thread terminates
– On return with a non-NULL value_ptr  the value passed
to pthread_exit() by the terminating thread is made available in
the location referenced by value_ptr. 

man pthread
https://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread.h.html
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 23

2/4/20
How t
o tell
Really if som
done? ething
is don
O K to
reclai e?

m i ts
Little
resou
rces?
Example

Kubiatowicz CS162 ©UCB Spring 2020


24
Fork-Join Pattern

create

exit

join

• Main thread creates (forks) collection of sub-threads passing


them args to work on, joins with them, collecting results.

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 25


Thread Abstraction

• Illusion: Infinite number of processors

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 26


Thread Abstraction

• Illusion: Infinite number of processor


• Reality: Threads execute with variable spee
– Programs must be designed to work with any schedule

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 27


s

Programmer vs. Processor View

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 28


Programmer vs. Processor View

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 29


Programmer vs. Processor View

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 30


Possible Executions

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 31


Per Thread Descriptor
(Kernel Supported Threads)

• Each Thread has a Thread Control Block (TCB


– Execution State: CPU registers, program counter (PC), pointer to stack
(SP
– Scheduling info: state, priority, CPU tim
– Various Pointers (for implementing scheduling queues
– Pointer to enclosing process (PCB) – user thread
– … (add stuff as you find a need

• OS Keeps track of TCBs in “kernel memory


– In Array, or Linked List, or
– I/O state (file descriptors, network connections, etc)

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 32


)


Multithreaded Processes

• Process Control Block (PCBs) points to multiple Thread Control


Blocks (TCBs)

• Switching threads within a block is a simple thread switc


• Switching threads across blocks requires changes to memory and I/O
address tables

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 33


:

Multiprocessing vs Multiprogramming
• Remember Definitions
– Multiprocessing ≡ Multiple CPU
– Multiprogramming ≡ Multiple Jobs or Processe
– Multithreading ≡ Multiple threads per Proces
• What does it mean to run two threads “concurrently”
– Scheduler is free to run threads in any order and interleaving: FIFO,
Random,
– Dispatcher can choose to run each thread to completion or time-slice
in big chunks or small chunks
A
Multiprocessing B
C

A B C

Multiprogramming A B C A B C B

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 34


Correctness for systems with concurrent threads


• If dispatcher can schedule threads in any way, programs must work
under all circumstance
– Can you test for this
– How can you know if your program works
• Independent Threads
– No state shared with other thread
– Deterministic ⇒ Input state determines result
– Reproducible ⇒ Can recreate Starting Conditions, I/
– Scheduling order doesn’t matter (if switch() works!!!
• Cooperating Threads
– Shared State between multiple thread
– Non-deterministi
– Non-reproducibl
• Non-deterministic and Non-reproducible means that bugs can be
intermitten
– Sometimes called “Heisenbugs”

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 35


t

Heisenberg

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020


36
Interactions Complicate Debugging
• Is any program truly independent
– Every process shares the file system, OS resources, network, et
– Extreme example: buggy device driver causes thread A to crash
“independent thread”
• You probably don’t realize how much you depend on reproducibility
– Example: Evil C compile
» Modifies files behind your back by inserting errors into C program unless
you insert debugging cod
– Example: Debugging statements can overrun stac
• Non-deterministic errors are really difficult to fin
– Example: Memory layout of kernel+user program
» depends on scheduling, which depends on timer/other thing
» Original UNIX had a bunch of non-deterministic error
– Example: Something which does interesting I/
» User typing of letters used to help generate secure keys

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 37


B

Why allow cooperating threads?


• People cooperate; computers help/enhance people’s lives, so
computers must cooperat
– By analogy, the non-reproducibility/non-determinism of people is a
notable problem for “carefully laid plans
• Advantage 1: Share resource
– One computer, many user
– One bank balance, many ATM
» What if ATMs were only updated at night
– Embedded systems (robot control: coordinate arm & hand
• Advantage 2: Speedu
– Overlap I/O and computatio
» Many different file systems do read-ahea
– Multiprocessors – chop up program into parallel piece
• Advantage 3: Modularity
– More important than you might thin
– Chop large problem up into simpler piece
» To compile, for instance, gcc calls cpp | cc1 | cc2 | as | l
» Makes system easier to extend

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 38


p

High-level Example: Web Server

• Server must handle many request


• Non-cooperating version
serverLoop() {
con = AcceptCon();
ProcessFork(ServiceWebPage(),con);
}
• What are some disadvantages of this technique?

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 39







Threaded Web Server


• Now, use a single proces
• Multithreaded (cooperating) version
serverLoop() {
connection = AcceptCon();
ThreadFork(ServiceWebPage(),connection);
}
• Looks almost the same, but has many advantages
– Can share file caches kept in memory, results of CGI scripts, other
thing
– Threads are much cheaper to create than processes, so this has a lower
per-request overhea
• Question: would a user-level (say one-to-many) thread package
make sense here
– When one request blocks on disk, all block
• What about Denial of Service attacks or digg / Slash-dot effects?

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 40







s

Thread Pools
• Problem with previous version: Unbounded Thread
– When web-site becomes too popular – throughput sink
• Instead, allocate a bounded “pool” of worker threads, representing
the maximum level of multiprogrammin

queue
Master
Thread

Thread Pool
worker(queue) {
master() {
while(TRUE) {
allocThreads(worker,queue);
con=Dequeue(queue);
while(TRUE) {
if (con==null)
con=AcceptCon();
sleepOn(queue);
Enqueue(queue,con);
else
wakeUp(queue);
ServiceWebPage(con);
}
}
}
}
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 41

















g

ATM Bank Server

• ATM server problem


– Service a set of request
– Do so without corrupting databas
– Don’t hand out too much money
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 42
:

ATM bank server example


• Suppose we wanted to implement a server process to handle
requests from an ATM network
BankServer() {
while (TRUE) {
ReceiveRequest(&op, &acctId, &amount);
ProcessRequest(op, acctId, amount);
}
}
ProcessRequest(op, acctId, amount) {
if (op == deposit) Deposit(acctId, amount);
else if …
}
Deposit(acctId, amount) {
acct = GetAccount(acctId); /* may use disk I/O */
acct->balance += amount;
StoreAccount(acct); /* Involves disk I/O */
}
• How could we speed this up
– More than one request being processed at onc
– Event driven (overlap computation and I/O
– Multiple threads (multi-proc, or overlap comp and I/O)

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 43








?



)







Event Driven Version of ATM server
• Suppose we only had one CP
– Still like to overlap I/O with computatio
– Without threads, we would have to rewrite in event-driven styl
• Exampl
BankServer() {
while(TRUE) {
event = WaitForNextEvent();
if (event == ATMRequest)
StartOnRequest();
else if (event == AcctAvail)
ContinueRequest();
else if (event == AcctStored)
FinishRequest();
}
}
– What if we missed a blocking I/O step
– What if we have to split code into hundreds of pieces which could be
blocking
– This technique is used for graphical programming

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 44















U








e

Can Threads Make This Easier?


• Threads yield overlapped I/O and computation without
“deconstructing” code into non-blocking fragment
– One thread per reques
• Requests proceeds to completion, blocking as required
Deposit(acctId, amount) {
acct = GetAccount(actId);/* May use disk I/O */
acct->balance += amount;
StoreAccount(acct); /* Involves disk I/O */
}
• Unfortunately, shared state can get corrupted:
Thread 1 Thread 2
load r1, acct->balance
load r1, acct->balance
add r1, amount2
store r1, acct->balance
add r1, amount1
store r1, acct->balance

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 45

















t









s



:





Problem is at the Lowest Level
• Most of the time, threads are working on separate data, so
scheduling doesn’t matter
Thread A Thread
x=1 y=2
• However, what about (Initially, y = 12)
Thread A Thread
x=1 y=2
x = y+1 y = y*2
– What are the possible values of x?
• Or, what are the possible values of x below
Thread A Thread
x=1 x=2
– X could be 1 or 2 (non-deterministic!)
– Could even be 3 for serial processors:
» Thread A writes 0001, B writes 0010 → scheduling order ABABABBA yields
3!
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 46
;
;
;
;
:

Atomic Operations
• To understand a concurrent program, we need to know what the
underlying indivisible operations are!
• Atomic Operation: an operation that always runs to completion or
not at al
– It is indivisible: it cannot be stopped in the middle and state cannot be
modified by someone else in the middl
– Fundamental building block – if no atomic operations, then have no
way for threads to work together
• On most machines, memory references and assignments (i.e. loads
and stores) of words are atomi
– Consequently – weird example that produces “3” on previous slide
can’t happen
• Many instructions are not atomi
– Double-precision floating point store often not atomi
– VAX and IBM 360 had an instruction to copy a whole array
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 47
l

Another Concurrent Program Example


• Two threads, A and B, compete with each othe
– One tries to increment a shared counte
– The other tries to decrement the counte
Thread A Thread
i=0 i = 0;
while (i < 10 while (i > -10)
i=i+1 i = i – 1;
printf(“A wins!”) printf(“B wins!”)
• Assume that memory loads and stores are atomic, but incrementing
and decrementing are not atomic
• Who wins? Could be eithe
• Is it guaranteed that someone wins? Why or why not
• What if both threads have their own CPU running at same speed? Is
it guaranteed that it goes on forever?

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 48


;
;
)
;
r



B


;

Hand Simulation Multiprocessor Example


• Inner loop looks like this
Thread A Thread
r1=0 load r1, M[i]
r1=0 load r1, M[i]
r1=1 add r1, r1, 1
r1=-1 sub r1, r1, 1
M[i]=1 store r1, M[i]
M[i]=-1 store r1, M[i]
• Hand Simulation
– And we’re off. A gets off to an early star
– B says “hmph, better go fast” and tries really har
– A goes ahead and writes “1
– B goes and writes “-1
– A says “HUH??? I could have sworn I put a 1 there
• Could this happen on a uniprocessor? With Hyperthreads
– Yes! Unlikely, but if you are depending on it not happening, it will and
your system will break…
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 49





















:


Correctness Requirements
• Threaded programs must work for all interleavings of thread
instruction sequence
– Cooperating threads inherently non-deterministic and non-reproducibl
– Really hard to debug unless carefully designed
• Example: Therac-2
– Machine for radiation therap
» Software control of electron
accelerator and electron beam/
Xray productio
» Software control of dosag
– Software errors caused the
death of several patient
» A series of race conditions on
shared variables and poor
software desig
» “They determined that data entry speed during editing was the key factor
in producing the error condition: If the prescription data was edited at a
fast pace, the overdose occurred.”

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 50


n





y


!

Motivating Example: “Too Much Milk”


• Great thing about OS’s – analogy between problems in
OS and problems in real lif
– Help you understand real life problems bette
– But, computers are much stupider than peopl
• Example: People need to coordinate:

Time Person A Person B


3:00 Look in Fridge. Out of milk
3:05 Leave for store
3:10 Arrive at store Look in Fridge. Out of milk
3:15 Buy milk Leave for store
3:20 Arrive home, put milk away Arrive at store
3:25 Buy milk
3:30 Arrive home, put milk away

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 51


e

Definitions
• Synchronization: using atomic operations to ensure cooperation
between thread
– For now, only loads and stores are atomi
– We are going to show that its hard to build anything useful with only
reads and write

• Mutual Exclusion: ensuring that only one thread does a particular


thing at a tim
– One thread excludes the other while doing its tas

• Critical Section: piece of code that only one thread can execute at
once. Only one thread at a time will get into this section of cod
– Critical section is the result of mutual exclusio
– Critical section and mutual exclusion are two ways of describing the
same thing

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 52


e

More Definitions
• Lock: prevents someone from doing somethin
– Lock before entering critical section and
before accessing shared dat
– Unlock when leaving, after accessing shared dat
– Wait if locke
» Important idea: all synchronization involves waitin
• For example: fix the milk problem by putting a key on the
refrigerato
– Lock it and take key if you are going to go buy mil
– Fixes too much: roommate angry if only wants O
#$@%
@#$@

– Of Course – We don’t know how to make a lock yet

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 53


r


g

Too Much Milk: Correctness Properties


• Need to be careful about correctness of concurrent programs,
since non-deterministi
– Impulse is to start coding first, then when it doesn’t work, pull
hair ou
– Instead, think first, then cod
– Always write down behavior firs
• What are the correctness properties for the “Too much milk”
problem??
– Never more than one person buy
– Someone buys if neede
• Restrict ourselves to use only atomic load and store
operations as building blocks

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 54


t

Too Much Milk: Solution #1


• Use a note to avoid buying too much milk
– Leave a note before buying (kind of “lock”
– Remove note after buying (kind of “unlock”
– Don’t buy if note (wait
• Suppose a computer tries this (remember, only memory read/write are
atomic)
if (noMilk) {

if (noNote) {

leave Note;

buy milk;

remove note;

}

}

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 55









:

Too Much Milk: Solution #1


• Use a note to avoid buying too much milk
– Leave a note before buying (kind of “lock”
– Remove note after buying (kind of “unlock”
– Don’t buy if note (wait
• Suppose a computer tries this (remember, only memory read/write are
atomic):
Thread A Thread
if (noMilk) 

if (noMilk) {

if (noNote)
if (noNote) {

leave Note
buy Milk
remove Note

leave Note
buy Milk
remove Note;

}

}
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 56




}

{
;

Too Much Milk: Solution #1


• Use a note to avoid buying too much milk
– Leave a note before buying (kind of “lock”
– Remove note after buying (kind of “unlock”
– Don’t buy if note (wait
• Suppose a computer tries this (remember, only memory read/write are
atomic)
if (noMilk) {

if (noNote) {

leave Note;

buy milk;

remove note;

}

}
• Result?
– Still too much milk but only occasionally!
– Thread can get context switched after checking milk and note but before
buying milk
• Solution makes problem worse since fails intermittentl
– Makes it really hard to debug
– Must work despite what the dispatcher does!
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 57







Too Much Milk: Solution #1½


• Clearly the Note is not quite blocking enoug
– Let’s try to fix this by placing note firs
• Another try at previous solution

leave Note;
if (noMilk) {

if (noNote) {

buy milk;

}


remove Note;


• What happens here


– Well, with human, probably nothing ba
– With computer: no one ever buys milk

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 58







}

Too Much Milk Solution #2


• How about labeled notes?
– Now we can leave note before checkin
• Algorithm looks like this
Thread A Thread
leave note A leave note B;

if (noNote B) if (noNoteA) {

if (noMilk) if (noMilk) {

buy Milk buy Milk;

}

}

remove note A remove note B
• Does this work
• Possible for neither thread to buy mil
– Context switches at exactly the wrong times can lead each to think
that the other is going to bu
• Really insidious:
– Extremely unlikely this would happen, but will at worse possible time
– Probably something like this in UNIX

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 59



}
}

;
y

;
{
;

{
g

Too Much Milk Solution #2: problem!

• I’m not getting milk, You’re getting mil


• This kind of lockup is called “starvation!”

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 60


k

Too Much Milk Solution #3


• Here is a possible two-note solution
Thread A Thread
leave note A leave note B;

while (note B) {\\X if (noNote A) {\\Y

do nothing if (noMilk) {

buy milk;

if (noMilk) }

buy milk }

remove note B;

remove note A
• Does this work? Yes. Both can guarantee that:
– It is safe to buy, o
– Other will buy, ok to qui
• At X:
– If no note B, safe for A to buy,
– Otherwise wait to find out what will happe
• At Y:
– If no note A, safe for B to bu
– Otherwise, A is either buying or waiting for B to quit
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 61

}
}
r

;
t

{
;
;

;
y

Case 1
• “leave note A” happens before “if (noNote A)”
leave note A 
 happene leave note B 

while (note B) {\\X before if (noNote A) {\\
do nothing 
 if (noMilk) {

} buy milk
}


remove note B;

if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 62


}
}
;

;
;
{
;
;
;
Y

Case 1
• “leave note A” happens before “if (noNote A)”
leave note A 
 happene leave note B 

while (note B) {\\X before if (noNote A) {\\
do nothing 
 if (noMilk) {

} buy milk
}


remove note B;

if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 63


}
}
;

;
{
;
;
;
;
Y

Case 1
• “leave note A” happens before “if (noNote A)”
leave note A 
 happene leave note B 

while (note B) {\\X before if (noNote A) {\\
do nothing 
 if (noMilk) {

} buy milk
}

Wait for note 

B to be
removed remove note B;

if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 64


}
}
;

;
{
;
;
;
;
Y

Case 2
• “if (noNote A)” happens before “leave note A”
leave note B 

ne if (noNote A) {\\
happe e
befor if (noMilk) {

leave note A 

while (note B) {\\X buy milk
do nothing 
 }

} 

remove note B;

if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 65


}
}
;

;
;
{
;
;
;
Y

Case 2
• “if (noNote A)” happens before “leave note A”
leave note B 

ne if (noNote A) {\\
happe e
befor if (noMilk) {

leave note A 

while (note B) {\\X buy milk
do nothing 
 }

} 

remove note B;

if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 66


}
}
;

;
;
{
;
;
;
Y

Case 2
• “if (noNote A)” happens before “leave note A”
leave note B 

ne if (noNote A) {\\
happe e
befor if (noMilk) {

leave note A 

while (note B) {\\X buy milk
do nothing 
 }

} 

remove note B;
Wait for note
B to be
removed
if (noMilk)
buy milk }


remove note A;

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 67


}
}
;

;
;
{
;
;
;
Y

Solution #3 discussion
• Our solution protects a single “Critical-Section” piece of code for each
thread
if (noMilk) {
buy milk;
}
• Solution #3 works, but it’s really unsatisfactor
– Really complex – even for this simple an exampl
» Hard to convince yourself that this really work
– A’s code is different from B’s – what if lots of threads
» Code would have to be slightly different for each threa
– While A is waiting, it is consuming CPU tim
» This is called “busy-waiting
• There’s a better wa
– Have hardware provide higher-level primitives than atomic load & stor
– Build even higher-level programming abstractions on this hardware support

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 68








:





s

Too Much Milk: Solution #4


• Suppose we have some sort of implementation of a loc
– lock.Acquire() – wait until lock is free, then grab
– lock.Release() – Unlock, waking up anyone waiting
– These must be atomic operations – if two threads are waiting for the
lock and both see it’s free, only one succeeds to grab the loc
• Then, our milk problem is easy
milklock.Acquire()
if (nomilk
buy milk
milklock.Release()
• Once again, section of code between Acquire() and
Release() called a “Critical Section”

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 69


)

How to Implement Locks?


• Lock: prevents someone from doing somethin
– Lock before entering critical section and
before accessing shared data
– Unlock when leaving, after accessing shared data
– Wait if locked
» Important idea: all synchronization involves waitin
» Should sleep if waiting for a long tim
• Atomic Load/Store: get solution like Milk #
– Pretty complex and error prone
• Hardware Lock instructio
– Is this a good idea?
– What about putting a task to sleep?
» What is the interface between the hardware and scheduler
– Complexity?
» Done in the Intel 43
» Each feature makes HW more complex and slow
2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 70
2


g

Naïve use of Interrupt Enable/Disable


• How can we build multi-instruction atomic operations
– Recall: dispatcher gets control in two ways.
» Internal: Thread does something to relinquish the CP
» External: Interrupts cause dispatcher to take CP
– On a uniprocessor, can avoid context-switching by
» Avoiding internal event
» Preventing external events by disabling interrupt
• Consequently, naïve Implementation of locks
LockAcquire { disable Ints; }
LockRelease { enable Ints; }
• Problems with this approach
– Can’t let user do this! Consider following:
LockAcquire();
While(TRUE) {;}
– Real-Time system—no guarantees on timing!
» Critical Sections might be arbitrarily lon
– What happens with I/O or other important events
» “Reactor about to meltdown. Help?”

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 71





:

:
:

Better Implementation of Locks by Disabling Interrupts


• Key idea: maintain a lock variable and impose mutual exclusion
only during operations on that variable

int value = FREE

Acquire() {
 Release() {

disable interrupts;
 disable interrupts;

if (value == BUSY) {
 if (anyone on wait queue) {

put thread on wait queue;
 take thread off wait queue

Go to sleep();
 Place on ready queue;

// Enable interrupts?
 } else {

} else {
 value = FREE;

}

value = BUSY;

enable interrupts;

}
 }

enable interrupts;
 

}

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 72


;

Where are we going with synchronization?


Programs Shared Programs

Higher-
level Locks Semaphores Monitors Send/Receive
API

Hardware Load/Store Disable Ints Test&Set Compare&Swap

• We are going to implement various higher-level synchronization


primitives using atomic operation
– Everything is pretty painful if only atomic primitives are load and stor
– Need to provide primitives useful at user-level

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 73



s

Summary
• Concurrent threads are a very useful abstractio
– Allow transparent overlapping of computation and I/
– Allow use of parallel processing when availabl

• Concurrent threads introduce problems when accessing shared


dat
– Programs must be insensitive to arbitrary interleaving
– Without careful design, shared variables can become completely
inconsisten

• Important concept: Atomic Operation


– An operation that runs to completion or not at al
– These are the primitives on which to construct various
synchronization primitives

2/4/20 Kubiatowicz CS162 ©UCB Spring 2020 74


a

You might also like