Opsmas 2025 Day 6: loopyring

Opsmas 2025 Day 6: loopyring

loopyring

What is a loopyring?

Well, what if you combine loopy and ring?

You get loopyRing!

loopyRing combines the loopy event loop with the ring producer/consumer ring buffer allowing lock-free multi-core hierarchical task delegation like:

Level 0 (Input)     Level 1 (Parse)     Level 2 (Process)     Level 3 (Output)
┌─────────────┐     ┌─────────────┐     ┌─────────────┐       ┌─────────────┐
│  Listener   │────▶│  Parser 0   │────▶│  Worker 0   │──────▶│  Replier 0  │
│  (accept)   │     │  Parser 1   │     │  Worker 1   │       │  Replier 1  │
│             │     │             │     │  Worker 2   │       │             │
└─────────────┘     └─────────────┘     │  Worker 3   │       └─────────────┘
                                        └─────────────┘

Stages

A stage is a single worker instance within a level. Multiple stages at the same level process work in parallel. Each stage has:

  • Its own thread
  • Its own event loop (loopyLoop)
  • Its own ring buffer (selfRing)
  • Connection to input rings from upstream levels

Topology

The topology defines how levels connect:

  • Level-wide attachment: All stages in level N read from all stages in level M
  • Self attachment: Stages read from their own ring (external writers publish directly)
  • Barrier routing: Entries flow through barriers for multi-phase processing

Architecture

we have a funky little architecture document with all levels of detail about how everything piles on together.

Threading Model

                                    ┌──────────────────────┐
                                    │     Main Thread      │
                                    │  (loopyRingStart)    │
                                    └──────────┬───────────┘
                                               │
                    ┌──────────────────────────┼──────────────────────────┐
                    │                          │                          │
           ┌────────▼────────┐        ┌────────▼────────┐        ┌────────▼────────┐
           │   Level 0       │        │   Level 1       │        │   Level 2       │
           │   Thread 0      │        │   Thread 0      │        │   Thread 0      │
           │                 │        │   Thread 1      │        │   Thread 1      │
           │ ┌─────────────┐ │        │ ┌─────────────┐ │        │ ┌─────────────┐ │
           │ │ Event Loop  │ │        │ │ Event Loop  │ │        │ │ Event Loop  │ │
           │ │ (loopyLoop) │ │        │ │ (loopyLoop) │ │        │ │ (loopyLoop) │ │
           │ └─────────────┘ │        │ └─────────────┘ │        │ └─────────────┘ │
           │ ┌─────────────┐ │        │ ┌─────────────┐ │        │ ┌─────────────┐ │
           │ │  Self Ring  │ │        │ │  Self Ring  │ │        │ │  Self Ring  │ │
           │ │  (buffer)   │ │        │ │  (buffer)   │ │        │ │  (buffer)   │ │
           │ └─────────────┘ │        │ └─────────────┘ │        │ └─────────────┘ │
           └─────────────────┘        └─────────────────┘        └─────────────────┘

Notification Mechanism

Workers sleep on file descriptors (pipes or eventfd on Linux) and wake when:

  1. Per-stage notification: Direct pipe write to specific stage
  2. Level-wide notification: Single write wakes all stages in level

Data Flow

Producer                    Ring Buffer                    Consumer
────────                    ───────────                    ────────
    │                                                         │
    │  1. Get next slot     ┌─────────────────────┐           │
    ├─────────────────────▶ │ ringPublisherMulti- │           │
    │                       │ EntryGetNext()      │           │
    │                       └─────────────────────┘           │
    │                                                         │
    │  2. Write entry       ┌─────────────────────┐           │
    ├─────────────────────▶ │ entry->content = x  │           │
    │                       └─────────────────────┘           │
    │                                                         │
    │  3. Commit to barrier ┌─────────────────────┐           │
    ├─────────────────────▶ │ ringPublisherMulti- │           │
    │                       │ EntryCommit()       │           │
    │                       └─────────────────────┘           │
    │                                                         │
    │  4. Notify consumer   ┌─────────────────────┐           │
    ├─────────────────────▶ │ write(pipe, 1)      │──────────▶│
    │                       └─────────────────────┘           │
    │                                                         │
    │                                          5. Wake & read │
    │                       ┌─────────────────────┐◀──────────┤
    │                       │ ringConsumerBarrier │           │
    │                       │ GetNext()           │           │
    │                       └─────────────────────┘           │
    │                                                         │
    │                                          6. Process     │
    │                       ┌─────────────────────┐◀──────────┤
    │                       │ processFn(entry)    │           │
    │                       └─────────────────────┘           │
    │                                                         │
    │                                          7. Release     │
    │                       ┌─────────────────────┐◀──────────┤
    │                       │ ringConsumerBarrier │           │
    │                       │ ReleaseEntry()      │           │
    │                       └─────────────────────┘           │

what’s it useful for?

great, but so what?

well, this is useful if you want to accelerate your magic 96-core or 288-core 3 TB RAM server into achieving perfect distributed concurrency data processing operating at the speed of contention-free memory reads and writes with no bulky syscall-triggering locks in the way.

pretty cool if you can swing it.

rando exampos here: https://github.com/mattsta/loopyRing/tree/main/examples

stats