19 - Concurrency (2)OutlineRace ConditionsCritical SectionsMutual ExclusionMechanisms for Mutual ExclusionDisabling InterruptsLock VariablesAnnouncementsReading:Race ConditionsCritical SectionsMutual ExclusionDisabling InterruptsAny time storage is shared between processes,there is potential for error.2 friends are trying to wire you some money atthe same time.Checking the last transaction in an opaque queueProducer-Consumer Problema.k.a. Bounded Buffer ProblemGiven:Producers and consumers share the bufferYou send duplicate requests to withdraw money.Race Conditions are situations where:Common types of race conditions:Lock VariablesPA 3 test script has been posted.It's a Python script, so you may need to installPython on your VM if you don't have it (hopefullyit's not too large)1)2)3)a)b)MOS 2.4.1 - 2.4.3ExamplesData Races2 or more processes rely on shared dataoutcome depends on instruction interleaving(non-deterministic)Bad InterleavingsError due to memory access by multiplethreads where at least one modifies data.Error due to unfavorable execution order,despite synchronized resources.a fixed-size buffer B with N elementsuncoordinated reads/writes can cause raceconditionsprogrammer is generally responsible foridentifying critical regionsP producer processesproducers put data in the bufferConsider single producer and consumer with thefollowing code:Where's the bug?increment and decrement are not necessarily asingle machine instruction:How do we avoid race conditions?A critical section is a region of program codewhere shared data is accessed.Avoid race conditions: prohibit multiple processesfrom simultaneously executing critical section(s)Make critical sections mutually exclusive by addingentry and exit procedures:Full criteria:There are some additional scenarios to considerwhen avoiding race conditions.Disable all hardware interrupts before enteringcritical section, the reenable them upon leaving.Use a single shared variable (lock) betweenprocesses for synchronizationProblem: now the race condition is on the lock!Problems:i.e. execution of critical sections should bemutually exclusive.A critical section is usually identified by desiredinvariant properties that can be potentially brokendue to interleaving.consumers remove data from the bufferC consumer processes>>>>------------------------1)2)3)4)-----int deposit(account, amount){ balance = get_balance(account); balance += amount; put_balance(account, balance); return balance;}TX check_last(transactions){ TX last = pop_last(transactions); TX last_copy = copy(last); push_last(transactions, last); return last_copy;}// shared globalvolatile int balance;int withdraw(account, amount){ if (amount <= balance) { balance -= amount; }}// shared globalvolatile int balance;int withdraw(account, amount){ if (amount <= balance) { balance -= amount; }}int deposit(account, amount){ balance = get_balance(account); balance += amount; put_balance(account, balance); return balance;}void do_transaction(transactions, tx){ push_last(transactions, tx);}Process 1Process 1Process 1Process 2Process 2Process 2Read-Modify-Write & TOCTOURead-Modify-WriteBad Interleaving (when push/pop atomic)Unintended modification even if some synchronization presentFailure to correctly identify critical regionProducerConsumerConsumerConsumerProducerProducerN itemsbuffer B// shared variablesconst int size;int count = 0;item_t buffer[size];count++ld r1, countadd r1, 1str r1, countdo { work(); enter_critical(); do_critical(); exit_critical(); do_noncritical();} while (cond);while (1) { while (lock != 0); lock = 1; do_critical(); lock = 0; do_noncritical();}while (1) { while (lock != 0); lock = 1; do_critical(); lock = 0; do_noncritical();}while (1) { while (lock != 0); lock = 1; do_critical(); lock = 0; do_noncritical();}while (1) { while (lock != 0); lock = 1; do_critical(); lock = 0; do_noncritical();}Example:void enter_critical(){ interrupt_disable();}void enter_critical(){ while (lock != 0); lock = 1;}void exit_critical(){ interrupt_enable();}void exit_critical(){ lock = 0;}// producerwhile (1) { item_t A = produce(); while (count == size); push(buffer, A); count++;}// consumerwhile (1) { while (count == 0); item_t A = pop(buffer); count--; consume(A);}(assume for now that push and pop are atomic)r1 := countr1 := countr2 := countr2 := countr1 = r1 + 1r1 = r1 + 1r2 = r2 + 1r2 = r2 + 1count := r1count := r1count := r2count := r2producerproducerconsumerconsumeridealactualSome people draw a distinction.Depends a bit on your level of abstraction.more detailsint deposit(account, amount){ balance = get_balance(account); balance += amount; put_balance(account, balance); return balance;}Example:reading/writing balanceinvariant: conservation of money(money is neither created nor destroyed)enter_criticaldisable interruptslock variablessleep() and wakeup()semaphoresmutexesmonitorsPrevents context switching (except syscall)0 = no process in critical regionCan only be done in kernel modeearly Linux kernel versionsembedded systemsDoesn't usually work in multicoreGenerally only used on single-core machines in kernelUser can otherwise misuse or abuseinterrupt disable instruction only works on current core.other cores may still run critical section."Guarantee" only current process running1 = some process in critical regioninitialized to 0strict alternationPeterson's solutiontest-and-set lock (TSL) instructionHardware:Busy Wait:Sleep & Wakeup:Mutual ExclusionNo assumptions of CPU speed or # of coresNo Lockout (Progress)No Starvation (Bounded Waiting)Example:exit_criticalNeed mechanisms forentry and exitprocedures toenforce mutualexclusion...processes outside critical section should not block otherprocesses from entering their critical sectionprocesses should not wait forever to enter critical sectionproc Aproc BA enterscriticalregionA leavescriticalregionB triesto entercriticalregion.B enterscriticalregion.B leavescriticalregion.B is blockedtime