06C++ & Operating SystemsPublic repository · two contributors · nothing benchmarked

Financial Tick Data Pipeline

A four-process Linux pipeline in C++17 that turns tick CSV records into per-symbol summaries through FIFOs, shared memory and a worker pool — and cleans up every resource it creates.

Processes
Four, plus a worker pool
IPC primitives
FIFO · shared memory · semaphores
Source
Five files, ~32 KB of C++
Benchmarks
None — nothing measured
Context
Public academic project · two contributors
Contributors
Rafay Khattak & Muhammad Umar Nadeem
Period
May 2026
Shape
Four processes and one shared header, in roughly 32 KB of C++
Built with
A Makefile — C++17, warnings enabled, pthreads linked
Status
Complete · one commit, and nothing measured
Visibility
Public repository · every claim here is checkable in source

Covers

Systems Programming
Data
Databases
Product
People

Stack

  • C++17
  • POSIX IPC
  • pthreads
  • Make
  • Linux
01

The topology

The pipeline is four separate programs rather than four functions, and that is the whole point of it. A dispatcher creates the IPC objects and forks the other three. An ingester walks a directory of CSVs and pushes framed chunks into a FIFO. A processor reads them, spreads the parsing across a worker pool, and writes an aggregate into shared memory. A reporter waits on a semaphore, reads that memory once it is signalled, and writes the output.

Splitting the work across processes rather than threads is a deliberate constraint. Threads would share an address space and make most of this disappear; separate processes force every hand-off to cross a real operating-system boundary, which is the thing the project exists to demonstrate.

Each stage is a small program with one job, and the shared header is the only thing all four agree on.

  1. 01DispatcherProcess
  2. 02IngesterProcess
  3. 03FIFOIPC
  4. 04ProcessorProcess
  5. 05Bounded queue + workersThreads
  6. 06Shared memoryMemory
  7. 07ReporterProcess
  8. 08Per-symbol summaryOutput
  • Named semaphores

    Access control

  • Signals

    Shutdown

  • Cleanup

    Resource release

Systems primitives

  • fork / exec
  • FIFO
  • Shared memory
  • Named semaphores
  • pthreads
  • Signals
  • Resource cleanup
  • Bounded queue
02

One shared header

The four programs share exactly one header, and it reads as a design document. It defines how a chunk is framed, what kinds of chunk exist, how the shared-memory region is laid out, and what every exit code means.

Two magic numbers guard the boundaries — one on each chunk header, one on the shared-memory block — so a stage that receives something unexpected can say so rather than parsing garbage confidently. It is a cheap check that turns a silent corruption into a loud failure.

The exit codes are named constants rather than numeric literals, and the signal cases follow the shell convention. That matters because the dispatcher is what reads them: a child that has died has to explain why through the only channel it has left.

Everything in shared memory is fixed-capacity — a bounded table of per-symbol entries rather than a growing one — because a region has to have a size before anybody can map it.

Chunk framing

  • Magic number
  • Chunk type
  • Chunk id
  • Source file id
  • Byte count

Chunk types

  • Data
  • End of file
  • Poison pill

Shared memory layout

  • Magic number
  • Entry count
  • Total records
  • Fixed entry table

Per-symbol entry

  • Symbol
  • Price-volume sum
  • High
  • Low
  • Total volume
  • Record count

Named exit codes

  • Bad arguments
  • IPC failure
  • Child died
  • IO failure
  • Interrupted
  • Terminated

Command line

  • Input directory
  • Output directory
  • Worker count
  • Queue size
  • Clean
  • Help

One header, shared by four programs. Every structure is fixed-capacity — a shared-memory region has to have a size before anyone can map it.

03

Inside the processor

Chunks arriving on the FIFO meet a classic bounded producer-consumer queue. Two counting semaphores track empty and full slots: the reader blocks when the queue is full, and workers block when it is empty.

There are two mutexes, not one. The queue has its own and the aggregation map has its own, so a worker folding a symbol's running totals is not holding the lock the reader needs to enqueue the next chunk. A single global lock would have been simpler and would have serialized precisely the work the thread pool exists to parallelize.

