Sei sulla pagina 1di 38

Chapter5Mutualexclusionandsynchronization InterprocessSynchronization Acriticalsectionisasectioncodeinwhichaprocess(orthread)competesinapotentially destructivewaywithanotherprocess(thread)foraccesstoashareddataitemorfile.

We discussedthisparticularexampleintheprevioussection: for (i = 0; i < loopcount; i++) { get_lock(id); val = shared; /* if ((i % 1000) == 0) printf("%d - %d - %d \n", id, i, shared); */ val += 1; shared = val; free_lock(id); } Iftwoprocessesareallowedtoconcurrentlybeincompetingcriticalsections,thenincorrect resultsmaybecomputed. Theprocessofensuringthatthisdestructiveinteractiondoesnooccuriscalledmutualexclusion (mutex).Failuretoensurethatmutexisprovidedwhenrequiredcanhaveliterallyfatal consequencesismedicalandmilitarysystems Objective proc1 while(1) { magicbullet c.s. otherprocessing } >

proc2 while(1) { otherprocessing magicbullet c.s. otherprocessing }

Themagicbullet ensuresonly1processinthecriticalsectionatanytime

PropertiesDesiredinamutexalgorithm: Safe: bothprocessescan'tbeincriticalsectionatthesametime. DeadlockFree (Definiteinfinitepostponement) StarvationFree (Indefinitepostponement) Doesn'trequirestrictalternation(ifotherprocessdoesn'tneedaccesstoc.s.,thena processshouldbeabletoenterimmediately)

PossibleapplicationlevelapproachestoMutualExclusionthatdoanddon'twork: 1.Haveasingleturnvariabletocontrolaccess. int turn = 1; /* must be in shared memory */ p1: while (1) { while(turn == 2); /* must be in shared memory /* this is a LOOP */ c.s.; turn = 1; otherstuff ; } p2: while (1) { while(turn == 1); /* must be in shared memory /* this is a LOOP */ c.s.; turn = 1; otherstuff ; }

*/

*/

Problems: Requiresalternatingaccesstoc.s.Supposep1spends5hoursinotherstuff versus5secondsforp2.) Failsevenifaprocessdiesoutsideofthec.s.

2.Use2variablestocontrolaccesswithtestthenset: int p1using, p2using; /* must be in shared memory */ p1: while(1) { while( p2using == 1 ); /* loop...busy-wait */ /*IFPREEMPTEDHEREBOTHPROCESSESCANGETINTOCS*/ p1using = 1; c.s.; p1using = 0 ; otherstuff ; } p2: while(1) { while( p1using == 1 ); /* loop...busy-wait */ /*IFPREEMPTEDHEREBOTHPROCESSESCANGETINTOCS*/ p2using = 1; c.s. ; p2using = 0 ; otherstuff ; } This"solution" Doesn'trequirealternatingaccess. Processfailureoutsidethec.s.isnotaproblem. ButisUNSAFE...Bothprocessescanbeinc.s.atthesametime. EventhoughthesolutionisUNSAFEitmayappeartooperatecorrectlyformonthsor evenyears,dependingupontherelativesizeofcsandotherstuff. Forfailuretooccurthefollowingsequenceofeventsisnecessary: p1preemptedaftertestingp2usingbutbeforesettingp1using p2dispatchedandthenpreemptedinthecs p1redispatchedandenterstheC.S.

3.Use2variablestocontrolaccesswithsetthentest: int p1using, p2using; /* must be in shared memory */

p1: while(1) { p1using = 1; /* IF PREEMPTED HERE--BOTH PROCESSES CAN DEADLOCK */ while( p2using == 1 ); /* loop...busy-wait */ c.s.; p1using = 0 ; otherstuff ; } p2: while(1) { p2using = 1 ; /* IF PREEMPTED HERE--BOTH PROCESSES CAN DEADLOCK */ while( p1using == 1 ); /* loop...busy-wait */ c.s. ; p2using = 0 ; otherstuff ; } This"solution" Doesn'trequirealternatingaccess. Solvesproblemofotherprocessfails. Issafe Butcancausedeadlock. Fordeadlocktooccur: p1mustbepreemptedaftersettingp1usingbutbeforetestingp2using p2mustthenbedispatchedandtrytoenterthecs.

