SE350: Operating Systems
Lecture 6: Synchronization
SE350: Operating Systems Lecture 6: Synchronization Outline Atomic - - PowerPoint PPT Presentation
SE350: Operating Systems Lecture 6: Synchronization Outline Atomic operations Hardware atomicity primitives Different implementations of locks Synchronization Motivation When threads concurrently read from or write to shared
Lecture 6: Synchronization
memory, program behavior is undefined
completed
// Thread 1 p = someComputation(); pInitialized = true; // Thread 2 While (!pInitialized); q = someFunc(p); If (q != someFunc(p)) panic();
Roommate A Roommate B 12:30 Look in fridge. Out of milk. 12:35 Leave for store. 12:40 Arrive at store. Look in fridge. Out of milk. 12:45 Buy milk. Leave for store. 12:50 Arrive home, put milk away. Arrive at store. 12:55 Buy milk. 01:00 Arrive home, put milk away. Oh no!
modified by someone else in the middle
for threads to work together
(i.e. loads and stores) of words are atomic
multiple concurrent threads
#$@%@#$@
(remember, only memory load/store are atomic)
if (!milk) { if (!note) { leave note; buy milk; remove note; } }
if (!milk) { if (!note) { leave note; buy milk; remove note; } } if (!milk) { if (!note) { leave note; buy milk; remove note; } }
before buying milk!
leave note; if (!milk) { if (!note) { buy milk; } } remove note;
// Thread A leave note A; if (!note B) { if (!milk) buy milk; } remove note A; // Thread B leave note B; if (!note A) { if (!milk) buy milk; } remove note B;
it is ok to quit
// Thread A leave note A; while (note B) // (X) do nothing; if (!milk) buy milk; remove note A; // Thread B leave note B; if (!note A) { // (Y) if (!milk) buy milk; } remove note B;
// Thread A leave note A; while (note B) // (X) do nothing; if (!milk) buy milk; remove note A; // Thread B leave note B; if (!note A) { // (Y) if (!milk) buy milk; } remove note B;
If A checks note B before B leaves the note, then A goes ahead and buys milk
If A checks note B after B leaves the note, then A waits to see what happens
// Thread A leave note A; while (note B) // (X) do nothing; if (!milk) buy milk; remove note A; // Thread B leave note B; if (!note A) { // (Y) if (!milk) buy milk; } remove note B;
// Thread A leave note A; while (note B) // (X) do nothing; if (!milk) buy milk; remove note A; // Thread B leave note B; if (!note A) { // (Y) if (!milk) buy milk; } remove note B;
B will not buy milk!
// Thread B leave note B; if (!note A) { // (Y) if (!milk) buy milk; } remove note B; // Thread A leave note A; while (note B) // (X) do nothing; if (!milk) buy milk; remove note A;
if (!milk) { buy milk; }
both see it’s free, only one succeeds to grab the lock
milklock.Acquire(); if (nomilk) buy milk; milklock.Release();
Load/Store Disable Interrupts Test&Set Compare&Swap Locks Semaphores Monitors Send/Receive Shared Programs Hardware Higher-level API Programs
synchronization primitives using atomic operations
Acquire { disable interrupts; } Release { enable interrupts; }
Acquire(); while(TRUE) {;}
int value = FREE; Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; go_to_sleep(); // Enable interrupts? } else { value = BUSY; } enable interrupts; } Release() { disable interrupts; if (threads on wait queue) { take one off wait queue place it on ready queue; } else { value = FREE; } enable interrupts; }
(doesn’t impact global machine behavior)
Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; go_to_sleep(); // Enable interrupts? } else { value = BUSY; } enable interrupts; }
Critical Section
Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; go_to_sleep(); } else { value = BUSY; } enable interrupts; }
enable here?
Thread A Thread B
. . disable interrupts sleep sleep return enable interrupts . . . disable interrupts sleep sleep return enable interrupts . .
c
t e x t s w i t c h context switch
very time consuming
multiprocessors
/* most architectures */ result = M[address]; /* return result from M[address] = 1; “address” and set value at return result; “address” to 1 */ }
/* x86 */ temp = M[address]; /* swap register’s value to M[address] = register; value at “address” */ register = temp; }
/* 68000 */ if (reg1 == M[address]) { M[address] = reg2; return success; } else { return failure; } }
int value = 0; // Free Acquire() { while (test&set(value)); // while busy } Release() { value = 0; }
lock Þ no progress!
Idea ea: only busy-wait to atomically check lock value
int guard = 0; int value = FREE; Acquire() { // Short busy-wait time while (test&set(guard)); if (value == BUSY) { put thread on wait queue; go_to_sleep() & guard = 0; } else { value = BUSY; guard = 0; } } Release() { // Short busy-wait time while (test&set(guard)); if (threads on wait queue) { take one off wait queue place it on ready queue; } else { value = FREE; } guard = 0; }
int guard = 0; int value = FREE; Acquire() { while (test&set(guard)); if (value == BUSY) { put thread on wait queue; go_to_sleep() & guard = 0; } else { value = BUSY; guard = 0; } } int value = FREE; Acquire() { disable interrupts; if (value == BUSY) { put thread on wait queue; go_to_sleep() & enable interrupts; } else { value = BUSY; } enable interrupts; }
synchronization primitives
modifications of that variable
globaldigitalcitizen.org