Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

This project has been created as part of the 42 curriculum by

Codexion

A multi-threaded simulation project built in C, inspired by the classic Dining Philosophers problem. In Codexion, instead of philosophers, forks, and eating, we have coders, dongles, and compiling. This project emphasizes process synchronization, threading, and resource management to prevent deadlocks and ensure smooth concurrent execution.

Features

  • Multi-threaded Simulation: Each coder runs in their own thread.
  • Resource Management: Coders must acquire two dongles (left and right) to safely compile their code.
  • Scheduler Policies: Includes a priority queue logic to handle dongle allocations based on scheduling rules (fifo or edf).
  • Monitor Thread: A dedicated monitor thread to watch over the coders and gracefully stop the simulation if any coder faces a burnout (starvation limit exceeded).
  • Graceful Cleanup: All resources, priority queues, and mutexes are carefully freed and destroyed at the end of the simulation.

Building the Project

Ensure you have a C compiler installed. Use the provided Makefile to compile the application:

make

This will produce the codexion executable.

To clean up object files:

make clean

To clean up all compiled files including the executable:

make fclean

Usage

The program expects 8 parameters to run correctly:

./codexion n_coders t_burn t_comp t_debug t_refac n_comp d_cool sched

Arguments:

  1. n_coders : Number of coders playing a part in the simulation.
  2. t_burn : Time to burnout (in milliseconds). If a coder doesn't start compiling within this time since the start of their last compilation, they will burn out (die).
  3. t_comp : Time to compile (in milliseconds). Determines how long the compiling process takes for a coder.
  4. t_debug : Time to debug (in milliseconds). Determines how long a coder will spend debugging after compiling.
  5. t_refac : Time to refactor (in milliseconds).
  6. n_comp : Number of compiles required before a coder finishes. (Optional limit, can be simulated indefinitely depending on logic).
  7. d_cool : Dongle cooldown time.
  8. sched : The scheduling policy to use (fifo or edf).

Example

./codexion 5 800 200 200 100 5 10 fifo

(This runs a simulation with 5 coders, an 800ms burnout timer, 200ms compile/debug times, 100ms refactor time, requiring 5 compiles to finish, 10ms dongle cooldown, and a FIFO scheduler.)

Architecture

  • main.c: Entry point handling arguments parsing, data initialization, thread creation, and joining.
  • code.h: Main header declaring all project constants, structures (t_data, t_coders, t_dongle, t_pq), and prototypes.
  • init.c: Sets up global state, memory allocations, mutex initializations.
  • routine.c: Holds the main core action loop for coders (coder_routine).
  • monitor.c: Houses the observer thread (monitor_routine) responsible for checking conditions such as coders hitting time_to_burnout or all coders fulfilling the compile quota.
  • scheduler.c: Implements the fifo and edf based Priority Queue processing limits for the threads.
  • utils.c: Helpful supplementary utilities such as clock routines, sleep handling, and basic string manipulation.

Blocking cases handled

This project addresses several critical concurrency challenges to ensure a stable and predictable simulation:

  • Deadlock Prevention (Breaking Coffman's Conditions):
    • Mutual Exclusion: Dongles are protected by pthread_mutex_t, ensuring only one coder can hold a specific dongle at a time.
    • Hold and Wait: To prevent coders from holding one dongle while waiting indefinitely for another, we use a centralized Priority Queue (Request Queue). A coder can only proceed to attempt taking their left and right dongles once they are at the front of the queue.
    • No Preemption: Dongles are never forcefully taken from a coder.
    • Circular Wait: The request queue enforces a strict ordering of execution. Since coders take dongles sequentially based on the queue's front-most request, circular wait (where Coder A waits for Coder B's dongle and vice versa) is mathematically impossible.
  • Starvation Prevention: The priority queue supports two scheduling policies:
    • FIFO (First-In-First-Out): Guarantees that every request is eventually served in the order it was received.
    • EDF (Earliest Deadline First): Prioritizes coders closest to their burnout limit, ensuring those most at risk are served first.
  • Cooldown Handling: When a coder releases dongles, a cooldown_end_time is set for each. Other coders checking these dongles will perform a precise ft_usleep if the cooldown hasn't expired, preventing "thundering herd" issues and ensuring the cooldown period is respected.
  • Precise Burnout Detection: A dedicated monitor thread continuously scans all coders' states. By comparing the current time with the last_compile_start timestamp using millisecond precision, it can detect burnout at the exact moment it occurs.
  • Log Serialization: All console output is synchronized using a global print_mutex. This prevents interleaved character streams and ensures that each status message is printed atomically.

Thread synchronization mechanisms

The simulation relies on robust threading primitives to coordinate complex interactions between multiple threads:

  • Threading Primitives:
    • pthread_mutex_t: Used for locking shared resources like individual dongles, the priority queue, logging, and global simulation state flags (simulation_stop).
    • pthread_cond_t: Acts as a signaling mechanism for the Priority Queue. Coders wait on this condition variable when they aren't at the front of the queue and are broadcasted to wake up whenever the queue state changes (e.g., when dongles are released).
  • Custom Event Implementation: The Priority Queue (PQ) serves as a custom event system. Instead of simple locks, the PQ coordinates "acquisition events." A coder "registers" for dongles by inserting a request and "listens" for their turn via condition variables.
  • Shared Resource Coordination:
    • Dongles: Individual mutexes ensure thread-safe access.
    • Logging: A global print_mutex serializes all printf calls.
    • Monitor State: Access to the simulation_stop flag is guarded by a stop_mutex, ensuring that when the monitor detects a burnout, all threads receive the signal immediately and safely.
  • Race Condition Prevention: Race conditions are prevented by strictly wrapping every access to shared variables (like compile_counter, last_compile_start, and the priority queue itself) within mutex locks. Thread-safe communication between coders and the monitor is achieved through these shared variables, where the monitor reads values updated by coder threads under mutex protection.

Resources

https://www.cs.unibo.it/~ghini/didattica/sistop/pthreads_tutorial/POSIX_Threads_Programming.htm

About

A multi-threaded simulation project built in C, inspired by the classic Dining Philosophers problem.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages