Sunteți pe pagina 1din 15

Vericarea programelor concurente

14 noiembrie 2011

Probleme asociate programelor concurente

Blocaj (deadlock) Livelock (ciclare care nu produce progres util) Starvation: inechitate n accesul la resurse (re de execut ie care nu primesc acces; f ar a blocaj pe ansamblu) Condit ii de curs a (race conditions) n particular: privind valorile datelor (data races) Atomicitate nerespectat a instruct iuni surs a simple (++) pot neatomice n cod ma sin a sau: atribuiri pe dimensiuni de mai multe cuvinte de memorie

Condit ii de curs a pentru date (data races)


Se nt ampl a c and dou a re de execut ie acceseaz a o variabil a si cel put in un acces e de scriere cele dou a re nu folosesc vreun mecanism explicit de sincronizare Analiza condit iilor de curs a e complicat a de reordon ari ntr-un r de execut ie (prin optimiz ari de compilator) init: x = 0; y = 0; Final (r1, r2) posibil: t1: r1 = x; t2: r2 = y; y = 2; x = 1; Dar prin reordonare n t1 sau t2 putem obt ine si r1 = 1, r2 (0, 0) (1, 0) (0, 2) =2!

Acest rezultat nu corespunde modelului de consistent a secvent ial a (cu care suntem obi snuit i intuitiv): toate accesele la memorie corespund unei ordini totale, si ordinea acceselor din orice r e ordinea din program.

Modelul de memorie n Java


Un limbaj concurent trebuie s a aib a un model de memorie intuitiv, si care s a nu limiteze performant a, restrict ion and optimiz arile. Solut ia [Manson, Pugh, Adve, PLDI05]: denirea unei clase de programe care sunt bine sincronizate (data race free), pentru care se asigur a consistent a secvent ial a + garant ii minimale pentru restul programelor (chiar dac a sunt incorect sincronizate) Principiul: denirea unei relat ii de ordine happens-before [Lamport] ntre act iunile programului, provenind tranzitiv din: ordonarea act iunilor de sincronizare ( ntre unlock si orice lock pe acela si monitor si intre o scriere de variabil a volatil a si citirea ei) si ordinea din program ( n cadrul relor de execut ie)

Volatile si sincronizarea

Citire variabil a volatile: ultima valoare scris a n ordinea de sincronizare. Citirea unei variabile non-volatile: poate returna orice valoare care nu e scris a ulterior n happens-before si nu e perimat a de alt a scriere

Volatile si sincronizarea

Citire variabil a volatile: ultima valoare scris a n ordinea de sincronizare. Citirea unei variabile non-volatile: poate returna orice valoare care nu e scris a ulterior n happens-before si nu e perimat a de alt a scriere Atent ie: volatile NU nseamn a atomic! Condit ie de curs a = accese conictuale (r-w, w-w) neordonate prin happens-before. Program bine sincronizat = nu are condit ii de curs a.

De ce sunt greu de vericat programele concurente?

Int elegerea problemelor de concurent a e adesea dicil a E dicil de exercitat o anume secvent a de execut ie necesit a controlarea/modicarea planicatorului/condit iilor externe Secvent ele de eroare pot foarte rare ( n situat ii complexe anume) Condit iile de eroare sunt dicil de reprodus (Heisenbugs) Explorarea exhaustiv a a secvent elor de execut ie e nefezabil a (exponent ial n num arul relor de execut ie / dimensiunea lor)

Tipare de erori n programe concurente


[dup a Farchi, Nir, Ur IBM Haifa] Ignorarea non-atomicit a tii x = 0 || x = 0x101 posibil x == 1 (!!) dac a cei doi octet i sunt scri si separat (hi din 0, low din 0x101) Acces n dou a etape chiar dac a sunt protejate, obiectul se poate schimba ntre... lock(); idx = table.find(key); unlock(); if (...) { lock(); table[idx] = newval; unlock(); } Lac at omis / gre sit (un nou programator a uitat...) t1: synchronized(o1) { n++; } t2: n++; sau t1: synchronized(o1) { n++; } t1: synchronized(o2) { n++; }

Tipare de erori n programe concurente (cont.)


Double-checked locking: optimizarea init ializ arii la cerere

