Lecture 9 --------- Semaphores/mutual exclusion/locks concurrency producer consumer using character devices deadlocks The problem of mutual exclusion ------------------------------- - how do we keep different "cores" (processes/threads) from stepping on each other, corrupting shared data structures? - this *cannot* be done in standard C, you must have lower level help (like making a system call) - natural idea is to have a variable, a "lock", which tells who owns the data structure - but regular variables are not safe under concurrent access - "variables" are multiple things - address range in RAM - L1/L2/L3 cache - in register(s) - load/store architecture means we - load RAM-based data into registers - change the registers - save registers to RAM - (with cache in the middle) - so what if two cores load data from the same memory locations into registers *at the same time*? RAM x = 1 CPU1 loads x (in a register) <-- loaded x from RAM CPU2 loads x (in a register) <-- loaded x from RAM CPU1 increments x CPU2 increments x CPU1 saves x CPU2 saves x what's the value of x? - could be 2 or 3 depending on the order of operations this is a race condition - semantics of the program depends on the relative rate of execution of "independent" execution units race conditions lead to nondeterministic behavior in order to make effective locks, we need to be able to check the value of a variable and change it...as an atomic operation s = 1 <== lock is unlocked s = 0 <== lock is locked to "take" the lock check value of s if s > 0 <-- these both have to be decrement s (take lock) <-- done with *1* CPU instruction do stuff (must be an atomic operation) increment s (release lock) otherwise wait until s > 0 to do the above