Thissolutionismorelikelytofailthantheunsafeone.

4.Twovariables,setthentest,withyielding p1: while(1) { RESTART: p1using=1; if(p2using==1) { p1using=0; gotoRESTART; } c.s.; p1using=0; otherstuff; } CanresultinLiveLock. Ifonlyoneprocessyields,thentheotherthreadhaspriorityandthesolutionbecomeslivelock free. p2: while(1) { RESTART2: p2using=1; if(p1using==1) { p2using=0; gotoRESTART2; } c.s.; p2using=0; otherstuff; }

4.Dekker'sAlgorithm(Correct)Twovariables,setthentestwithalternatingyielding. P1: while(1) { p1using=1; Ifnotmyturn while(p2using==1) thenIyield { if(turn==2) { p1using=0; Iunsetmyvariableand while(turn==2); waituntilmyturn p1using=1; } } criticalsection turn=2; p1using=0; otherstuff; } P2: while(1) { p2using=1; while(p1using==1) { if(turn==1) { p2using=0; while(turn==1); p2using=1; } } criticalsection turn=1; p2using=0; otherstuff; }

Peterson'sAlgorithm P1: while(1) { p1using=1; turn=1; while((p2using==1)&&(turn==1)); c.s; p1using=0; otherstuff; } P2: while(1) { p2using=1; turn=2; while((p1using==1)&& (turn==2)); c.s p2using=0; otherstuff }

Notes: Ifonlyoneprocessistryingtoaccessthecriticalsectiontheturnvariableisirrelevant becausetheotherprocessesusingvariablewillbe0. Ifbothprocessesareracingtoenterthecriticalsection,bothusingvariableswillbe1,but theturnvariablecanonlyhaveasinglevalue:either1or2. Thustheprocessthatsetsturnlastwillgetstuckintheloop. Theorderofthesettingofthe"using"andtheturnvariableiscritical. Iftheorderisreversedandaprocessispreemptedaftersettingtheturnvariablebutbefore settingtheusingvariable,thenbothprocessescangetintothecriticalsection!!

