Slides on thr Slides on threads eads borr borrowed by Chase owed - - PowerPoint PPT Presentation

slides on thr slides on threads eads borr borrowed by
SMART_READER_LITE
LIVE PREVIEW

Slides on thr Slides on threads eads borr borrowed by Chase owed - - PowerPoint PPT Presentation

Slides on thr Slides on threads eads borr borrowed by Chase owed by Chase Landon Cox Landon Cox Thr Thread states ead states Running Thread is Thread calls scheduled Lock or wait Thread is (or makes I/O request) Pre-empted (or yields) ?


slide-1
SLIDE 1

Slides on thr Slides on threads eads borr borrowed by Chase

  • wed by Chase

Landon Cox Landon Cox

slide-2
SLIDE 2

Thr Thread states ead states

Blocked Ready Running

Thread is scheduled Thread is Pre-empted (or yields) Thread calls Lock or wait (or makes I/O request) Another thread calls unlock or signal (or I/O completes)

?

slide-3
SLIDE 3

Pr Project 1 (test cases)

  • ject 1 (test cases)
  • Remember to work on your thr

Remember to work on your thread library test suite too ead library test suite too

  • For each of your test cases

For each of your test cases

  • No inputs (e.g., no input files, no command-line args)
  • All output to stdout (i.e., use cout)
  • Do not call start_preemption()
  • Diff output of test case w/ your library vs. w/ thread.o
  • Writing test cases

riting test cases

  • Read through spec

spec

  • Write test cases to stress each required part
  • E.g., lock() blocks if lock is already held
  • Pre-emption is off, so use yield() to create the right interleavings
  • Read through your code

your code

  • Write test cases to exercise all lines of code
  • E.g. each clause of an if/else statement
  • Micr

Micro-tests ar

  • -tests are better for debugging than macr

e better for debugging than macro-tests

  • -tests
slide-4
SLIDE 4

Pr Project 1 (garbage collection)

  • ject 1 (garbage collection)
  • Garbage collecting thr

Garbage collecting threads eads

  • Do not want to run out of memory
  • What needs to be (C++) “deleted”?

What needs to be (C++) “deleted”?

  • Any state associated with the thread (e.g., stack, TCB)

// simple network server ¡ while (1) { int s = socket.accept (); thread_create (handle_request, s); }

slide-5
SLIDE 5

Pr Project 1 (garbage collection)

  • ject 1 (garbage collection)
  • Two key questions:

wo key questions:

  • When can a stack be deleted and by whom?
  • When can a stack be deleted?

When can a stack be deleted?

  • Only after the thread has finished its “work”
  • Work = function passed to thread_create, thread_libinit
  • Who definitely cannot delete a thr

Who definitely cannot delete a thread’ ead’s stack? s stack?

  • The thread itself!
  • Try deleting the stack you are running on …
  • So which thr

So which thread should delete the stack? ead should delete the stack?

slide-6
SLIDE 6

Pr Project 1 (garbage collection)

  • ject 1 (garbage collection)
  • Hint: don’

Hint: don’t use t use uc_link uc_link

  • Only use set/swapcontext to jump to threads
  • Anyone want to guess why?

Anyone want to guess why?

  • What can you say about state of interrupts?

What can you say about state of interrupts?

  • Interrupts are enabled inside “work” function
  • After it exits, interrupts must be disabled
  • Tricky to guarantee this using uc_link
  • Can get an interrupt while switching to uc_link
slide-7
SLIDE 7

Pr Project 1 (garbage collection)

  • ject 1 (garbage collection)
  • What makes

What makes swapcontext swapcontext simpler? simpler?

  • uc_link loads threads outside of your lib
  • Calls to swapcontext are explicit
  • “Keep everything in front of you”
  • Any other Pr

Any other Project 1 questions?

  • ject 1 questions?
slide-8
SLIDE 8

Using interrupt disable-enable Using interrupt disable-enable

  • Disable-enable on a

Disable-enable on a uni uni-pr

  • processor
  • cessor
  • Assume atomic (can use atomic load/store)
  • How do thr

How do threads get switched out (2 ways)? eads get switched out (2 ways)?

  • Internal events (yield, I/O request)
  • External events (interrupts, e.g., timers)
  • Easy to pr

Easy to prevent inter event internal events nal events

  • Use disable/enable to pr

Use disable/enable to prevent exter event external events nal events

slide-9
SLIDE 9
  • Disable interrupts, busy-waiting

Disable interrupts, busy-waiting

Lock implementation #1 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).

slide-10
SLIDE 10
  • Disable interrupts, busy-waiting

Disable interrupts, busy-waiting

Lock implementation #1 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)

slide-11
SLIDE 11
  • Disable interrupts, busy-waiting

