École 42 · 100/100
Philosophers
Le problème classique du dîner des philosophes : des threads qui doivent manger, dormir et penser indéfiniment, sans jamais se bloquer mutuellement.
Philosophers impose de synchroniser N philosophes autour d'une table avec des threads POSIX et des mutex, sans qu'aucun ne meure de faim ni que le programme se bloque. Pour être validé, il doit gérer des cas limites précis : un seul philosophe (qui ne peut jamais réunir deux fourchettes), des dizaines de configurations de timing différentes, et détecter la mort d'un philosophe dans un délai de quelques millisecondes après l'échéance, sans jamais rien afficher après la fin réelle du programme.
Une ressource partagée, pas copiée
Le point central de l'exercice tient en une phrase simple à énoncer et facile à rater en pratique : une fourchette est une ressource partagée entre deux philosophes voisins, protégée par un unique mutex, et non une valeur que chacun détiendrait de son côté.
L'exclusion mutuelle ne protège quelque chose que si tous les threads concernés pointent réellement vers le même verrou, jamais vers une copie locale.
Diagnostiquer une concurrence bloquée
Un programme concurrent qui se bloque ne donne aucune indication directe sur sa cause : rien, de l'extérieur, ne distingue un philosophe qui pense normalement d'un philosophe bloqué sur un mutex. lldb n'étant pas disponible sur cette machine, le diagnostic est passé par l'outil de profiling sample de macOS, qui capture un instantané des piles d'appel de chaque thread à un instant donné.
Prouver plutôt que supposer
La concurrence est un des domaines où « ça a l'air de marcher » est le plus trompeur : un interblocage ou une famine peuvent rester invisibles pendant des dizaines d'exécutions avant de se manifester. Ce projet a ancré l'habitude de construire des cas de test délibérément adversariaux plutôt que de considérer un programme correct simplement parce qu'il tourne sans erreur visible.
Ce que ça m'apporte aujourd'hui
La distinction entre une ressource partagée et une ressource copiée n'est pas restée un détail de cours : c'est le même réflexe qui me fait chercher un vrai verrou (transaction en base, verrou Redis) plutôt qu'une illusion d'exclusion en mémoire dès qu'un système doit gérer un double enregistrement, un webhook traité deux fois, ou n'importe quel scénario où deux processus touchent la même donnée en même temps.
Cette même famille de bugs est aussi une vraie catégorie de vulnérabilités (les failles TOCTOU) : avoir déjà chassé un interblocage à la main me donne une intuition concrète de ce terme, pas juste un nom retenu d'un cours de sécurité. Et savoir diagnostiquer un programme bloqué sans débogueur classique, à coups d'outils d'observation système, est directement transférable à un service qui bloque en production sans qu'on puisse y attacher un débogueur.