Workers are shut down with a poison pill — a chunk type that means stop — rather than a separate flag somebody has to remember to check. The drain travels the same queue as the data, so there is one shutdown mechanism instead of two that can disagree.

The signal handler does the one thing a signal handler may safely do: set a flag of the correct type and return. Everything else happens back on the main path, where it is allowed to happen.

  • Bounded buffer
  • Counting semaphores
  • Separate locks
  • Worker pool
  • Poison-pill shutdown
  • Async-signal-safe handler
04

The two functions that matter

The shared header carries two small helpers that say more about Unix experience than any amount of architecture: a write loop and a read loop.

Neither assumes the kernel will move everything it was asked to move in one call. Both loop until the byte count is satisfied, and both retry rather than fail when a call is interrupted by a signal. A single write followed by optimism is the most common way a pipeline like this quietly corrupts data on a busy machine, and handling it is the difference between code that works on the sample file and code that works under load.

The reports are produced by duplicating file descriptors rather than by opening a file and printing into it — the human-readable report is standard output, redirected — and each child's output and errors are routed to its own log. It is the same technique a shell uses, applied on purpose.

  1. 01Write and read loops that handle short transfers
  2. 02Both retry on interruption rather than failing
  3. 03Magic numbers validated at the chunk and shared-memory boundaries
  4. 04Reports written through file-descriptor redirection
  5. 05Each child's output and errors routed to its own log
  6. 06Log lines carry component, process id and parent process id

Known-data KPI test

  1. Chunk framed
  2. Written in full
  3. Read in full
  4. Magic checked
  5. Parsed
  6. Aggregated

The chain names the stages a record passes through, read from the source. No timing, volume or throughput figure appears anywhere on this page, because none has been measured.

05

One run, end to end

A single wrapper script carries a run from nothing to a report. It checks that a compiler and make are present before it does anything, builds the four executables, launches the dispatcher with whatever arguments it was given, and summarizes what happened.

Worker count and queue size are both command-line options, which is the right shape for a project about concurrency: the two numbers a reader would most want to vary are the two the program asks for.

A sample input file is committed, so the project runs on a clean checkout without anyone having to find data first — a small courtesy that a surprising number of systems projects skip.

  1. 01

    Preflight

    The wrapper checks that a compiler and make are present before building anything.

  2. 02

    Build

    One Makefile produces four separate executables.

  3. 03

    Dispatch

    The dispatcher creates the FIFO, the shared-memory object and the semaphore, then forks and execs the three stages.

  4. 04

    Ingest

    The ingester walks the input directory and sends framed chunks into the FIFO.

  5. 05

    Process

    Workers drain the bounded queue, parse rows and fold them into per-symbol totals.

  6. 06

    Publish

    The aggregate is written into shared memory and the reporter is signalled.

  7. 07

    Report

    A human-readable report and a machine-readable summary are written out.

06

When a run does not finish

The more interesting path is the one where a run is interrupted. A pipeline that creates a FIFO in the filesystem, a named shared-memory object and a named semaphore has left three things behind that outlive the process, and on Linux they persist until something explicitly removes them.

So the dispatcher cleans up on interruption as well as on success. A signal sets a flag, the stages wind down, every child is reaped with a wait rather than abandoned, and the three named objects are unlinked before exit. No zombies, and nothing left behind for the next run to collide with.

