| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
This project has been created as part of the 42 curriculum by dporhomo
Codexion is a modernized, advanced variation of Edsger W. Dijkstra’s classic "Dining Philosophers" concurrency problem. Instead of philosophers and forks, this simulation features coders and USB dongles. The goal of the project is to safely orchestrate multiple threads (coders) competing for limited shared resources (dongles) to compile code without "burning out" (starving).
Unlike the traditional problem, this simulation introduces mandatory hardware cooldowns and strict arbitration policies. Access to shared resources is strictly regulated by a custom-built Min-Heap Priority Queue, enforcing either FIFO (First In, First Out) or EDF (Earliest Deadline First) scheduling algorithms to guarantee fair access and prevent starvation.
The project includes a Makefile that compiles the source files. To compile the project, run:
make
Other available commands:
Run the executable with the following mandatory arguments:
./codexion <num_coders> <time_to_burnout> <time_to_compile> <time_to_debug> <time_to_refactor> <req_compiles> <cooldown> <scheduler>
During development, specific edge cases were used to detect architectural faults and harden the simulation against deadlocks and data races. You can test them using the following commands:
Verifies basic functionality, scheduling, and standard lifecycle. Below is a sample of a correct, parallel execution where coders successfully reach the required 2 compiles without burning out:
0 1 has taken a dongle
0 1 has taken a dongle
0 1 is compiling
0 3 has taken a dongle
0 3 has taken a dongle
0 3 is compiling
200 1 is debugging
200 3 is debugging
300 2 has taken a dongle
300 2 has taken a dongle
300 2 is compiling
300 4 has taken a dongle
300 4 has taken a dongle
300 4 is compiling
400 1 is refactoring
400 3 is refactoring
500 2 is debugging
500 4 is debugging
600 1 has taken a dongle
600 1 has taken a dongle
600 1 is compiling
600 3 has taken a dongle
600 3 has taken a dongle
600 3 is compiling
700 2 is refactoring
700 4 is refactoring
800 1 is debugging
800 3 is debugging
900 2 has taken a dongle
900 2 has taken a dongle
900 2 is compiling
900 4 has taken a dongle
900 4 has taken a dongle
900 4 is compiling
1100 2 is debugging
1100 4 is debugging
--- Simulation Finished Safely ---
./codexion 2 200 100 0 0 2 0 fifo
This highly constrained environment initially caused an AB-BA Lock Inversion between the Monitor thread (trying to print a burnout) and a Coder thread (trying to grab the arbitrator). It led to the architectural decision to unlock the arbitrator early before executing terminal output.
./codexion 4 1000 200 200 200 2 0 edf
Compiled with -fsanitize=thread or run with valgrind --tool=helgrind, this test verified the absolute absence of Data Races. It exposed circular wait potentials, when the last coder grabs the dongle which the first coder also grabs, leading to the implementation of Hierarchical Locking and a dedicated state_lock (comparing the integer IDs of the two required dongles and forcing the coders to strictly lock the lower-numbered mutex first, breaking the circular wait graph).
Common Tests
This simulation actively mitigates all major concurrency pitfalls:
To ensure thread-safe communication and state management, the architecture relies heavily on POSIX synchronization primitives:
pthread_mutex_t:
Dongles Array: Each physical dongle has its own mutex. While the logical state (free/cooldown) is managed by the arbitrator, the actual locking of the dongle mutex guarantees absolute exclusive access.
Print Lock: Ensures printf calls are safely serialized.
Arbitrator: The core "global lock" of the simulation. It protects the Priority Queue array, the dongle_ready_time arrays, and condition variable checks.
State Lock: A lightweight, dedicated lock explicitly used to protect reads and writes of shared state variables (like sim_active, last_compile, and compiles_done), completely eliminating Data Races without bottlenecking the main arbitrator.
pthread_cond_t:
Coder Condition Array: Instead of busy-looping, a coder thread evaluates if it can compile. If it cannot, it calls pthread_cond_wait (or pthread_cond_timedwait for cooldowns). This safely puts the thread to sleep and unlocks the arbitrator for others.
When a coder drops their dongles, they call pthread_cond_signal on the sleeping threads, instructing them to wake up, re-lock the arbitrator, and check if their requested dongles are now available.
Preventing Race Conditions: A race condition could occur if a coder checks the priority queue, determines they can compile, but gets suspended by the OS before actually locking the dongles. Because the entire can_compile evaluation, queue extraction, and LLONG_MAX state claim happens sequentially inside the locked arbitrator mutex, the state is mathematically guaranteed not to change.
Thread-Safe Communication with Monitor: When the monitor detects a burnout, it must cleanly shut down coders who are currently sleeping. The monitor locks the state_lock, sets sim_active to 0, locks the arbitrator, and uses pthread_cond_broadcast to instantly wake all sleeping coders. The coders wake up, notice the simulation is inactive, immediately drop their requests, and safely exit to be joined by the main thread.
Classic References:
CodeVault: POSIX Threads (pthreads) Playlist - An incredibly comprehensive video series breaking down mutexes, condition variables, and practical C multithreading concepts.
AI Usage: During the development of this project, I used an AI assistant (Google's Gemini) as a virtual tutor and guide. The AI assisted by explaining complex thread synchronization concepts (like the mathematical mapping of a Min-Heap onto a flat array), and structuring a phased approach to the architecture. It was highly instrumental in analyzing -fsanitize=thread and Helgrind outputs, helping to identify and resolve unprotected data races via a dedicated state_lock, and fixing AB-BA lock-order inversions through Hierarchical Locking. It also provided guidance on adhering to the strict 42 Norm requirements by suggesting clean functional abstractions.
| Back | FazBrowse Home | New Git URL |