Disable interrupts, busy-waiting

Lock implementation #1 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)

slide-12
SLIDE 12

Using r Using read-modify-write instructions ead-modify-write instructions

  • Disabling interrupts

Disabling interrupts

  • Ok for uni-processor, breaks on multi-processor
  • Why?
  • Could use atomic load-stor

Could use atomic load-store to make a lock e to make a lock

  • Inefficient, lots of busy-waiting
  • Har

Hardwar dware people to the r e people to the rescue! escue!

slide-13
SLIDE 13

Using r Using read-modify-write instructions ead-modify-write instructions

  • Most moder

Most modern pr n processor ar

  • cessor architectur

chitectures es

  • Provide an atomic read-modify-write instruction
  • Atomically

Atomically

  • Read value from memory into register
  • Write new value to memory
  • Implementation details

Implementation details

  • Lock memory location at the memory controller
slide-14
SLIDE 14
  • Interrupt disable, no busy-waiting

Interrupt disable, no busy-waiting

Lock implementation #3 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 }

slide-15
SLIDE 15

Lock implementation #3 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 }

Who gets the lock after someone calls unlock? Who gets the lock after someone calls unlock? This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-16
SLIDE 16

Lock implementation #3 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 }

Who might get the lock if it wer Who might get the lock if it weren’ en’t handed-of t handed-off f dir directly? (i.e., if value wer ectly? (i.e., if value weren’ en’t set BUSY in unlock) t set BUSY in unlock) This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-17
SLIDE 17

Lock implementation #3 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 }

What kind of or What kind of ordering of lock acquisition guarantees dering of lock acquisition guarantees does the hand-of does the hand-off lock pr f lock provide?

  • vide?

This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-18
SLIDE 18

Lock implementation #3 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 }

What does this mean? Ar What does this mean? Are we saving the PC? e we saving the PC? This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-19
SLIDE 19

Lock implementation #3 Lock implementation #3

lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { lockqueue.push(&current_thread->ucontext); swapcontext(&current_thread->ucontext, &new_thread->ucontext)); } enable interrupts }

No, just adding a pointer to the TCB/context. No, just adding a pointer to the TCB/context. This is This is called a called a “hand-of “hand-off” f” lock. lock.

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 }

slide-20
SLIDE 20

Lock implementation #3 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 }

Why a separate queue for the lock? Why a separate queue for the lock? This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-21
SLIDE 21

Lock implementation #3 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 }

Pr Project 1 note: you must guarantee FIFO or

  • ject 1 note: you must guarantee FIFO ordering of

dering of lock operations lock operations This is This is called a called a “hand-of “hand-off” f” lock. lock.

slide-22
SLIDE 22

Lock implementation #3 Lock implementation #3

