Skip to content

Error Handling

Error handling in this project is part of the runtime architecture, not a separate afterthought. Startup, shutdown, and runtime exits all depend on the same ownership rules inside t_app.

Failure paths

Startup can fail anywhere, cleanup must still be safe

This codebase relies on progressive initialization and centralized cleanup. Every failure path either returns immediately before resources exist or routes through `free_app(&app)` once ownership has started.

01
startup failure chain

The main function advances in order and aborts through one cleanup gate.

  1. 1
    init_app(&app) NULL base

    zero-init and NULL-init the whole app state so cleanup always has safe defaults

    return 1
  2. 2
    bonus_levels_init(&app) bonus

    prepare optional level context before parsing starts

    free_app(&app) + return 1
  3. 3
    parse_file(&app, argv[1]) parsing

    load config, validate map, and stop startup on parser/validator errors

    free_app(&app) + return 1
  4. 4
    init_player_vectors(&app)

    convert validated spawn orientation into direction and camera plane

    free_app(&app) + return 1
  5. 5
    init_mlx(&app) MLX alloc

    allocate display, window, textures, and frame buffer resources

    free_app(&app) + return 1
  6. 6
    mlx_loop(app.mlx_ptr) runtime

    enter runtime loop after startup has succeeded

    runtime exit paths call free_app(...)
02
runtime exit paths

Normal close and fatal resize/init failures converge on the same ownership model.

`ESC` / window cross

`close_window(app)` → `free_app(app)` → `exit(0)`

fatal resize/init error

`error_put(...)` → `free_app(app)` → `exit(1)`

03
`free_app(&app)` responsibilities

Cleanup is pointer-guarded, so partial init states do not need a separate flag table.

free config texture paths if ptr
destroy mandatory textures if img_ptr
run bonus shutdown helpers bonus
destroy frame image if img_ptr
destroy window if win_ptr
destroy display + free mlx_ptr if mlx_ptr
free map grid if grid
04
why NULL guards are enough

Click the diagram to replay a failure happening during startup.

click to replay a failure at init_mlx
idempotent cleanup model

Pointer validity acts as the cleanup gate. That prevents double free, supports partial initialization, and keeps the failure model defendable during evaluation.

no separate init flag table safe partial cleanup single central shutdown path normal and error exits aligned

The error model is designed to guarantee:

  • explicit error reporting through Error\n...
  • predictable cleanup on every failure path
  • no double-destroy on MLX resources
  • no leaks from partially initialized state
  • safe termination on both normal exit and fatal exit

Resources are initialized progressively, and cleanup relies on pointer validity instead of a separate init-state table.

That means:

  • init_app starts from a safe zeroed state
  • each later stage may allocate more resources
  • any failure after ownership begins routes through free_app(&app)
  • each cleanup action checks whether the owned pointer is valid before destroy/free

The main runtime exits are:

  • ESC
  • window close event
  • fatal resize/init path that reports an error before exiting

Normal close uses close_window(...), which then calls free_app(...) before exit(0). Error exits use the same ownership model but end with an error status.

free_app(...) is the central cleanup gate for:

  • config strings
  • map grid
  • frame image
  • mandatory textures
  • bonus subsystem shutdown
  • MLX window / display resources

Because it is pointer-guarded, it naturally supports partial initialization.

This model explicitly covers:

  • failure before MLX initialization
  • failure during MLX setup
  • parser failure after dynamic allocations
  • partial image or texture initialization
  • repeated cleanup entry points routed to the same function

This architecture is easy to defend because it is easy to explain:

  • failure paths are visible in control flow
  • ownership is centralized in t_app
  • cleanup logic is deterministic
  • no separate hidden state machine is needed to know what may be freed
  • srcs/tools/utils.c
  • srcs/core/main.c
  • srcs/core/shutdown.c
  • srcs_bonus/doors/
  • srcs_bonus/hud/
  • srcs_bonus/pickups/
  • srcs_bonus/sprites/