cps110: implementing threads/locks on a uni-processor landon cox

37
CPS110: Implementing threads/locks on a uni-processor Landon Cox

Upload: sherman-franklin

Post on 21-Dec-2015

226 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: CPS110: Implementing threads/locks on a uni-processor Landon Cox

CPS110: Implementing

threads/locks on a uni-processor

Landon Cox

Page 2: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Recall, thread interactions

1. Threads can access shared data Use locks, monitors, and

semaphores What we’ve done so far

2. Threads also share hardware CPU (uni-processor) Memory

Page 3: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Private vs global thread state

What state is private to each thread? Code (like lines of a play) PC (where actor is in his/her script) Stack, SP (actor’s mindset)

What state is shared? Global variables, heap (props on set)

Page 4: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Thread control block (TCB)

Running thread’s private data is on CPU

Non-running thread’s private state is in TCB

Thread control block (TCB) Container for non-running thread’s

private data (PC, code, SP, stack, registers)

Page 5: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Thread control block

CPUCPU

Address SpaceAddress Space

TCB1PCSP

registers

TCB1PCSP

registers

TCB2PCSP

registers

TCB2PCSP

registers

TCB3PCSP

registers

TCB3PCSP

registers

CodeCode

StackStackCodeCode

StackStackCodeCode

StackStack

PCSP

registers

PCSP

registersThread 1 running

Ready queue

Page 6: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Thread control block

CPUCPU

Address SpaceAddress Space

TCB2PCSP

registers

TCB2PCSP

registers

TCB3PCSP

registers

TCB3PCSP

registers

CodeCode

StackStack StackStack StackStack

PCSP

registers

PCSP

registersThread 1 running

Ready queue

Page 7: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Thread states

Running Currently using the CPU

Ready Ready to run, but waiting for the

CPU Blocked

Stuck in lock (), wait () or down ()

Page 8: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Switching threads

What needs to happen to switch threads?1.Thread returns control to OS

For example, via the “yield” call

2.OS chooses next thread to run3.OS saves state of current thread

To its thread control block

4.OS loads context of next thread From its thread control block

5.Run the next thread

Project 1: swapcontext

Page 9: CPS110: Implementing threads/locks on a uni-processor Landon Cox

1. Thread returns control to OS

How does the thread system get control? Could wait for thread to give up control

(internal events) Thread might block inside lock or wait Thread might call into kernel for service

(system call)

Thread might call yield Are internal events enough?

Page 10: CPS110: Implementing threads/locks on a uni-processor Landon Cox

1. Thread returns control to OS

External events (events not initiated by the thread) Hardware interrupts

Transfer control directly to OS interrupt handlers From 104

CPU checks for interrupts while executing Jumps to OS code with interrupt mask set

Interrupts lead to pre-emption (a forced yield) Common interrupt: timer interrupt

Page 11: CPS110: Implementing threads/locks on a uni-processor Landon Cox

2. Choosing the next thread

If no ready threads, just spin Modern CPUs: execute a “halt” instruction Project 1: exit if no ready threads

Loop switches to thread if one is ready Many ways to prioritize ready threads

Will discuss a little later in the semester

Page 12: CPS110: Implementing threads/locks on a uni-processor Landon Cox

3. Saving state of current thread

What needs to be saved? Registers, PC, SP

What makes this tricky? Need registers to save state But you’re trying to save all the

registers Saving the PC

Page 13: CPS110: Implementing threads/locks on a uni-processor Landon Cox

4. OS loads the next thread

Where is the thread’s state/context? Thread control block (in memory)

How to load the registers? Use load instructions to grab from

memory How to load the stack?

Stack is already in memory, load SP

Page 14: CPS110: Implementing threads/locks on a uni-processor Landon Cox

5. OS runs the next thread

How to resume thread’s execution? Jump to the saved PC

On whose stack are these steps running?

or Who is running these steps? The thread that called yield (or was interrupted or called lock/wait)

How does this thread run again? Some other thread must switch to it

Page 15: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Example thread switchingThread 1 print “start thread 1” yield () print “end thread 1”

Thread 2 print “start thread 2” yield () print end thread 2”

yield () print “start yield (thread %d)” switch to next thread (swapcontext) print “end yield (thread %d)”

Thread 1 output Thread 2 output--------------------------------------------start thread 1start yield (thread 1)

start thread 2 start yield (thread 2)

end yield (thread 1)end thread 1

end yield (thread 2) end thread 2

Note: this assumes no pre-emptions.Programmers must assume pre-emptions, so this output is not guaranteed.

Page 16: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Creating a new thread

Also called “forking” a thread Idea: create initial state, put on ready queue1.Allocate, initialize a new TCB2.Allocate a new stack3.Make it look like thread was going to call a

function PC points to first instruction in function SP points to new stack Stack contains arguments passed to function Project 1: use makecontext

4.Add thread to ready queue

Page 17: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Using interrupt disable-enable

Disable-enable on a uni-processor Assume atomic (can use atomic load/store)

How do threads get switched out? Internal events (yield, I/O request) External events (interrupts, e.g. timers)

Easy to prevent internal events Use disable-enable to prevent external

events

Page 18: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Why use locks?

If we have disable-enable, why do we need locks? Program could bracket critical sections with disable-

enable Might not be able to give control back to thread library

Can’t have multiple locks (over-constrains concurrency)

Project 1: only disable interrupts in thread library

disable interruptswhile (1){}

Page 19: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Disable interrupts, busy-waiting

Lock implementation #1

lock () { disable interrupts while (value != FREE) { enable interrupts disable interrupts } value = BUSY enable interrupts}

unlock () { disable interrupts value = FREE enable interrupts}

Why is it ok for lock code to disable interrupts?It’s in the trusted kernel (we have to trust something).

Page 20: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Disable interrupts, busy-waiting

Lock implementation #1

lock () { disable interrupts while (value != FREE) { enable interrupts disable interrupts } value = BUSY enable interrupts}

unlock () { disable interrupts value = FREE enable interrupts}

Do we need to disable interrupts in unlock?Only if “value = FREE” is multiple instructions (safer)

Page 21: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Disable interrupts, busy-waiting

Lock implementation #1

lock () { disable interrupts while (value != FREE) { enable interrupts disable interrupts } value = BUSY enable interrupts}

unlock () { disable interrupts value = FREE enable interrupts}

Why enable-disable in lock loop body?Otherwise, no one else will run (including unlockers)

Page 22: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Using read-modify-write instructions

Disabling interrupts Ok for uni-processor, breaks on multi-

processor Why?

Could use atomic load-store to make a lock Inefficient, lots of busy-waiting

Hardware people to the rescue!

Page 23: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Test&set, busy-waiting Initially, value = 0

Lock implementation #2

lock () { while (test&set(value) == 1) { }}

unlock () { value = 0}

What happens if value = 1?What happens if value = 0?

Page 24: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Locks and busy-waiting

All implementations have used busy-waiting Wastes CPU cycles

To reduce busy-waiting, integrate Lock implementation Thread dispatcher data structures

Page 25: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Interrupt disable, no busy-waiting

Lock implementation #3

lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Page 26: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Who gets the lock after someone calls unlock?

This is called a

“hand-off” lock.

Page 27: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Who might get the lock if it weren’t handed-off directly? (e.g. if value weren’t set BUSY in unlock)

This is called a

“hand-off” lock.

Page 28: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

What does this mean? Are we saving the PC?

This is called a

“hand-off” lock.

Page 29: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Why a separate queue for the lock?

This is called a

“hand-off” lock.

Page 30: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Project 1 note: you must guarantee FIFO ordering of lock acquisition.

This is called a

“hand-off” lock.

Page 31: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { enable interrupts add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

When and how could this fail?

Page 32: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock enable interrupts switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

When and how could this fail?

Page 33: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

Putting lock thread on lock wait queue, switch must be atomic. Must call switch with interrupts off.

Page 34: CPS110: Implementing threads/locks on a uni-processor Landon Cox

How is switch returned to?

Review from last time Think of switch as three phases

Save current location (SP, PC) Point processor to another stack (SP’) Jump to another instruction (PC’)

Only way to get back to a switch Have another thread call switch

Page 35: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Lock implementation #3lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { add thread to queue of threads waiting for lock switch to next ready thread // don’t add to ready queue } enable interrupts}

unlock () { disable interrupts value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } enable interrupts}

What is lock() assuming about the state of interrupts after switch returns?

Page 36: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Interrupts and returning to switch

Lock() can assume that switch Is always called with interrupts disabled

On return from switch Previous thread must have disabled interrupts

Next thread to run Becomes responsible for re-enabling interrupts

Invariants: threads promise to Disable interrupts before switch is called Re-enable interrupts after returning from switch

Page 37: CPS110: Implementing threads/locks on a uni-processor Landon Cox

Thread A Thread Byield () { disable interrupts … switch

enable interrupts} // exit thread library function<user code>lock () { disable interrupts … switch

back from switch … enable interrupts} // exit yield<user code>unlock () // moves A to ready queueyield () { disable interrupts … switch

back from switch … enable interrupts} // exit lock<user code>