class Foo { private Helper helper = null; public Helper getHelper() { // evita synchronized la orice ap if (helper == null) // deja alocat ? se returneaza synchronized(this) { // sincronizare doar cand nu e nealoc if (helper == null) // a doua verificare e protejata helper = new Helper(); // dar adresa poate fi atribui } // inainte de a face initializarile din construct return helper; // si alt fir va vedea un obiect creat incomp } }

Problema: compilatorul e liber s a fac a reordon ari pentru optimizare

Tipare de erori n programe concurente (cont.)


Situat ii presupuse imposibile (dar care se nt ampl a): sleep() folosit gre sit pentru a garanta o nt arziere Lost Notify: se pierde c and e executat nainte de wait: t1: synchronized(o) { o.wait(); } || t2: synchronized(o) { o.notifyAll(); } Wait nevericat: la revenire trebuie vericat a condit ia a steptat a (revenirea se putea nt ampla si din alte cauze) Situat ii de blocare Cod scris presupun and c a sect iunea critic a nu se blocheaz a fals, dac a codul e furnizat (eronat) de altcineva... Fire de execut ie orfane dac a rul care le-a creat se termin a eronat poate duce la blocare

Solut ii pentru testarea de modul (unit testing)

Implicit, JUnit observ a rul care a lansat execut ia testului nu detecteaz a except ii n re de execut ie create ulterior Solut ie: Concutest-JUnit [Rice University] creeaz a/observ a un grup de re de execut ie avertizeaz a dac a re create mai ruleaz a dup a ncheierea rului principal (ar trebuit a steptate cu un join ...) poate insera nt arzieri arbitrare genereaz a alte secvent ieri

Solut ii: testare la nivel de sistem

Ideea: crearea de variat ie n planicarea relor de execut ie ConTest [IBM Haifa] instrumenteaz a programul (sleep(), yield(), etc.) sau simuleaz a nt arzieri/pierderi de mesaje variat ie aleatoare n planicare m asoar a acoperirea raportat a la toate planic arile posibile CHESS [Microsoft Research] capteaz a funct ii de sincronizare genereaz a sistematic execut ii cu planic ari noi n ordine cresc atoare a nr. de ntreruperi (preemptions) poate reproduce execut iile generate

Eraser: detectarea condit iilor de curs a


Combin a analiza dinamic a si static a prin analiza unei execut ii determin a erori potent iale n altele ret ine lac atele det inute de ecare r de execut ie ncearc a s a deduc a ce lac at protejeaz a ecare obiect partajat // pentru ecare variabil av init: C (v ) = all locks ; // la acces n rul t access: C (v ) = C (v ) locks held (t ); if C (v ) = warning(); // acces neprotejat! Extins, poate distinge ntre lac ate pentru citire si scriere, urm arind starea ec arei variabile (virgin, exclusive, shared, shared-modied) Algoritm conservator, poate da alarme false pentru programe corecte (care nu asociaz a o variabil a cu un lac at unic pe ntreaga execut ie)

High-level data races


[Artho, Havelund, Biere 2003] Erori: c and granularitatea variabilelor protejate variaz a:
void swap() { synchronized(this) { lx = c.x; ly = c.y; } synchronized(this) { c.x = ly; c.y = lx; } } void reset() { synchronized(this) { c.x = 0; } synchronized(this) { c.y = 0; } }

Accesul la membri e sincronizat, dar swap si reset pot s a interfereze! Analiz a nu doar dpdv al variabilelor (ce lac ate le protejeaz a) dar si dpdv al lac atelor (ce mult imi de variabile acoper a ecare)

Java PathFinder [NASA]: vericare prin model checking

Un sistem de vericare prin explorare complet a a programului simuleaz a nedeterminismul printr-o ma sin a virtual a proprie care permite alegerea alternativelor de planicare la ecare pas si revenirea la cele neexplorate nc a (vezi: backtracking) Permite vericarea (pornind de la bytecode) situatiilor de blocaj condit iilor de except ie asert iunilor din cod Limitat la programe mici (10 kloc): explozia spat iului st arilor dimensiunea st arilor memorate (nr. variabile n program) num arul de execut ii posibile (exponent ial n nr. threads)