Insummary: Preemptioncanoccuranywhere. TheOSdoesn'tinterruptaprocess...thehardwaredoes. TheOScoulddecidenottopreemptaprocess.. Thiswouldrequireasystemcalllike"OSEnterCriticalSection" Wouldalsorequireprocessesnottoabusethisprivilege. Dekker's&Peterson'sAlgorithmsare: Safe(aslongastheSMPhardwareenforcesstrictreadafterwritememaccessorder) Deadlockfree Starvationfree Don'trequirestrictalternation Disadvantagesofapplicationlevelmutualexclusion However,theyhaveseveraldisadvantages: Eachemploysbusywaiting Oneprocessloopswhileotherprocessisincriticalsection BusywaitscanbefatalonaUPwithstrictpriorityscheduling. LowpriorityprocessentersC.S. HighpriorityprocessgetsunblockedandtriestoenterC.S. Highpriorityprocesswillloopforevery Workforonlytwoprocesses(modulowestall'shack2) MaynotworkatallonsomeMP'swithwritebuffering(modulowestall'shack1) Therefore,accordingtomosttexts,theseareoflittleusethetherealworld. (ThoughIactuallyfoundarealworlduseafter15yearsofsearching)

Westall'shack#1(overcomingtheSMPwritebufferingproblem) Inamultiprocessorsystemwithwritebuffering,thisisnotonlysusceptibledodeadlock, itcanactuallybeunsafe. p1using=1; while(p2using==1); c.s. p1using=0; p2using=1; while(p1using=1); c.s. p2using=0;

Westall'shackwilldefeatthisproblem(assumingyoucandetermineFIFO). p1using=1 for(i=0;i<FIFO;i++) dummy[i]=i; while(p2using==1); c.s. p1using=0; p2using=1; for(i=0;i<FIFO;i++) dummy[i]=i; while(p1using=1); c.s. p2using=0;

10

Westall'shack#2 Canbeusedtoprovidemutextoanynumberofcompetingthreadsorprocesses.Wewillassume thenumberisapowerof2.Aprocesswishingtoenterthecriticalsectionmusttraverseabinary treeofPetersontypelockobstaclesandwinthecompetitionattherootofthetree. WhenitcompletesthecriticalsectionitmustunlockeachPetersontypelockitheldinthereverse orderitlockedthem. IfthereareNcompetingthreadsorprocessestheremustbeN1lockstructuresarrangedin log2(N)treelevels 6 4 5 1 1 2 3 4 2 5 6 3 7

0 0

Thelockstructureslooklike: structpete_lock_type { intusing[2];/*0=left1=right*/ intturn;/*Ditto*/ }locks[7]; Eachthreadhasapathtotheroot: structpete_path_type { intlockid; intside;/*=left1=right*/ }paths[8][3]= {{0,0},{4,0},{6,0},//thread0path {0,1},{4,0},{6,0},//thread1path {3,1},{5,1},{6,1}}//thread7path
11

IngeneralDekkerandPetersonshouldbelastresorts OSbasedmutex/synchronizationmechanismsarepreferredforapplicationcode.

Theydon'tusebusywaiting Theydon'trequiretheapplicationprogrammertounderstandthesubtlewaysinwhichad hackmechanismsmayfail. They(should)supportanynumberofcompetingthreads/processes

Semaphores InventedbyDjkstrainlate'60s Supportanynumberofprocesses Eliminatebusywaiting Thesemaphoreisanabstractdatatypethatsupportstwooperations, wait(s) signal(s) Operationofwait&signalsystemcalls..

wait( s ) { if ( s.value == 0 ) suspend( self ) ; else s.value = s.value -1 ; }

signal( s ) { if (processes are suspended on s) unsuspend(exactly one of them) ; else s.value = s.value + 1 ; }

12

Semaphoreexample Fornprocessestosynchronize,alltheyhavetodoiscreateasemaphoreandthenuseit.

semaphore:sem; process1: while(1) { wait(sem); cs; signal(sem); otherstuff; }

/*valuedefaults1*/ process2: while(1) { wait(sem); cs; signal(sem); otherstuff; } process3: while(1) { wait(sem); cs; signal(sem); otherstuff; }

13

ImplementationofSemaphores Semaphorestructure: value lock queue | | V pcb>pcb>pcb structsemtype { intvalue;/*Anonnegativevalue*/ unsignedcharlock; /*Usedtoserializeaccesstovalue*/ structpcbtype*queue; /*Queueofblocked(waiting)pcbs*/ }

14

A(broken)implementationofwait() wait(structsemtype*sem) { if(sem>value>0) /**sem,apointertothesemaphorestucture*/

sem>value=sem>value1; else } Thisimplementationcanfailif: Twoprocessesattempttowaitonthesamesemaphore. Thevalueofthesemaphoreis1and process1ispreemptedaftertestofsem>valuebutbeforeitisdecremented. ==>Testingandresettingofthesemaphorevalueisitselfacriticalsection suspendself(sem>queue); /*suspendmeonthesemaphorequeue*/

15

Possiblesolutions: Disableinterruptsbeforeenteringacriticalsection Reenableinterruptsafterexitingthecriticalsection. Thisguaranteesnopreemptionwithingthecriticalsection. ThissolutioniscorrectforasingleCPUsystem. Settingaglobaldon'tpreemptflagwillalsowork. Usingaglobaldon'tpreemptpolicyinkernelmodeasisthecasewithtraditionalUnix implementationswillalsowork. ALLthesesolutionswillfailonamultiprocessorsystem becausetwoprocessorscouldberunningthesemaphorecodeinlockstep.

16

TheTestandSetMechanism WhenComputerSciencehitsthewallitscommontocalluponComputerEngineeringto provideahardwaremechanismthatprovidesthesolution. Thetestandsetinstructionprovidesahardwarelockingmechanismthatcanbeusetosafely implementsemaphoresinaSMPsystem. voidsetlock( int*lockvar) { CLI RETRY: CMPI lockvar,0 JNZ retry TS lockvar JNZ RET retry

/*Disableinterrupts.

*/

/*readthelockvarandseehowitlooks. */ /*ifnotzeroretryit. */ /*itwillunconditionallysetlockvarto1.itwillsetacondition flagtoindicatethevalue(0or1)beforeTSisexecuted*/ /*jumpnotzero;1=>locked,0=>available*/

voidrlselock( int*lockvar) { MOVIlockvar,0/*Unlockthelock*/ STI /*Enableinterrupts*/ RET }

17

TShardwaresupportserializesaccesstosharedmemorybyallprocessors. CPUmemoryinamultiprocessorsystem CPU CPU CPU CPU | | | | cache cache cache cache | | | | SYSTEMBUS | MainMemory

18

ConsiderationsintheuseofTS: InterruptsmustbedisabledwhileholdingaTSlockbecause onasingleCPUsystemwithprioritydispatching alowpriorityprocessispreemptedholdingthelock, ahighpriorityprocesstriestoobtainthelock... thenthetwoprocessesaredeadlocked. ona2CPUprocess,thedeadlockisnotguaranteedbutcanoccurif. numberofprocessescontendingforalock=numberofprocessors Whenreleasingthelock,theenablingofinterruptsMUSTFOLLOW,NOTPRECEDE, thereleaseofthelock. Onasingleprocessorsystem,thesemaphoreimplementation... Issafewithoutthelock..causesnoharmifthelockingcodeisincluded. Thecostisafewextrainstructions. OnasingleCPU,theTSwillalwayssucceedonthefirsttry (Orelseyouhavewhatiscommonlycalledasystemcrash)

19

Thecompleteimplementationofwait: wait(structsemtype*sem) /**sem,apointertothesemaphorestucture*/ { setlock(&sem>lock); if(sem>value>0) { sem>value=(sem1); rlselock(&sem>lock); } else suspendself(sem>queue); /*suspendmeonthesemaphorequeue*/ /*mustreleaselockafterPCBonsemqueue*/ } Asanexercise,implementsignal().

20

ComparisonofSemaphoresandLocks: Locks Usebusywaiting(loopwhilewaiting) HeldonlyinsideOScode Holdermustnotbepreempted Holdingtimemustbeshort. Semaphores Useblockedwaiting(suspendedwhilewaiting) Applicationand(someOS)codemayusethem Holdermaybepreempted Holdingtimecanbearbitrarilylong.

Binaryvs.CountingSemaphores Binarysemsonlytakeonvaluesof0or1 Signalingabinarysemaphorewithvalue1istypicallyasignofalogicerror. Countingsemaphorescanhaveanypositivevalue. Binarysemaphoresaretotallysufficientformutex. Countingsemaphoresprovideextracapabilityusefulinothersynchronizationproblems.

21

SimulatingSemaphoresinUnix OriginalUnixhadnosemaphoresupport. RecentUnixversionssupportbothsharedmemoryandsemaphoresviaapainfulAPI Itseasytoemulateacountingsemaphoreusingapipe. Creatingasemaphorecorrespondsto: intpipedef[2]; pipe(pipedef);/*Createasinglepipeandgetbackreadandwritehandles*/ Signalingasemaphorecorrespondsto: write(pipedef[1],"T",1); pipedef[1]isthewritehandle The"T"representsa1bytetoken The1sayswrite1byteintothepipe. Waitingonthesemaphorecorrespondsto: read(pipedef[0],buff,1); pipedef[0]isthereadhandle buff[0]willrecievethe"T"

22

ClassicalCooperatingProcessProblems TheProducer/ConsumerProblem a.k.a(Boundedbuffer||Circularbuffer) Probleminvolvesboth mutexand generalsynchronization Thebufferisacirculararrayofslots Twoindices/pointersareusedtomanagebufferaccess in_slot out_slot identifiesthenextslotinthebufferinwhichanewitemwillbeplaced identifiesthenextslotinthebufferfromwhichanitemwillbetaken

WhenimplementedinhardwaresuchbuffersarecommonlycalledFIFOs

23

Synchronizationproblems Whenthebufferisemptyconsumersmustbeblockeduntilanitemisproduced Whenthebufferisfullproducersmustbeblockeduntilspaceisavailableinthebuffer. Countingsemaphoresthatcountbothfreeslotsandavailableitemscanaccomplishbothofthese missions. Mutexproblems Iftherearemultiplecompetingproducers,twoormoreofthemmustnotbeallowedtoproduce intothesameslot. Iftherearemultiplecompetingconsumers,twoormoreofthemmustnotbeallowedtoconsume thesameitem. Ifthereisonlyasingleproducerandasingleconsumer,themutexproblemsdon'texistbutthe syncrhonizationproblemremainscritical.

24

Theproducersolution #define SLOTS 32 sem sem sem sem int int cmutex; pmutex; slot_free; item_avail; in_slot; out_slot; /* This thread contains the producer code */ // or whatever

producer() {

/* Initialize slot_free semaphore */ for( i = 0 ; i < SLOTS ; i++ ) signal(slot_free) ; in_slot = 0 ; while( 1 ) { wait(slot_free) ; wait(pmutex ) ; buffer[in_slot] = produce_item(); in_slot = (in_slot + 1) % SLOTS ; signal(pmutex ) ; signal(item_avail) ; } }

25

Theconsumersolution consumer() /* This thread contains the consumer code */ { out_slot = 0 ; while( 1 ) { wait( item_avail) ; wait(cmutex ) ; consumed_item = buffer[out_slot] ; out_slot = (out_slot + 1) % SLOTS ; signal(cmutex ) ; signal( slot_free ) ; } }

26

MutexConsiderations Singleproducerandsingleconsumer: Mutexesnotneededforacorrectsolution. Multipleproducersorconsumers: Separateconsumermutexandproducermutexasshownallowsmostparallelism Useofasinglemutexcanworkbutmorecaremustbetaken. Orderofwaits(butnotsignals)becomescritical. Ifasinglemutexusedbyboththeproducerandtheconsumer: If wait(mutex) occursbefore wait(slot_free) orwait(item_avail): Iftheconsumergetsthewaitsinthewrongorder,thesystemcandeadlockwhen thethebufferisempty.Iftheconsumerstartsbeforetheproducerstarts. Theconsumerwillblockon item_availwhileholdingmutex. Theproducerthenstartsupandblockspermanentlyonmutex. Iftheproducergetsthewaitsinthewrongorderdeadlockcanoccuronlywhenthe bufferisfull.Inthiscasetheproducerwillblockonslot_free whileholding mutex. Theconsumerwillthenblockpermanentlyon mutex whenittriesto consumeanitem. Withpmutexforproducersandcmutexforconsumers,theorderofwaitsisirrelevant.The orderofsignalsisalwaysirrelevantbecausesignalneverblocks. Sharedmutexisundesirablebecauseitpreventsoverlappedproductionandconsumption. Caneachproducerandeachconsumerhaveit'sownsemaphore? NO,thatwouldbreakthecodecompletely. Bydefinititionforamutexsemaphoretoworkatall,allprocessesrequiring mutualexclusionmustusethesamemutexsemaphore!
27

Canaprocessbebothaproducerandaconsumer? Yes... Child2inmp1wasone Process1 Producer Process2 ConsumerProducer |_|_|_|_|_|_|_|_|_| buffer1 Process3 Consumer

|_|_|_|_|_|_|_|_|_| buffer2

Process2willhavethefollowingorganization: while(1) { usualconsumercodeforbuffer1; possiblyperformsomemannerofoperationonconsumeddata usualproducercodeforbuffer2; (producercodemayormaynotbecalledbasedonresultofoperation) } Canproducer/consumerproblemsbesolvedwithoutusingsemaphores? Certainly!Youremailfilterprogramisaclassicexample!

28

TheReadersandWritersproblem FrameworkoftheProblem: Aresourceexiststhatcanbereadorcanbewrittento. Multipleprocessesthatcansafelyreadconcurrently Onlyoneprocesscanwriteatatimeandonlyifnobodyisreading. Thereare3possiblesolutionobjectives: ReaderpriorityArrivingreadersmaypasswaitingwriterstojoinexistingreaders.This mayleadtowriterstarvation. WriterpriorityArrivingwritersbypasswaitingreadersinthequeue.Thismaximizes concurrentreadingbutmayleadtoreaderstarvation. StrictFIFONopassingbyeitherreadersorwriters.Thisminimizesthelevelof concurrentreading,buteliminatesstarvation...butwithapotentialnastysideeffectsif readersreadforaVERYLONGTIME. Therearesemaphorebasedsolutionstothesebuttheyareverytricky. Asafe(butdefective)solution writer() { | wait(wsem); write(); signal(wsem); | } Thissolutiondoesn'tallowconcurrentreading. reader() { | wait(wsem); read(); signal(wsem); | }

29

TheReaderPrioritySolution readersbypasswaitingwriterstojoinexistingreaders. writer()//sameasinthedefectivesolution { | wait(wsem); write(); signal(wsem); | }

30

reader() { | wait(rmutex); rcount+=1; /*countofreadersinsystem,increment*/ if(rcount==1) /*nopreexistingreaders,thisisthefirst*/ wait(wsem); signal(rmutex); read(); wait(rmutex); rcount=1; /*decrementcountofreaders*/ if(rcount==0) signal(wsem); signal(rmutex);

Ifwritingistakingplacewhenreadersarrive: Firstreadergetsblockedonwsem... Subsequentreadersgetblockedonrmutex Subsequentwritersgetblockedonwsem Wheneveronereaderisallowedtoread,allexistingreaderswilljoinit. lastarrivingfirstarriving Example: Wtr Wtr Rdr Rdr Rdr Wtr Wtr Wtr Wtr Wtr blockedon: wmtx wmtx rmtx rmtx wsem wsem wsem wsemwsemwriting Nevermorethanonereaderblockedonwsem. Starvationissues: Writerscangetblockedoutforever(starvation)ifthereareenoughreaders. ReaderstarvationisNEVERpossible.

31

TheStrictFIFOsolution WithStrictFIFO,areadercan'tbypasswaitingwriterstojoinexistingreaders. Semaphorebasedimplementationexistsandbutisevenmorecomplex! Rdr Rdr Wtr Rdr Rdr Rdr Wtr Wtr ||Rdr/Rdr/Rdr

AneasierapproachistojustinventanewO/Ssystemcalldesignedspecificiallyforthispurpose (Thismechanismissometimescalledamonitor). Thisimplementationisusedtoserializereadersandwritersistheenqueueanddequeue mechanismprovidedbyMVS Enqueueanddequeueare higherlevelprimitivesthanwaitandsignal TheyareimplementedintheMVSkernel. Togainaccesstotheresourceanapplicationmakesthesystemcall: enqueue(ResourceID,"r")orenqueue(ResourceID,"w") Whencompletetheprocessmakesthesystemcall: dequeue(ResourceID)

32

Apossibleimplementation: TheoperatingsystemisresponsibleformanagingaFIFOqueueofwaitingPCB'sassociatedwith eachresource. 16 17 18 19 20 21 28 29 30 31 32 33 34 35 36 struct qeltype { struct pcbtype *pcb; int action; struct qeltype *qnext; }; struct qcbtype { int state; int numusing; int numwaiting; int lock; struct qeltype *qhead; struct qeltype *qtail; };

/* Pointer to PCB /* Read or write /* Next element in list

*/ */ */

/* /* /* /* /* /*

0 Free, 1 Read, 2 write Active of readers/writers Waiting readers/writers TS lock for serialization Head of wait queue Tail of wait queue.

*/ */ */ */ */ */

void enqueue(struct qcbtype *qcb, int action) { get_lock(&qcb->lock); if (qcb->state == FREE) { qcb->numusing += 1; qcb->state = action; goto out; } if ((action == READ) && (qcb->state == READ) && (qcb->numwaiting == 0)) { qcb->numusing += 1; goto out; } qel = get_qel(); qel->pcb = current; qel->action = action; qel_queue_tail(qcb, qel); rlse_lock(&qcb->lock); schedule(); return; out: rlse_lock(&qcb->lock); }

33

ConditionVariables Asynchronizationprimitivethatismoregeneralthanthesemaphorewhichcanbeusedtosolve theReadersandWritersProblem(andotherproblems). Declaring: pthread_cond_t pthread_mutex_t Initializing: pthread_cond_init(&cv, NULL); pthread_mutex_init(&mtx, NULL); Usage:Onethreadiswaitingforsomeconditiontobecometrue(forexample,thereisanitemina buffer) pthread_mutex_lock(&mtx); while (some-condition-is-not-true) pthread_cond_wait(&cv, &mtx); pthread_mutex_unlock(&mtx); Anotherthreadcausessomethingtohappen(placesaniteminthebuffer) pthread_mutex_lock(&mtx); make-some-condition-become-true pthread_cond_broadcast(&cv); pthread_mutex_unlock(&mtx); Operation: pthread_cond_wait(&cv, &mtx); Releasethemutexandblockthread Onwakeupreacquirethemutex pthread_cond_broadcast(&cv); WakeupALLTHREADSwaitingonthemutex cv; mtx;

34

Mutexrequirements Itscriticalthatthewaitingthreadlockthemutexbeforetestingtheconditionandpotentiallygoing tosleep.Itisalsocriticalthatthecausingthreadlockthemutexensuringthatmakingthe conditiontrueandbroadcastingtheconditionbeingatomic. pthread_mutex_lock(&mtx); while (some-condition-is-not-true) pthread_cond_wait(&cv, &mtx); pthread_mutex_unlock(&mtx); Anotherthreadcausessomethingtohappen: pthread_mutex_lock(&mtx); make-some-condition-become-true pthread_cond_broadcast(&cv); pthread_mutex_unlock(&mtx); Ifnotthefollowingeventscanhappen. thread0testtheconditionandfinditfalse thread1maketheconditionbecometrue thread1broadcasttheconditionvariable(nothreadsareblockedsonothinghappens) thread0blockontheconditionvariable(possiblyforever!!!) UnlikethecountingsemaphoretheCVdoesnothaveavaluethatallowsittoaccumulate broadcastcredits

35

Semaphorescanbereadilyconstructedwithconditionvariables struct semtype { int value; pthread_cond_t cv; pthread_mutex_t mtx; }; void wait(struct semtype *s) { pthread_mutex_lock(&s->mtx); while (s->value == 0) { pthread_cond_wait(&s->cv, &s->mtx); } s->value -= 1; pthread_mutex_unlock(&s->mtx); }

void signal(struct semtype *s) { pthread_mutex_lock(&s->mtx); s->value += 1; pthread_cond_broadcast(&s->cv); pthread_mutex_unlock(&s->mtx); } Exercise:Showhowintheabsenceofthemutexinthesignalcodethesemaphorewouldnot workreliably.Wouldthefailurebehard(everytimesignalcalled)ortransient(muchofthetime itmightappeartoworkcorrectly)?

36

Theproducersolution #define SLOTS 32 int int int int slots_free; items_avail; in_slot; out_slot; // or whatever

pthread_cond_t scv; pthread_mutex_t smtx; pthread_cond_t icv; pthread_mutex_t imtx; producer() { /* This thread contains the producer code */

/* Initialize slots_free counter */ slots_free = SLOTS; in_slot = 0 ; while( 1 ) { /* wait until a slot is available */ pthread_mutex_lock(smtx); while (slots_free == 0) { pthread_cond_wait(scv, smtx); } pthread_mutex_unlock(smtx); /* produce an item into it */ buffer[in_slot] = produce_item(); in_slot = (in_slot + 1) % SLOTS ; /* signal that an item is available */ pthread_mutex_lock(imtx); items_avail += 1; pthread_cond_broadcast(icv); pthread_mutex_unlock(imtx); } }

37

Theconsumersolution consumer() /* This thread contains the consumer code */ { out_slot = 0 ; while( 1 ) { /* wait until an item is available */ pthread_mutex_lock(imtx); while (items_avail == 0) { pthread_cond_wait(icv, imtx); } pthread_mutex_unlock(imtx); /* Consume the item */ consumed_item = buffer[out_slot] ; out_slot = (out_slot + 1) % SLOTS ; /* Signal a slot is now available */ pthread_mutex_lock(smtx); slots_avail += 1; pthread_cond_broadcast(scv); pthread_mutex_unlock(smtx); } } Exercise:Willthissolutionworkif:scv=icv=cvandsmtx=imtx=mtx? Exercise:Howshouldthesolutionbechangediftwoproducersareactive??

38

Potrebbero piacerti anche