The exit code carries the reason out. Bad arguments, an IPC failure, a child dying, an IO failure and each of the two termination signals all have their own named code, so a script — or a person — can tell what went wrong before opening a log.

  1. Signal received

    Interrupt or terminate

  2. Flag set

    All the handler does

  3. Stages wound down

    Poison pill through the queue

  4. Children reaped

    Waited on, never abandoned

  5. IPC objects unlinked

    FIFO, memory, semaphore

  6. Named exit code

    The reason, carried out

    07

    Aggregation

    The transformation itself is deliberately modest: group by symbol, and carry a handful of numbers per group.

    The one worth naming is the volume-weighted average price, which weights each observed price by the volume traded at it rather than treating every tick as equally informative. One share changing hands and ten thousand changing hands are not the same evidence about what something is worth.

    Computing it across a process boundary is what makes this a systems exercise rather than an arithmetic one. The shared entry stores the running price-volume sum and the running volume — not the average — because partial averages cannot be merged. The division happens once, at the end, in the process that writes the report.

    Tick records — symbol, price, volume

    Group by symbol

    • Record count
    • Total volume
    • High
    • Low
    • Price-volume sum

    Per-symbol summary, as text and as CSV

    VWAP

    Volume-weighted average price weights each observed price by its associated volume rather than treating every tick equally. The shared structure carries the running price-volume sum rather than the average, because partial averages cannot be merged — the division happens once, at the end, in the process that writes the report.

    VWAP = Σ(price × volume) / Σ(volume)
    08

    Why it matters

    The project is small — five files and roughly thirty kilobytes of C++ — and it is the most direct evidence in this portfolio of what I can do below the level of a framework.

    Processes, interprocess transport, memory that outlives the code that created it, synchronization primitives, cleanup on the path nobody tests, and an aggregation that has to stay correct across a boundary. None of that is visible in a web application, and all of it decides whether one behaves when it is under pressure.

    • Processes
    • Threads
    • Memory
    • IPC
    • Synchronization
    09

    Known limits

    The repository is public, so the left column below can be read rather than taken on trust.

    The right column is short and unflattering. Nothing has been measured — no throughput, no behaviour on a large input, no profiling. There are no automated tests. And the repository carries a single commit, so its history shows a finished thing rather than how it came to be one.

    For a project whose whole subject is systems behaviour, the absence of a single measured number is the honest headline, which is why it also sits in the hero rather than only down here.

    Verified

    • The four-process topology and its interprocess transport
    • The bounded queue, its semaphores and the worker pool
    • Short-transfer and interruption handling in the shared IO helpers
    • Per-symbol aggregation and both output formats

    Not verified

    • Throughput — nothing has been benchmarked
    • Behaviour on a large input
    • Automated tests — there are none
    • Reliability under sustained load

    Next proof

    1. A controlled dataset
    2. A throughput measurement
    3. A resource measurement
    4. Documented results
    10

    Current status

    The project is complete as an academic exercise, the repository is public, and it runs on a clean checkout against the committed sample input.

    What it does not have is any measurement. The rows below keep that apart from what is genuinely there.

    Public repository
    Available
    Source
    Five files, readable
    Sample input
    Committed
    Build
    One Makefile, four executables
    Reproducible run
    Wrapper script
    Automated tests
    None
    Benchmark
    None measured

    Evidence boundary

    Supported

    I implemented fork and exec orchestration, FIFO and shared-memory transport, named semaphores, a bounded producer-consumer queue, a worker thread pool, signal handling, resource cleanup and volume-weighted aggregation. The repository is public, so the shared header, the four stage programs and the build are all readable.

    Not overstated

    Nothing has been measured. There is no throughput benchmark, no large-input run, no profiling and no automated test, and the repository carries a single commit — so it shows a finished thing rather than how it was arrived at. No production, trading or high-frequency capability is claimed, and the contribution split between Rafay Khattak and Muhammad Umar Nadeem is not documented, so none is stated.

    Technical notes

    The pipeline is a C++17 project built by a Makefile into four separate executables, targeting Linux and POSIX interprocess communication, compiled with warnings enabled and pthreads linked. It is an academic systems project rather than a financial product: no throughput, latency or dataset-scale figure has been measured, so none is reported.

    Core stack

    • C++17
    • POSIX IPC
    • pthreads
    • Make
    • Linux

    Processes & IPC

    • fork / exec
    • FIFO
    • POSIX shared memory
    • Named semaphores
    • Signal handling
    • waitpid reaping

    Concurrency

    • Worker thread pool
    • Bounded queue
    • Counting semaphores
    • Separate queue and aggregate locks

    IO discipline

    • Short-transfer loops
    • Interruption retry
    • Magic-number validation
    • Descriptor redirection

    Data processing

    • Per-symbol aggregation
    • Record count
    • Total volume
    • High and low
    • VWAP
    Repository
    Public
    Public artifacts
    Terminal output, a process topology diagram, source excerpts and aggregation results can be added once captured from a real run.

    Contributors

    • Rafay Khattak
    • Muhammad Umar Nadeem