Multi-process chairlift/ropeway simulation using C11 and System V IPC (msg/sem/shm) on Linux.
cd ropeway-simulation
mkdir -p build && cd build
cmake ..
cmake --build .
# Run with default config (../config/default.conf)
./ropeway_simulation
# Run with specific config
./ropeway_simulation test1_capacity.conf
# Save logs to file (no terminal output)
./ropeway_simulation > simulation.log 2>&1Config files are located in ../config/ relative to the binary.
# Run all tests
cd tests
./run_all.sh
# Run by category
./run_all.sh integration # Tests 1-4: Basic functionality
./run_all.sh stress # Tests 5-8: High load scenarios
./run_all.sh edge # Tests 9-13: Edge cases
./run_all.sh recovery # Tests 14-16: Crash recovery
./run_all.sh signal # Tests 17-18, 20: Signal handling
./run_all.sh sync # Test 19: Synchronization
# Run individual test
cd build
bash ../tests/test1_capacity.shThe simulation demonstrates classical Unix IPC patterns through a real-world scenario:
Process Model — Each logical component runs as a separate process created via the fork()+exec() pattern. Seven distinct process types cooperate: the orchestrating Main process, a dedicated TimeServer for clock management, Cashier for ticket sales, two platform Workers (lower/upper), a TouristGenerator, and individual Tourist processes.
Inter-Process Communication — All coordination happens through System V primitives:
- Five message queues handle request-response flows between tourists, cashier, and workers
- A shared memory segment stores global simulation state accessible to all processes
- Ten semaphores control concurrent access to station capacity, chairlift slots, entry/exit gates, and emergency coordination
Signal-Driven Events — The system responds to various signals for different purposes:
SIGUSR1/SIGUSR2coordinate emergency stop and resume between workersSIGTSTP/SIGCONT(Ctrl+Z / fg) pause and resume the entire simulation with proper time offset trackingSIGALRMtriggers periodic checks and timeoutsSIGCHLDis handled by a dedicated zombie reaper thread viasigwait()to reap terminated children
Defensive Design — Every system call checks for errors and handles EINTR interrupts. IPC resources are cleaned up on both graceful shutdown and crash recovery. All user-provided configuration values are validated before use.
| Aspect | Requirement |
|---|---|
| Language | C11 standard, compiled with CMake 3.18+ |
| Dependencies | Standard library, pthreads, System V IPC only |
| IPC | Exclusively System V (no POSIX semaphores/queues) |
| Process creation | fork() + exec() for workers, no process pools |
| Threading | Limited to kid simulation within Tourist and zombie reaper in Main |
| Permissions | All IPC objects use 0600 mode |
| Cleanup | Resources removed via IPC_RMID on shutdown; ipcs empty after exit |
The chairlift operates through a chain of coordinated steps:
-
Initialization — Main generates IPC keys with
ftok(), creates all resources (ipc_create), then spawns worker processes (process spawning) -
Time Management — TimeServer atomically updates simulated clock every 10ms (update_sim_time), compensating for any shell-level pauses (pause handling)
-
Tourist Generation — Generator creates tourist processes with randomized attributes (age, type, VIP status) via
fork()+execl()(tourist_generator_main) -
Ticket Purchase — Each tourist sends a request to the Cashier queue and waits for a response with pricing and validity (cashier_main)
-
Station Entry — Tourist acquires entry gate and station capacity semaphores, then joins the platform queue (tourist lifecycle)
-
Boarding — LowerWorker collects tourists into chairs (max 4 slots), acquires a chair semaphore, and dispatches (dispatch_chair, lower_worker_main)
-
Arrival — UpperWorker tracks arrivals per chair and releases the chair semaphore when all passengers have disembarked (upper_worker_main)
-
Trail Descent — Tourist simulates walking or biking down, then either re-enters for another ride or exits when ticket expires
-
Emergency Protocol — When danger is detected, workers coordinate via SIGUSR1/SIGUSR2 signals plus message queue handshake to safely halt and resume operations (worker_trigger_emergency_stop, worker_initiate_resume)
| Process | File | Purpose |
|---|---|---|
| Main | src/main.c | Orchestrator: IPC creation, worker spawning, signal handling, zombie reaping |
| TimeServer | src/processes/time_server.c | Atomic time updates, SIGTSTP/SIGCONT pause offset |
| Cashier | src/processes/cashier.c | Ticket sales with age discounts and VIP surcharges |
| LowerWorker | src/processes/lower_worker.c | Lower platform boarding management |
| UpperWorker | src/processes/upper_worker.c | Upper platform arrivals and chair release |
| TouristGenerator | src/processes/tourist_generator.c | Spawns tourist processes via fork+exec |
| Tourist | src/tourist/main.c | Individual tourist lifecycle (buy ticket, ride, descend) |
Shared Memory (include/ipc/shared_state.h)
- SharedState structure with flexible array member for per-tourist tracking
- Atomic
current_sim_time_msupdated by TimeServer (line 47) - Global flags:
running,closing,emergency_stop(lines 50-53) - Statistics:
total_tourists,total_rides,rides_by_ticket[](lines 56-59) - Process PIDs for signal handling (lines 91-96)
Semaphores (include/constants.h#L28-L38)
| Index | Name | Purpose | Initial Value |
|---|---|---|---|
| 0 | SEM_STATE | Mutex for SharedState | 1 |
| 1 | SEM_STATS | Mutex for statistics | 1 |
| 2 | SEM_ENTRY_GATES | Entry gate slots | 4 |
| 3 | SEM_EXIT_GATES | Exit gate slots | 2 |
| 4 | SEM_LOWER_STATION | Station capacity | config |
| 5 | SEM_CHAIRS | Chair availability | 36 |
| 6 | SEM_WORKER_READY | Startup barrier | 0 |
| 7 | SEM_PLATFORM_GATES | Platform gates | 3 |
| 8 | SEM_EMERGENCY_CLEAR | Emergency release | 0 |
| 9 | SEM_EMERGENCY_LOCK | Emergency mutex | 1 |
Semaphore operations: src/ipc/sem.c
- sem_wait - Atomic wait (decrement) by count
- sem_wait_pauseable - Wait with EINTR retry loop
- sem_post - Atomic post (increment) by count
- sem_trywait - Non-blocking acquire
- sem_getval - Read current value
Message Queues (include/ipc/messages.h)
| Queue | ID | Direction | Message Type |
|---|---|---|---|
| MQ_CASHIER | 1 | Tourist <-> Cashier | CashierMsg |
| MQ_PLATFORM | 2 | Tourist → LowerWorker | PlatformMsg |
| MQ_BOARDING | 3 | LowerWorker → Tourist | PlatformMsg |
| MQ_ARRIVALS | 4 | Tourist → UpperWorker | ArrivalMsg |
| MQ_WORKER | 5 | Worker <-> Worker | WorkerMsg |
VIP Priority: Regular tourists use mtype=2, VIPs use mtype=1; msgrcv with -2 retrieves lowest mtype first (VIPs first).
- Main: src/lifecycle/process_signals.c - SIGTERM/SIGINT, SIGALRM
- Zombie Reaper: src/lifecycle/zombie_reaper.c - SIGCHLD via
sigwait()in dedicated thread - TimeServer: src/processes/time_server.c#L49-L97 - SIGTSTP, SIGCONT, SIGTERM, SIGALRM
- Workers: include/common/signal_common.h - Macro-generated handlers for SIGUSR1/SIGUSR2/SIGALRM
| Signal | Purpose |
|---|---|
| SIGCHLD | Zombie reaping via dedicated thread (sigwait()) |
| SIGTERM/SIGINT | Graceful shutdown |
| SIGALRM | Periodic time checks, emergency timeouts |
| SIGUSR1 | Emergency stop notification |
| SIGUSR2 | Resume notification |
| SIGTSTP | Pause simulation (shell Ctrl+Z) |
| SIGCONT | Resume simulation (shell fg) |
Main Process (src/main.c)
Signal workers to stop and destroy IPC to unblock blocked operations.
Entry point: load config, block SIGCHLD, create IPC, start zombie reaper thread, spawn workers, run main loop.
IPC Management (src/ipc/ipc.c)
Clean up stale IPC resources from a previous crashed run. Checks if shared memory exists and if the stored main_pid is dead.
- Parameters:
keys- IPC keys to check - Returns: 1 if stale resources cleaned, 0 if no stale resources, -1 on error
Create all IPC resources (shared memory, semaphores, message queues).
- Parameters:
res- IPC resources struct to populate,keys- IPC keys,cfg- configuration - Returns: 0 on success, -1 on error
Attach child process to existing IPC resources.
- Parameters:
res- IPC resources struct to populate,keys- IPC keys - Returns: 0 on success, -1 on error
Destroy all IPC resources (message queues, semaphores, shared memory).
- Parameters:
res- IPC resources to destroy
Signal-safe IPC cleanup for use in signal handlers. Only uses async-signal-safe syscalls (msgctl, semctl, shmctl). Cleanup order: MQ → Semaphores → SHM (unblocks waiting processes first).
- Parameters:
res- IPC resources to clean up
Semaphore Operations (src/ipc/sem.c)
Create and initialize semaphore set with configured values.
- Parameters:
res- IPC resources,key- semaphore key,cfg- configuration - Returns: 0 on success, -1 on error
Atomically wait (decrement) a semaphore by count. Blocks until count slots are available, then acquires all at once.
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index,count- number of slots to acquire - Returns: 0 on success, -1 on error/interrupt
Semaphore wait with EINTR handling. Kernel handles SIGTSTP/SIGCONT automatically (process gets suspended). Only returns -1 on shutdown (EIDRM) or other errors.
- Parameters:
res- IPC resources,sem_num- semaphore index,count- number of slots - Returns: 0 on success, -1 on shutdown/error
Atomically post (increment) a semaphore by count. Releases count slots at once.
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index,count- number of slots to release - Returns: 0 on success, -1 on error
Time Server (src/processes/time_server.c)
SIGTSTP handler - capture pause start time and suspend. Uses SA_RESETHAND so handler is automatically reset to SIG_DFL before this handler runs. After capture, raises SIGTSTP again to actually suspend the process.
- Parameters:
sig- signal number (unused)
SIGCONT handler - signal that pause has ended. Reinstalls the SIGTSTP handler (sigaction is async-signal-safe). Actual pause offset calculation happens in handle_resume().
- Parameters:
sig- signal number (unused)
Handle pause offset calculation after SIGCONT. Called outside signal handler context to safely calculate the pause duration and update the total pause offset.
Update the atomic simulated time in SharedState. Calculates current simulated time accounting for pause offsets and stores it atomically for other processes to read.
- Parameters:
state- shared state to update
Time Server process entry point. Maintains the current simulated time with sub-millisecond precision. Handles SIGTSTP/SIGCONT pause tracking and offset calculation. Atomically updates SharedState.current_sim_time_ms for other processes.
- Parameters:
res- IPC resources (shared memory for time updates),keys- IPC keys (unused)
Cashier (src/processes/cashier.c)
Calculate ticket expiration time in simulated minutes.
- Parameters:
state- shared state with ticket duration settings,ticket- type of ticket being purchased - Returns: Expiration time in simulated minutes from midnight
Calculate ticket price for one person. Age discounts: under 10 years or 65+ get 25% off.
- Parameters:
age- tourist age in years,ticket- type of ticket being purchased,is_vip- whether tourist has VIP status (adds surcharge) - Returns: Price in PLN
Calculate total family ticket price. Parent and kids all get the same ticket type. Kids (4-7 years old) always get the under-10 discount.
- Parameters:
parent_age- parent's age in years,ticket- type of ticket for the family,is_vip- whether family has VIP status,kid_count- number of children (0-2) - Returns: Total price in PLN for entire family
Cashier process entry point. Handles ticket sales via message queue. Calculates prices with age discounts and VIP surcharges. Runs until station closes or shutdown signal received.
- Parameters:
res- IPC resources (message queues, semaphores, shared memory),keys- IPC keys (unused)
Lower Worker (src/processes/lower_worker.c)
Check for random danger and trigger emergency stop if detected. Uses pause-adjusted time for cooldown calculation.
- Parameters:
res- IPC resources for emergency coordination - Returns: 1 if danger was detected, 0 otherwise
Dispatch the current chair with all buffered tourists. Acquires a chair slot, then sends boarding confirmations to all buffered tourists with the same departure_time so they arrive together. The upper_worker releases the chair slot when all tourists have arrived.
- Parameters:
res- IPC resources for semaphores and message queues,chair_number- chair identifier for logging,slots_used- total slots used on this chair
Lower platform worker process entry point. Manages tourist boarding onto chairlift. Buffers tourists until chair is full or queue is empty, then dispatches. Handles emergency stops and random danger detection.
- Parameters:
res- IPC resources (message queues, semaphores, shared memory),keys- IPC keys (unused)
Upper Worker (src/processes/upper_worker.c)
Find or create a tracker for a chair.
- Parameters:
chair_id- chair identifier to find or create tracker for,tourists_on_chair- expected number of tourists on this chair - Returns: Pointer to tracker, or NULL if tracking array is full
Upper platform worker process entry point. Processes tourist arrivals at the upper platform. Tracks chairs and releases chair slots when all tourists from a chair have arrived. Handles emergency stops and random danger detection.
- Parameters:
res- IPC resources (message queues, semaphores, shared memory),keys- IPC keys (unused)
Tourist Generator (src/processes/tourist_generator.c)
Generate random age (8-80) and check if person can have kids. Adults 26+ can be guardians for kids aged 4-7.
- Parameters:
can_have_kids- output: 1 if person can be a guardian, 0 otherwise - Returns: Generated age in years
Generate number of kids for a family (1-2). Distribution: ~63% one kid, ~37% two kids. Only called when family_percentage check already passed.
- Returns: Number of children (1 or 2)
Select ticket type for a tourist. Distribution: 30% single, 20% T1, 20% T2, 15% T3, 15% daily.
- Returns: Random ticket type
Tourist generator process entry point. Spawns tourist processes with random attributes (age, type, VIP status, ticket type, kids). Uses fork+exec to create tourist processes. Waits for all spawned tourists to exit before returning.
- Parameters:
res- IPC resources (shared memory for config values),keys- IPC keys (unused),tourist_exe- path to tourist executable
Tourist (src/tourist/main.c)
Tourist process entry point. Handles complete tourist lifecycle: ticket purchase, ride loop (enter station, board chair, ride, descend trail), and exit when ticket expires.
Emergency Coordination (src/common/worker_emergency.c)
Initiate emergency stop. Attempts to acquire SEM_EMERGENCY_LOCK to become the initiator. If lock acquired, sets emergency_stop flag and signals the other worker via SIGUSR1. If lock not acquired, falls back to receiver role.
- Parameters:
res- IPC resources,role- worker role (WORKER_LOWER or WORKER_UPPER),state- emergency state tracking
Acknowledge emergency stop from the other worker. Sets emergency_stop flag, then blocks waiting for resume message via MQ_WORKER. After resume received, signals ready and clears emergency flag.
- Parameters:
res- IPC resources,role- worker role,state- emergency state tracking
Resume operations after emergency cooldown passed. Sends READY_TO_RESUME message to other worker, waits for I_AM_READY response, sends SIGUSR2, clears emergency flag, and releases SEM_EMERGENCY_LOCK.
- Parameters:
res- IPC resources,role- worker role,state- emergency state tracking
Logger (src/core/logger.c)
Initialize logger with shared state and component type for colored output.
- Parameters:
state- shared state (for simulation time),comp- component type enum
Formatted log output with simulation timestamp, level, and component tag. Uses colors based on component type when output is a terminal.
- Parameters:
level- log level string,component- component name,fmt- printf-style format string
Async-signal-safe logging using only write(). For use in signal handlers.
- Parameters:
msg- message string to write
Signal-safe integer to string conversion. Does not use any non-async-signal-safe functions.
- Parameters:
n- integer to convert,buf- output buffer,buf_size- buffer size
Configuration (src/core/config.c)
Initialize configuration with default values.
- Parameters:
cfg- configuration structure to initialize
Load configuration from a file. Sets defaults first, then overrides with file values.
- Parameters:
path- path to the configuration file,cfg- configuration structure to populate - Returns: 0 on success, -1 on error
Validate configuration values. Prints error messages for invalid values to stderr.
- Parameters:
cfg- configuration structure to validate - Returns: 0 if valid, -1 if invalid
Report (src/core/report.c)
Write final simulation summary to file including duration, total tourists, total rides, per-tourist breakdown, and aggregates by ticket type. Report is saved to simulation_report.txt.
- Parameters:
state- shared state with simulation statistics,filepath- output file path - Returns: 0 on success, -1 on error
Time Simulation (src/core/time_sim.c)
Initialize time acceleration in shared state. Called once at startup by main process.
- Parameters:
state- shared memory state,cfg- configuration with time settings
Get current simulated time in minutes from midnight.
- Parameters:
state- shared memory state - Returns: Simulated minutes from midnight (e.g., 480 = 08:00)
Get current simulated time in minutes from midnight with fractional part.
- Parameters:
state- shared memory state - Returns: Simulated minutes from midnight as double
Check if simulation time has ended (past sim_end_minutes).
- Parameters:
state- shared memory state - Returns: 1 if simulation is over, 0 otherwise
Check if station is closing (approaching end time).
- Parameters:
state- shared memory state - Returns: 1 if closing, 0 otherwise
Convert simulated minutes to real seconds.
- Parameters:
state- shared memory state,sim_minutes- duration in simulated minutes - Returns: Duration in real seconds
Sleep for simulated minutes (handles pause via EINTR).
- Parameters:
state- shared memory state,sem_id- semaphore ID,sim_minutes- duration to sleep - Returns: 0 on success, -1 if simulation should stop
Format current simulated time as HH:MM string.
- Parameters:
state- shared memory state,buf- output buffer,buf_size- buffer size
Format minutes from midnight as HH:MM string.
- Parameters:
minutes- minutes from midnight,buf- output buffer,buf_size- buffer size
Tourist Lifecycle (src/tourist/lifecycle.c)
Buy ticket at cashier via message queue. For families, parent buys tickets for all kids.
- Parameters:
res- IPC resources,data- tourist data (updated with ticket info) - Returns: 0 on success, -1 if rejected or on error
Check if tourist's ticket is still valid.
- Parameters:
res- IPC resources,data- tourist data with ticket info - Returns: 1 if valid, 0 if expired
Check if station is closing.
- Parameters:
res- IPC resources - Returns: 1 if closing, 0 if open
Check if tourist is too scared to ride (~10% chance on first two rides if enabled).
- Parameters:
res- IPC resources,data- tourist data - Returns: 1 if scared, 0 otherwise
Get reason string for scared tourist.
- Parameters:
data- tourist data - Returns: Reason string for logging
Tourist Movement (src/tourist/movement.c)
Pause-aware sleep for simulated durations. Handles EINTR from signals.
- Parameters:
res- IPC resources,real_seconds- duration in real seconds,running_flag- pointer to running flag - Returns: 0 on success, -1 if interrupted by shutdown
Simulate riding the chairlift to the upper station.
- Parameters:
res- IPC resources,data- tourist data,departure_time- chair departure time,running_flag- pointer to running flag - Returns: 0 on success, -1 if interrupted by shutdown
Simulate walking or biking down the trail.
- Parameters:
res- IPC resources,data- tourist data,running_flag- pointer to running flag - Returns: 0 on success, -1 if interrupted by shutdown
Tourist Initialization (src/tourist/init.c)
Parse command line arguments and populate tourist data.
- Parameters:
argc- argument count,argv- argument vector,data- tourist data structure - Returns: 0 on success, -1 on error
Get logging tag based on tourist type and VIP status.
- Parameters:
data- tourist data - Returns: Logging tag string (e.g., "VIP CYCLIST", "FAMILY", "TOURIST")
Install signal handlers for tourist process.
- Parameters:
running_flag- pointer to running flag to clear on SIGTERM/SIGINT
Process Manager (src/lifecycle/process_manager.c)
Spawn a worker process via fork.
- Parameters:
worker_func- worker entry point,res- IPC resources,keys- IPC keys,name- worker name for logging - Returns: Child PID on success, -1 on error
Spawn the tourist generator process via fork.
- Parameters:
res- IPC resources,keys- IPC keys,tourist_exe- path to tourist executable - Returns: Child PID on success, -1 on error
IPC Functions (src/ipc/)
Generate IPC keys using ftok.
- Parameters:
keys- structure to populate,path- file path for key generation - Returns: 0 on success, -1 on error
Clean up stale IPC resources from a previous crashed run.
- Parameters:
keys- IPC keys to check - Returns: 1 if cleaned, 0 if no stale resources, -1 on error
Detach from IPC resources (for child processes before exit).
- Parameters:
res- IPC resources to detach from
Signal-safe IPC cleanup for use in signal handlers. Only uses async-signal-safe syscalls.
- Parameters:
res- IPC resources to clean up
Atomically wait (decrement) a semaphore by count.
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index,count- slots to acquire - Returns: 0 on success, -1 on error or signal interruption
Atomically post (increment) a semaphore by count.
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index,count- slots to release - Returns: 0 on success, -1 on error
Non-blocking semaphore wait (try to decrement by 1).
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index - Returns: 0 on success, -1 if would block or on error
Semaphore wait with EINTR handling and pause support. Retries on EINTR.
- Parameters:
res- IPC resources,sem_num- semaphore index,count- slots to acquire - Returns: 0 on success, -1 on shutdown or error
Get current semaphore value.
- Parameters:
sem_id- semaphore set ID,sem_num- semaphore index - Returns: Current value, or -1 on error
Wait for emergency stop to clear. Tracks emergency_waiters for reliable wakeup.
- Parameters:
res- IPC resources
Release all processes waiting for emergency to clear.
- Parameters:
res- IPC resources
Signal that a worker has completed initialization.
- Parameters:
res- IPC resources - Returns: 0 on success, -1 on error
Wait for all workers to signal ready.
- Parameters:
res- IPC resources,expected_count- number of workers to wait for - Returns: 0 on success, -1 on error/timeout
Signal Handling (src/lifecycle/process_signals.c)
Initialize signal handling module. Must be called before install_signal_handlers().
- Parameters:
res- pointer to IPC resources for signal-safe cleanup
Install all signal handlers for main process. Sets up SIGCHLD, SIGINT, SIGTERM, SIGALRM handlers.
Zombie Reaper (src/lifecycle/zombie_reaper.c)
Reap zombie child processes (non-blocking). Called from main loop when SIGCHLD is received.
Wait for all worker processes to exit (blocking). Called during shutdown.
Tourist Stats (src/tourist/stats.c)
Record tourist entry in shared state for final report.
- Parameters:
res- IPC resources,data- tourist data
Update ride statistics after completing a ride. Counts parent and all kids.
- Parameters:
res- IPC resources,data- tourist data
Tourist Boarding (src/tourist/boarding.c)
Board the chairlift by messaging lower worker.
- Parameters:
res- IPC resources,data- tourist data,departure_time_out- receives departure time,chair_id_out- receives chair ID,tourists_on_chair_out- receives tourist count - Returns: 0 on success, -1 on error or shutdown
Arrive at upper station and notify upper worker.
- Parameters:
res- IPC resources,data- tourist data,chair_id- chair ID,tourists_on_chair- tourist count - Returns: 0 on success, -1 on error
Tourist Threads (src/tourist/threads.c)
Thread function for kid simulation. Kids log and exit immediately.
- Parameters:
arg- pointer to KidThreadData - Returns: NULL
Thread function for bike simulation. Bikes log and exit immediately.
- Parameters:
arg- pointer to BikeThreadData - Returns: NULL
Create threads for kids and bike (if cyclist).
- Parameters:
data- tourist data,family- family state,kid_data- kid thread data array,kid_threads- thread handles array,bike_data- bike thread data,bike_thread- bike thread handle,bike_thread_created- output flag - Returns: 0 on success, -1 on error
Join all family threads (kids and bike).
- Parameters:
data- tourist data,kid_threads- thread handles array,bike_thread- bike thread handle,bike_thread_created- whether bike thread was created
Config file format: KEY=VALUE with # comments.
| Parameter | Default | Purpose |
|---|---|---|
STATION_CAPACITY |
50 | Max tourists in lower station |
SIMULATION_DURATION_REAL_SECONDS |
120 | Real time duration |
SIM_START_HOUR/SIM_START_MINUTE |
8:00 | Simulated start time |
SIM_END_HOUR/SIM_END_MINUTE |
17:00 | Simulated end time |
CHAIR_TRAVEL_TIME_SIM_MINUTES |
1 | Chair ride duration (sim minutes) |
TOTAL_TOURISTS |
100 | Tourists to generate (must be > 0) |
TOURIST_SPAWN_DELAY_US |
10000 | Spawn delay (microseconds) |
VIP_PERCENTAGE |
1 | VIP tourist percentage |
WALKER_PERCENTAGE |
50 | Walker vs cyclist ratio |
TRAIL_WALK_TIME_SIM_MINUTES |
2 | Walking trail duration (sim minutes) |
TRAIL_BIKE_*_TIME_SIM_MINUTES |
1/2/3 | Bike trail durations (fast/med/slow) |
TICKET_T*_DURATION_SIM_MINUTES |
60/120/180 | Time ticket durations |
DANGER_PROBABILITY |
0 | Emergency detection (0-100) |
DANGER_DURATION_SIM_MINUTES |
30 | Emergency duration |
DEBUG_LOGS_ENABLED |
1 | Show debug logs |
Constants (include/constants.h)
| Constant | Value | Purpose |
|---|---|---|
CHAIR_CAPACITY |
4 | Max slots per chair |
MAX_CHAIRS_IN_TRANSIT |
36 | Concurrent chairs |
TOTAL_CHAIRS |
72 | Total chairs in system |
ENTRY_GATES |
4 | Entry gate count |
EXIT_GATES |
2 | Exit gate count |
PLATFORM_GATES |
3 | Platform gate count |
MAX_KIDS_PER_ADULT |
2 | Max children per guardian |
Enums (include/constants.h#L70-L114)
TouristType: TOURIST_WALKER (0), TOURIST_CYCLIST (1), TOURIST_FAMILY (2)
TicketType: TICKET_SINGLE (0), TICKET_TIME_T1 (1), TICKET_TIME_T2 (2), TICKET_TIME_T3 (3), TICKET_DAILY (4)
TouristStage: 10 lifecycle stages from STAGE_AT_CASHIER (0) to STAGE_LEAVING (9)
Logger Colors (src/core/logger.c#L17-L28)
| Component | Color |
|---|---|
| TOURIST | Bright green |
| VIP | Bright red |
| CASHIER | Yellow |
| LOWER_WORKER | Blue |
| UPPER_WORKER | Cyan |
| GENERATOR | Magenta |
| MAIN | White |
| IPC | Gray |
| TIME_SERVER | Bright yellow |
test1_capacity.sh - Lower Station Capacity Limit
- Goal: Station visitor count never exceeds N
- Rationale: Tests for race condition between 4 entry gates incrementing station count via
SEM_LOWER_STATION. Without proper semaphore synchronization, concurrentsem_wait()calls could allow count > N briefly. - Parameters:
station_capacity=5,tourists=15,simulation_time=1s - Expected: Max observed count <= 5. No zombies. IPC cleaned.
test2_children.sh - Children Under 8 with Guardians
- Goal: Children board with guardians, max 2 kids per adult
- Rationale: Tests atomic multi-slot semaphore acquisition for families.
sem_wait_n(SEM_LOWER_STATION, family_size)must be atomic - partial acquisition would split family or cause deadlock if slots < family_size. - Parameters:
walker_percentage=80,family_percentage=80,tourists=15,simulation_time=1s - Expected: Families board together. No adult has >2 kids. No deadlock.
test3_vip.sh - VIP Priority Without Queue Starvation
- Goal: VIPs skip entry gates while regular tourists wait in queue
- Rationale: VIPs bypass
SEM_ENTRY_GATESentirely, reaching the platform faster when gates are congested. Verifies VIPs log "skipped gate queue" while regulars log "entered through gate", and both groups are served. - Parameters:
vip_percentage=30,tourists=30,simulation_time=1s - Expected: VIPs skip gates, regulars enter gates, both groups served.
test4_emergency.sh - Emergency Stop and Resume (Signals)
- Goal: SIGUSR1 stops chairlift, SIGUSR2 resumes after worker confirmation
- Rationale: Tests signal handler coordination via
SEM_EMERGENCY_LOCKandSEM_EMERGENCY_CLEAR. Race condition possible if both workers try to initiate emergency simultaneously. Resume requires both workers to signal ready. - Parameters: SIGUSR1/SIGUSR2 injection with 0.5s delays,
simulation_time=3s - Expected: Emergency stop logged. Resume only after both workers ready.
test5_stress.sh - High Throughput Stress Test
- Goal: Simulation completes without timeout under high concurrency
- Rationale: Tests for deadlock between
MQ_CASHIER,MQ_PLATFORM,MQ_BOARDINGunder load. Concurrent tourists stress all semaphores and message queues simultaneously - exposes circular wait conditions or semaphore exhaustion. - Parameters:
tourists=6000,station_capacity=500,simulation_time=180s, rapid spawn (no delay) - Expected: No timeout (deadlock). No zombies. IPC cleaned.
test6_race.sh - Entry Gate Race Condition Test
- Goal: Capacity N=5 never exceeded across 10 iterations
- Rationale: Tests TOCTOU race in
SEM_LOWER_STATION. Multiple tourists callingsem_wait()simultaneously could interleave check-and-decrement operations. 10 iterations increases probability of catching intermittent race. - Parameters:
tourists=10,N=5,simulation_time=3s, 10 iterations - Expected: Count <= 5 in all iterations. 100% pass rate.
test7_emergency_race.sh - Emergency Race Condition Test
- Goal: Only one worker initiates emergency when both detect danger
- Rationale: Tests race on
SEM_EMERGENCY_LOCKwhen lower_worker and upper_worker both callsem_trywait()simultaneously. Double-initiation would corrupt emergency state or cause deadlock waiting onSEM_EMERGENCY_CLEAR. - Parameters:
danger_probability=100,tourists=10,simulation_time=3s - Expected: Single initiator per emergency. No deadlock. System recovers.
test8_chair_saturation.sh - Chair Saturation Test
- Goal: Tourists block on
SEM_CHAIRSwhen all 36 chairs are in transit - Rationale: Tests semaphore blocking behavior when
SEM_CHAIRSreaches zero. Fast boarding + slow travel saturates chairs. Verifiessem_wait()blocks correctly andsem_post()from arrivals unblocks waiting tourists. - Parameters:
tourists=20,station_capacity=50,simulation_time=3s - Expected: Max 36 chairs in transit. No deadlock on chair exhaustion.
test9_zero.sh - Zero Tourists Edge Case
- Goal: Graceful rejection of invalid configuration (0 tourists)
- Rationale: Tests config validation.
TOTAL_TOURISTS=0is rejected at startup with clear error message. Verifies no IPC resources are leaked when startup fails early. - Parameters:
tourists=0,simulation_time=1s - Expected: Config rejected gracefully. No IPC leaks from failed startup.
test10_single.sh - Single Tourist Edge Case
- Goal: One tourist completes full lifecycle without concurrency
- Rationale: Baseline test - verifies message passing chain works: tourist→MQ_CASHIER→cashier→MQ_PLATFORM→lower_worker→MQ_BOARDING→tourist→MQ_ARRIVALS→upper_worker. No concurrency masks IPC bugs.
- Parameters:
tourists=1, walker,simulation_time=1s - Expected: Tourist completes ride. All IPC handoffs succeed.
test11_capacity_one.sh - Capacity One Edge Case
- Goal: Only 1 tourist in station at any time with N=1
- Rationale: Tests convoy effect on
SEM_LOWER_STATION. With N=1, all tourists serialize on single semaphore slot. Potential for priority inversion or starvation ifsem_wait()ordering is unfair. - Parameters:
N=1,tourists=10,simulation_time=1s - Expected: Max count=1. All tourists eventually served. No deadlock.
test12_all_vip.sh - All VIPs Edge Case
- Goal: All tourists served when everyone has same mtype=1 priority
- Rationale: Tests
msgrcv()behavior when all messages have identical mtype. Verifies FIFO ordering within same priority level. Edge case where priority differentiation provides no benefit - system must still function. - Parameters:
vip_percentage=100,tourists=10,simulation_time=1s - Expected: FIFO order maintained. All tourists served. No starvation.
test13_all_families.sh - All Families Edge Case
- Goal: Families board atomically without splitting
- Rationale: Tests
semop()withsem_op > 1for atomic multi-slot acquisition. Family of 3 must acquire 3 slots atomically - partial acquisition would split family or deadlock if remaining capacity < family_size. - Parameters:
walker_percentage=100,family_percentage=100,tourists=10,simulation_time=1s - Expected: No split families. Atomic acquisition verified. No deadlock.
test14_sigterm.sh - SIGTERM Cleanup Test
- Goal: No orphaned IPC resources after SIGTERM shutdown
- Rationale: Tests
ipc_cleanup()is called from SIGTERM handler. Semaphores, shared memory, and message queues must be released viasemctl(RMID),shmctl(RMID),msgctl(RMID). Leaked IPC persists until reboot. - Parameters: Uses test1_capacity.conf (1s, 15 tourists), SIGTERM mid-run
- Expected: ipcs empty after shutdown. No zombies. All children terminated.
test15_sigkill_recovery.sh - SIGKILL Recovery Test
- Goal: New run cleans orphaned IPC from previous crash
- Rationale: SIGKILL bypasses signal handlers - no
ipc_cleanup()runs. Testsipc_cleanup_stale()at startup: must detect orphaned resources viaftok()key collision and remove before creating new IPC objects. - Parameters: Uses test1_capacity.conf, SIGKILL first run, start second run
- Expected: Second run succeeds. Stale IPC cleaned. Log shows cleanup.
test16_child_death.sh - Child Death Test
- Goal: Main process shuts down gracefully when child dies unexpectedly
- Rationale: Tests SIGCHLD handler and zombie reaper thread. Killing cashier mid-run must trigger:
waitpid()reaps child, main detects worker death, initiates shutdown. Without proper handling: zombies or orphaned children. - Parameters: Uses test1_capacity.conf, kill cashier mid-simulation
- Expected: Main detects death. Graceful shutdown. No zombies or IPC leaks.
test17_pause_resume.sh - Pause/Resume Test (SIGTSTP/SIGCONT)
- Goal: Simulated time offset adjusts for pause duration
- Rationale: Tests SIGTSTP/SIGCONT handlers in time_server. Pause duration must be added to time_offset so accelerated simulation time doesn't jump. Without offset adjustment: tickets expire during pause, time discontinuity.
- Parameters: Uses test1_capacity.conf (1s simulation), SIGTSTP then SIGCONT
- Expected: Time continues smoothly after resume. No expired tickets.
test18_rapid_signals.sh - Rapid Signals Test
- Goal: No crash under rapid signal delivery to worker processes
- Rationale: Tests signal handler reentrancy in workers. Rapid SIGUSR1 can interrupt handler mid-execution. Main process does not handle SIGUSR1 (default action terminates), so signals are sent to child worker processes. Non-async-signal-safe functions in handler cause undefined behavior.
- Parameters: 10 SIGUSR1 signals with 50ms delay, sent to child process
- Expected: No segfault. No hang. Simulation survives signal storm.
test19_sigalrm_sync.sh - SIGALRM Sync Test
- Goal: Verify simulation works with SIGALRM-based sync (no usleep polling)
- Rationale: Confirms refactored code uses blocking IPC +
alarm()correctly. AllIPC_NOWAIT+usleep polling has been replaced with blocking calls that use SIGALRM for periodic wakeup. EINTR handled properly on alarm interrupt. - Parameters:
tourists=15,simulation_time=1s(uses test1_capacity.conf) - Expected: Simulation completes. No deadlock. No leftover IPC.
test20_sigint_emergency.sh - SIGINT During Emergency Stop
- Goal: Graceful shutdown when Ctrl+C is sent during emergency stop
- Rationale: Workers blocked on
msgrcv()during emergency handshake must wake up on SIGINT (EINTR) and proceed with shutdown. Emergency lock (SEM_EMERGENCY_LOCK) must be released. IPC cleanup must complete even when chairlift is stopped. - Parameters:
danger_probability=100,danger_duration=120min(long duration ensures SIGINT hits during emergency) - Expected: Clean shutdown within 15s. No zombies. No orphaned processes. No leftover IPC.
Tests check for:
- Capacity violations: Station count never exceeds configured limit
- Zombie processes: No defunct processes after shutdown
- IPC cleanup:
ipcsshows no leftover resources - Log analysis: Parses simulation logs for expected behavior
- C11 + CMake 3.18: No external dependencies
- System V IPC only: No POSIX IPC
- fork+exec: All workers spawned via fork+exec pattern
- ftok: All IPC keys generated via ftok
- Signal safety: Only async-signal-safe functions in handlers
- EINTR handling: All blocking operations handle interrupts
- IPC cleanup: Resources destroyed on shutdown via
IPC_RMID - Threads: Only for kids/bikes within Tourist process and zombie reaper