lock () { 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? When and how could this fail?

slide-23
SLIDE 23

Lock implementation #3 Lock implementation #3

lock () { 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?

1 2 3

slide-24
SLIDE 24

Lock implementation #3 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 }

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

slide-25
SLIDE 25

How is switch r How is switch retur eturned to? ned to?

  • Think of switch as thr

Think of switch as three phases ee phases

  • 1. Save current location (SP

, PC)

  • 2. Point processor to another stack (SP’)
  • 3. Jump to another instruction (PC’)
  • Only way to get back to a switch

Only way to get back to a switch

  • Have another

another thread call switch

slide-26
SLIDE 26

Lock implementation #3 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 }

What is lock() assuming about the state of interrupts What is lock() assuming about the state of interrupts after switch r after switch retur eturns? ns?

slide-27
SLIDE 27

Interrupts and r Interrupts and retur eturning to switch ning to switch

  • Lock() can assume that switch

Lock() can assume that switch

  • Is always called with interrupts disabled
  • On r

On retur eturn fr n from switch

  • m switch
  • Previous thread must have disabled interrupts
  • Next thr

Next thread to run ead to run

  • Becomes responsible for re-enabling interrupts
  • Invariants

Invariants: thr : threads pr eads promise to

  • mise to
  • Disable interrupts before switch is called
  • Re-enable interrupts after returning from switch
slide-28
SLIDE 28

Thread A Thread B

yield () { disable interrupts … switch (B->A) enable interrupts } // exit thread library <user code> lock () { disable interrupts … switch (A->B) back from switch (B->A) … enable interrupts } // exit yield <user code> unlock () // moves A to ready queue yield () { disable interrupts … switch (B->A) back from switch (A->B) … enable interrupts } // exit lock <user code>

B holds lock B holds lock

slide-29
SLIDE 29
  • Test&set, minimal busy-waiting

est&set, minimal busy-waiting

Lock implementation #4 Lock implementation #4

lock () { while (test&set (guard)) {} // like interrupt disable if (value == FREE) { value = BUSY } else { put on queue of threads waiting for lock switch to another thread // don’t add to ready queue } guard = 0 // like interrupt enable } unlock () { while (test&set (guard)) {} // like interrupt disable value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } guard = 0 // like interrupt enable }

slide-30
SLIDE 30

Lock implementation #4 Lock implementation #4

lock () { while (test&set (guard)) {} // like interrupt disable if (value == FREE) { value = BUSY } else { put on queue of threads waiting for lock switch to another thread // don’t add to ready queue } guard = 0 // like interrupt enable } unlock () { while (test&set (guard)) {} // like interrupt disable value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } guard = 0 // like interrupt enable }

Why is this better than only spinning with Why is this better than only spinning with test&set test&set?

Only busy-wait Only busy-wait while another while another thr thread is in ead is in lock or unlock lock or unlock Befor Before, we e, we busy-waited busy-waited while lock was while lock was held held

slide-31
SLIDE 31

Lock implementation #4 Lock implementation #4

lock () { while (test&set (guard)) {} // like interrupt disable if (value == FREE) { value = BUSY } else { put on queue of threads waiting for lock switch to another thread // don’t add to ready queue } guard = 0 // like interrupt enable } unlock () { while (test&set (guard)) {} // like interrupt disable value = FREE if anyone on queue of threads waiting for lock { take waiting thread off queue, put on ready queue value = BUSY } guard = 0 // like interrupt enable }

What is the switch invariant? What is the switch invariant? Thr Threads pr eads promise to call switch with guar

  • mise to call switch with guard set to 1.

d set to 1.

slide-32
SLIDE 32

const1=1 const2=0

main

Example stack Example stack

tmp=1 RA=0x804838c

A

RA=0x8048361

B

const=0 RA=0x8048354

C

tmp=0 RA=0x8048347

A

0xfffffff 0x0

Memory Memory

void C () { ¡ A (0); } void B () { C (); } void A (int tmp){ ¡ if (tmp) B (); } int main () { A (1); return 0; }

0x8048347 0x8048347 0x8048354 0x8048354 0x8048361 0x8048361 0x804838c 0x804838c Code Stack Stack

slide-33
SLIDE 33

Cr Creating a new thr eating a new thread ead

  • Also called “forking” a thr

Also called “forking” a thread ead

  • Idea: cr

Idea: create initial state, put on r eate initial state, put on ready queue eady queue 1.

  • 1. Allocate, initialize a new TCB

Allocate, initialize a new TCB 2.

  • 2. Allocate a new stack

Allocate a new stack 3.

  • 3. Make it look like thr

Make it look like thread was going to call a function ead 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.

  • 4. Add thr

Add thread to r ead to ready queue eady queue

slide-34
SLIDE 34

Example: thr Example: thread-safe queue ead-safe queue

dequeue () { lock (qLock); element = NULL; // if queue non-empty if (head != NULL) { element = head; head = head->next; element->next = NULL; } unlock (qLock); return element; } enqueue () { lock (qLock) // ptr is private // head is shared new_element = new node(); if (head == NULL) { head = new_element; new_element->next=0; } else { node *ptr; // find queue tail for (ptr=head; ptr->next!=NULL; ptr=ptr->next){} ptr->next=new_element; } unlock(qLock); }

What can go wrong?

(1) 2 elements in queue, ptr->next is non-null, switch to

  • ther thread

(2) previous head->next set to null (3) ptr->next now null, set ptr to null (4) dereference null pntr

slide-35
SLIDE 35

Thr Thread-safe queue ead-safe queue

  • Can

Can enqueue enqueue unlock anywher unlock anywhere? e?

  • No
  • Must leave shar

Must leave shared data ed data

  • In a consistent/sane state
  • Data

Data invariant invariant

  • “consistent/sane state”
  • “always” true

enqueue () { lock (qLock) // ptr is private // head is shared new_element = new node(); if (head == NULL) { head = new_element; } else { node *ptr; // find queue tail for (ptr=head; ptr->next!=NULL; ptr=ptr->next){} ptr->next=new_element; } unlock(qLock); // safe? new_element->next=0; }

Another thread can call enqueue

slide-36
SLIDE 36

How about this? How about this?

I’m always holding a lock while accessing shared state.

enqueue () { lock (qLock) // ptr is private // head is shared new_element = new node(); if (head == NULL) { head = new_element; } else { node *ptr; // find queue tail for (ptr=head; ptr->next!=NULL; ptr=ptr->next){} unlock(qLock); lock(qLock); ptr->next=new_element; } new_element->next=0; unlock(qLock); }

ptr may not point to tail after lock/unlock. Lesson:

  • Thinking about individual accesses is not enough
  • Must reason about dependencies between accesses