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) ? Ready Blocked Another thread calls unlock or signal (or I/O completes)
Pr Project 1 (test cases) oject 1 (test cases) ead library test suite too Remember to work on your thr Remember to work on your thread 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 o-tests are better for debugging than macr e better for debugging than macro-tests o-tests •
Pr Project 1 (garbage collection) oject 1 (garbage collection) • Garbage collecting thr Garbage collecting threads eads // simple network server ¡ while (1) { int s = socket.accept (); thread_create (handle_request, s); } • 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)
Pr Project 1 (garbage collection) oject 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?
Pr Project 1 (garbage collection) oject 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
Pr Project 1 (garbage collection) oject 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? oject 1 questions?
Using interrupt disable-enable Using interrupt disable-enable • Disable-enable on a Disable-enable on a uni uni-pr -processor ocessor • 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
Lock implementation #1 Lock implementation #1 • Disable interrupts, busy-waiting Disable interrupts, busy-waiting unlock () { lock () { disable interrupts disable interrupts value = FREE while (value != FREE) { enable interrupts enable interrupts } disable interrupts } value = BUSY enable interrupts } Why is it ok for lock code to disable interrupts? It’s in the trusted kernel (we have to trust something).
Lock implementation #1 Lock implementation #1 • Disable interrupts, busy-waiting Disable interrupts, busy-waiting unlock () { lock () { disable interrupts disable interrupts value = FREE while (value != FREE) { enable interrupts enable interrupts } disable interrupts } value = BUSY enable interrupts } Do we need to disable interrupts in unlock? Only if “value = FREE” is multiple instructions (safer)
Lock implementation #1 Lock implementation #1 • Disable interrupts, busy-waiting Disable interrupts, busy-waiting unlock () { lock () { disable interrupts disable interrupts value = FREE while (value != FREE) { enable interrupts enable interrupts } disable interrupts } value = BUSY enable interrupts } Why enable-disable in lock loop body? Otherwise, no one else will run (including unlockers)
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!
Using r Using read-modify-write instructions ead-modify-write instructions • Most moder Most modern pr n processor ar ocessor 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
Lock implementation #3 Lock implementation #3 • Interrupt disable, no busy-waiting Interrupt disable, no busy-waiting 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 }
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 This is This is } enable interrupts called a called a } “hand-off” “hand-of f” unlock () { disable interrupts lock. lock. 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?
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 This is This is } enable interrupts called a called a } “hand-off” “hand-of f” unlock () { disable interrupts lock. lock. 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 directly? (i.e., if value wer dir ectly? (i.e., if value weren’ en’t set BUSY in unlock) t set BUSY in unlock)
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 This is This is } enable interrupts called a called a } “hand-off” “hand-of f” unlock () { disable interrupts lock. lock. 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? ovide?
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 This is This is } enable interrupts called a called a } “hand-off” “hand-of f” unlock () { disable interrupts lock. lock. 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? What does this mean? Ar e we saving the PC?
Lock implementation #3 Lock implementation #3 lock () { disable interrupts if (value == FREE) { value = BUSY // lock acquire } else { lockqueue.push(¤t_thread->ucontext); swapcontext(¤t_thread->ucontext, This is This is &new_thread->ucontext)); } called a called a enable interrupts } “hand-of “hand-off” f” unlock () { lock. lock. 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 } No, just adding a pointer to the TCB/context. No, just adding a pointer to the TCB/context.
Recommend
More recommend