A concurrency simulation to master threading and resource synchronization in C
Codexion is a comprehensive concurrency simulation in C designed to master threading and resource synchronization. Based on the classic Dining Philosophers problem, the project models a circular co-working hub where Coders (threads) compete for limited USB Dongles (mutexes) to compile their work.
The core challenge is to implement efficient arbitration algorithms to manage resource contention without "burning out" (starving) any coder. The simulation ensures system stability through:
- Adaptive Scheduling: Choice between FIFO (fairness based on explicit queue slots) or EDF (Earliest Deadline First) to manage waiting lists.
- Deadlock Prevention: Implementation of an Odd/Even strategy prevents dependency cycles.
- Thread Safety: A dedicated Monitor thread that tracks state changes to detect termination conditions.
Run the program with the following arguments to start the simulation:
./codexion <coders> <t_burnout> <t_compile> <t_debug> <t_refactor> <n_compiles> <cooldown> <scheduler>| Argument | Description | Example |
|---|---|---|
| coders | Total number of coders (threads) and dongles. | 5 |
| t_burnout | Time (ms) a coder can survive without compiling. | 800 |
| t_compile | Time (ms) spent compiling (holding 2 dongles). | 200 |
| t_debug | Time (ms) spent debugging (no dongles). | 200 |
| t_refactor | Time (ms) spent refactoring (no dongles). | 200 |
| n_compiles | Simulation stops if all coders reach this count. | 7 |
| cooldown | Time (ms) a dongle remains unavailable after use. | 100 |
| scheduler | Arbitration policy: fifo or edf. |
fifo |
The simulation relies on POSIX threads (pthread) and utilizes a specific combination of mutexes and explicit queue management to coordinate the interaction between Coders, Dongles, and the Monitor.
The project moves away from standard condition variable broadcasts for resource waiting, opting instead for specific mutexes protecting explicit queue slots.
Global Control (s_data)
threads_lock&threads_cond: Act as a barrier synchronization mechanism to ensure all threads (n_codersandmonitor) start the simulation simultaneously.print_lock: Serializes console output to prevent interleaved logs from concurrent threads.flag_lock: Protects the globalmonitor_flag. This single boolean indicates the simulation end state, whether due to a "burnout" (starvation) or successful completion of all compiles.
Resource Mutexes (s_dongle)
take_lock: Represents the physical exclusive possession of a dongle by a coder.cooldown_lock: Protects thenext_available_timetimestamp, ensuring the mandatory cooldown period is thread-safe.queue_lock: Critical for the scheduling logic. It protects the explicit waiting slots (first_in_queueandsecond_in_queue) on each dongle, preventing race conditions when multiple coders attempt to queue up simultaneously.
Coder Mutexes (s_coder)
compile_lock: Protects the coder's vital statistics (last_compileandtimes_compiled). This allows the Monitor thread to read a coder's status without causing data races while the coder updates their own meal time.
The arbitration logic uses a polling-based approach combined with explicit queue slots on every dongle structure:
-
FIFO (First-In, First-Out)
- Queue Entry: Coders attempt to occupy one of the two available slots (
firstorsecond) protected byqueue_lock. - Wait Strategy: If both slots are full, the thread yields execution (
usleep) and retries later. - Acquisition: Once in the queue, the coder waits for the
first_in_queueposition and for the cooldown to expire before locking the dongle.
- Queue Entry: Coders attempt to occupy one of the two available slots (
-
EDF (Earliest Deadline First)
- Priority Check: Before granting the resource, the scheduler compares the
last_compiletimestamp of the coders currently in the queue slots. - Dynamic Selection: The function
closest_deadlinedetermines which queued coder has gone the longest without compiling. That specific thread is granted priority to lock the dongle, effectively preventing starvation for the most critical threads.
- Priority Check: Before granting the resource, the scheduler compares the
A dedicated Monitor Thread runs parallel to the simulation to ensure rules are respected:
- Routine: It iterates through all coders in a continuous loop.
- Burnout Detection: It locks
compile_lockto calculate the time elapsed since the last compile. If(current_time - last_compile) >= t_burnout, it locksflag_lockand raises the stop flag. 🚩 - Completion: It also tracks the total number of coders who compiled at least the minimum amount of times required. If the target
n_compilesis reached by everyone, the simulation terminates successfully, raising the same flag. 🚩
