Situació de competició és una expressió utilitzada en electrònica i en programació concurrent. Prové de l'anglès race condition (tot i que seria millor parlar destat de competició, igual que es parla destat d'espera). Una situació de competició descriu l'error que es produeix en programes o circuits lògics quan no han estat dissenyats adequadament per a la seva execució simultània amb altres.

Un exemple típic és l'interbloqueig que es produeix quan dos processos estan esperant que l'altre faci una acció. Com que els dos estan esperant, cap arriba a realitzar l'acció que l'altre espera.

Aquest tipus d'errors de programació poden ser aprofitats per exploits locals per a vulnerar els sistemes.

Riscos en la concurrència cooperativa En entorns de programació asíncrona d'un sol fil, com els gestionats per un bucle d'esdeveniments, les situacions de competició no es produeixen a nivell d'instrucció de CPU, sinó en els anomenats punts de suspensió. Aquests punts corresponen a les instruccions marcades amb la paraula clau await, on la tasca actual cedeix el control per esperar una operació d'entrada/sortida.

El risc d'inconsistència apareix quan una corrutina accedeix a un estat compartit mutable, se suspèn, i una altra corrutina modifica aquest mateix estat abans que la primera reprengui l'execució. Tot i que el codi pot semblar seqüencial, l'atomicitat lògica es trenca en cada interrupció del bucle d'esdeveniments. Per mitigar aquests errors, s'utilitzen diverses estratègies de sincronització: Panys asíncrons (Locks): Garanteixen l'exclusió mútua en seccions crítiques que contenen operacions d'espera.

Immutabilitat: L'ús d'estructures de dades que no poden ser modificades un cop creades elimina la possibilitat de canvis inesperats durant la suspensió de la tasca.

Disseny sense estat compartit: L'arquitectura basada en el pas de missatges entre tasques independents evita la dependència de variables globals, reduint la complexitat de la sincronització.

En programació concurrent En programació concurrent, una situació de competició pot aparèixer quan dues o més unitats d'execució accedeixen a un mateix recurs compartit i el resultat final depèn de l'ordre o del moment exacte en què es produeixen aquests accessos. Aquestes unitats d'execució poden ser fils, processos, tasques asíncrones o components d'un sistema distribuït.

Un cas habitual es produeix quan dues tasques llegeixen, modifiquen i tornen a escriure una mateixa variable compartida. Si aquestes operacions no es fan de manera atòmica o no estan protegides per algun mecanisme de sincronització, una de les modificacions pot quedar sobreescrita per l'altra. Això pot provocar resultats inconsistents, pèrdua de dades o errors difícils de reproduir.

Les situacions de competició són especialment problemàtiques perquè sovint depenen de diferències molt petites de temporització. Un programa pot funcionar correctament en moltes execucions i fallar només en determinades condicions de càrrega, planificació del sistema operatiu o ordre d'arribada d'esdeveniments. Aquesta naturalesa no determinista en dificulta la depuració i la verificació.

Recursos compartits i seccions crítiques Una situació de competició sol estar relacionada amb l'accés concurrent a un recurs compartit. Aquest recurs pot ser una variable en memòria, un fitxer, una connexió de xarxa, una entrada d'una base de dades o qualsevol altre element que pugui ser llegit o modificat per diverses parts del programa.

La part del codi que accedeix a un recurs compartit rep sovint el nom de secció crítica. Per evitar inconsistències, és habitual protegir aquesta secció amb mecanismes d'exclusió mútua, de manera que només una unitat d'execució pugui modificar el recurs en un moment determinat.

Els mecanismes més habituals per evitar situacions de competició inclouen bloquejos, semàfors, monitors, variables de condició, cues de missatges i operacions atòmiques. En Python, per exemple, el mòdul threading proporciona objectes de bloqueig que poden estar en estat bloquejat o desbloquejat i que permeten controlar l'accés a una secció crítica.

Relació amb la programació asíncrona Les situacions de competició no són exclusives dels programes multifil. També poden aparèixer en programes asíncrons quan diverses corutines comparteixen estat mutable. Encara que un programa basat en un bucle d'esdeveniments pugui executar-se en un sol fil, les corutines poden suspendre's i reprendre's en punts diferents de l'execució. Si diverses corutines modifiquen el mateix estat compartit sense coordinació, el resultat pot dependre de l'ordre en què el bucle d'esdeveniments les reprèn.

En asyncio, la biblioteca asíncrona de Python, existeixen primitives de sincronització específiques per a tasques asíncrones. Per exemple, asyncio.Lock pot utilitzar-se per garantir accés exclusiu a un recurs compartit, i asyncio.Condition permet que una tasca esperi un esdeveniment i després accedeixi de manera exclusiva a un recurs.

Això mostra que el model asíncron redueix alguns problemes associats als fils, però no elimina completament la necessitat de dissenyar correctament l'accés a l'estat compartit.

Exemple conceptual Un exemple simple és un comptador compartit que dues tasques intenten incrementar al mateix temps. Cada tasca pot llegir el valor actual, sumar-hi una unitat i escriure el nou valor. Si totes dues tasques llegeixen el mateix valor inicial abans que cap d'elles escrigui el resultat, l'increment final pot comptar-se només una vegada en lloc de dues.

Aquest problema no depèn del llenguatge de programació concret, sinó de la intercalació de les operacions. L'ús d'un bloqueig o d'una operació atòmica pot garantir que l'actualització del comptador es faci com una única operació indivisible.

Prevenció La prevenció de situacions de competició requereix identificar quins recursos són compartits i quines operacions han de considerar-se crítiques. Algunes estratègies habituals són:

protegir les seccions crítiques amb mecanismes d'exclusió mútua; reduir l'ús d'estat mutable compartit; utilitzar estructures de dades immutables quan sigui possible; comunicar tasques mitjançant cues en lloc de compartir variables directament; emprar transaccions en sistemes de bases de dades; utilitzar operacions atòmiques proporcionades pel llenguatge o pel sistema.

En sistemes distribuïts o aplicacions amb bases de dades, les situacions de competició també poden aparèixer quan diverses operacions intenten modificar el mateix registre o recurs extern. En aquests casos, es poden utilitzar bloquejos, transaccions, control de concurrència optimista o mecanismes de consistència proporcionats pel sistema de gestió de dades.

Vegeu també Concurrència Programació concurrent Fil d'execució Procés (informàtica) Exclusió mútua Semàfor (informàtica) Secció crítica Interbloqueig Programació asíncrona Bucle d'esdeveniments

Referències

Enllaços externs Starvation and Critical Race Analyzers for Ada Article "Secure programmer: Prevent race conditions-Resource contention can be used against you" by David A. Wheeler Interview with Dmitriy Vyukov - the author of Relacy Race Detector (RRD)

Categoria:Sistemes operatius Categoria:Programació Categoria:Hacking