More projects · 04 / 05Operating systems · C

Concurrent Supermarket Simulator in C

A supermarket becomes a working model of operating systems: customers shop and change queues, cashiers serve and report demand, and a separate director process decides when to open or close checkouts. From fork and exec to threads, sockets, condition variables and signals.

Supermarket, live. Click to restart the day.
shoppingin a queueat the till
2processes, talking over a local socket
6checkouts in the larger configuration
50customers in the store at once
3signals: normal close, emergency, orders
0external libraries
01 / Architecture

Two processes, one protocol.

The director owns the checkout policy; the supermarket process owns customers, cashiers, queues and their state. In the simplest launch the director starts the supermarket itself with fork and exec. They talk through a Unix-domain stream socket with integer-coded requests, responses and acknowledgments, converted to network byte order, and coordinate orders with signals.

Architecture: the director process and the supermarket process connected by a socket and signals, with customer threads, cashier threads and checkout queues inside the supermarket
The two processes, their socket and signals, and the threads inside the supermarket. Diagram from the repository.
02 / The simulation

Customers who change their mind.

  • Admission. At opening the store admits C customers; when occupancy falls to C − E, it refills back to C.
  • Customers are threads: each picks a shopping time and a basket size. With an empty basket they ask the director for permission to leave; otherwise they join an open checkout and, every S milliseconds, compare the people ahead of them with the other queues, moving to a shorter one.
  • Cashiers are threads too, each with a random base service time between 20 and 80 ms plus a fixed time per product. A reporting thread sends each checkout's customer count to the director at regular intervals.
  • Synchronization. Shared state is protected by mutexes, and threads wait on condition variables instead of spinning.
ParameterMeaningconfig1config2
Kmaximum open checkouts26
Ccustomers in the store at once2050
Edepartures before the store refills53
S1nearly empty checkouts that trigger a closing12
S2queue length that triggers an opening45

The demo at the top simulates the policy with config2, slowed down about eight times.

03 / The director

A policy under load.

On every report the director scans the checkouts in order and acts on the first condition it meets: a queue of S2 or more customers opens a closed checkout; S1 checkouts with zero or one customer close the current one, always keeping at least one open. Every order goes through a small handshake: a signal to warn the supermarket, a signal back, then the order and the checkout number over the socket.

server.c · decision()
static void decision() {    bool closing = false, opening = false;    int at_most_one_customer = 0, i = 0, checkout = -1;    // open or close a checkout, according to the thresholds in the configuration file    while (i < dir_args.max_open_checkouts && !closing && !opening) {        if (customers_per_checkout[i] >= dir_args.s_open_checkout) {            opening = true;        } else if (customers_per_checkout[i] == 0 || customers_per_checkout[i] == 1) {            at_most_one_customer++;            if (at_most_one_customer >= dir_args.s_close_checkout) {                checkout = i;                closing = true;            }        }        i++;    }    if (opening) {        // pick the first closed checkout (-1 marks a closed one)        i = 0;        while (i < dir_args.max_open_checkouts && checkout == -1) {            if (customers_per_checkout[i] == -1)                checkout = i;            else                i++;        }        if (checkout != -1) {            // SIGPROF warns the supermarket that an order is coming, then wait for its SIGPROF back            syscall(kill(mrkt_pid, SIGPROF), "kill")            while (!SIGPROF_arrived)                ec_nzero(pthread_cond_wait(&alrm_cond, &mtx))            SIGPROF_arrived--;            write_on_socket(supermarket_fd, ORDER_OPEN_CHECKOUT);        } else return;    }    /* ... the closing order follows the same handshake, only if more than one checkout is open */}
The director's decision from server.c, shortened, with identifiers and comments translated into English.
04 / Shutdown and logs

Closing time, done right.

The day ends through signals sent to the director. SIGHUP closes admission and lets the customers inside finish their shopping, as at the end of the demo's cycle. SIGQUIT requests an emergency closing through a separate coordination path. Internally, SIGPROF carries the director's checkout orders.

After closing, the supermarket writes a structured log, and a Bash script turns it into tables: total customers and products, then per customer (products, time inside, time at the checkout, queues joined) and per checkout (products, customers served, open time, average service time, closures).

Operating systems, made tangible.

The C sources, the Makefile with its test targets, the analysis script and the report that explains the architecture, protocol and synchronization are on GitHub.

Next project · 05 / 05Interpreter in OCaml