Mètodes

42 (VIII): philosophers, fils a taula

Filòsofs que mengen, pensen i moren de fam: concurrència, mutex i deadlocks

3 min de lectura

so_long tenia finestra i sprites (ho vaig contar a 42 (VII): so_long, el primer joc); philosophers no té res a la vista: un terminal que imprimeix línies i, si ho fas malament, un programa que es penja o declara morts que estan vius. És el problema clàssic dels filòsofs a taula: N filòsofs al voltant d'una taula circular, un tenidor entre cada parella, i per menjar calen els dos de costat. Si un no menja dins del temps donat, mor, i el log (el diari del programa) ho ha de dir sense barrejar línies ni afegir res després.

El problema de veritat

Aquí no s'ordenen dades: es coordinen fils (un fil és una línia de treball dins del programa; diversos fils van alhora). El perill té tres cares. El deadlock, un bloqueig en cercle on cada fil espera el que té al costat: si tots els filòsofs agafen primer el tenidor esquerre, tots queden esperant el dret per sempre. Les condicions de carrera, quan dos fils toquen la mateixa dada sense coordinar-se: el moment de l'últim àpat l'escriu un fil i el llegeix un altre; sense protecció, de tant en tant algú mor per error o sobreviu sent mort. I la precisió temporal: usleep, la pausa que ofereix el sistema, no dorm exactament el que li demanes, i mig mil·lisegon de desfase és la diferència entre un filòsof viu i un mort.

Com ho vaig resoldre

Un fil per filòsof i un mutex per tenidor (un mutex és un pany: mentre un fil el té, cap altre pot passar). Contra el deadlock, trencar el cicle: els filòsofs parells agafen primer el tenidor dret i els senars primer l'esquerre; així ningú queda bloquejat en cercle. Contra les carreres, estat compartit mínim i protegit: el flag de parada (la senyal que ordena aturar-ho tot) i el log tenen el seu mutex, i cada filòsof protegeix el seu moment d'últim àpat i el comptador de menjars amb un mutex propi. Un monitor al fil principal recorre la taula cada mig mil·lisegon comprovant si algú ha superat el temps o si tots han menjat el que tocava, i quan toca aturar-ho tot, ho atura. Per dormir amb precisió vaig fer un sleep a trossos petits que comprova la parada, i un desfase inicial entre parells i senars perquè no arrenquin tots alhora amb la mateixa gana. El cas d'un sol filòsof, amb un únic tenidor, va per camí propi: l'agafa, el log ho diu, i mor.

Una curiositat: tinc dos repositoris d'aquest projecte, philosophers i 42_philosophers. El primer funcionava, però el vaig repetir amb l'estructura més neta que havia anat aprenent; comparar-los és veure com havia canviat la meva manera de partir un problema.

Què m'enduc

Philosophers em va deixar una desconfiança sana: el bug de concurrència no surt cada vegada, surt un de cada cinquanta execucions, així que vaig acabar provant el programa en bucles de centenars d'execucions abans de dir que anava bé. També vaig entendre que un mutex no és màgia sinó disciplina: saber exactament quines dades toca qui i protegir només allò que cal. I que els problemes clàssics dels anys seixanta i setanta continuen ensenyant coses que cap tutorial modern explica tan bé.