Skip to main content

ZeroChannel

License: MIT

High-performance, lock-free single-writer multi-reader (SWMR) inter-process communication (IPC) over shared memory. Implemented in Rust with Python bindings via PyO3.

Overview

ZeroChannel provides a fast, asynchronous, and safe IPC mechanism for passing fixed-size entries between processes using a circular buffer in shared memory. It offers two access paths:

  • One-copy: Safe for any number of readers. Copies payload out of shared memory with a double read of sequence numbers.
  • Zero-copy: Single exclusive reader that borrows the slot in place, avoiding unnecessary memory copies.

Both paths are fully lock-free and work on x86_64 and aarch64 targets.

Features

  • Lock-free: No mutexes or spinlocks; all synchronization via atomic operations
  • Zero-copy access: Optional read path that borrows slots in place without copying
  • Cross-platform: Linux (x86_64, aarch64) and Windows (x86_64)
  • Type-safe: Generic Writer<T>/Reader<T> over any fixed-size element, with concrete aliases for bytes and f64
  • no_std core: The ring protocol lives in a dependency-free zerochannel-core crate; the OS-facing half is a thin wrapper
  • Python support: Full PyO3 bindings expose the channel to Python, releasing the GIL for every copy
  • Lossy reads: Readers can safely lag behind the writer; old entries are silently skipped

Installation

pip install zerochannel

Quick Start

Python

Entries are fixed-size: every write must be exactly entry_length long, and a read returns (entry_seq, payload) for a single entry — or None when nothing newer than the reader's watermark is available.

from zerochannel import BytesWriter, BytesReader

# The writer declares the geometry, so it creates the segment.
writer = BytesWriter("/my_channel", entry_length=64, entry_count=256)
# The reader attaches to whatever the writer published.
reader = BytesReader("/my_channel")

writer.write(b"Hello from ZeroChannel!".ljust(64, b"\0"))

entry = reader.read()
if entry is not None:
    entry_seq, payload = entry
    print(entry_seq, payload.rstrip(b"\0"))  # 1 b'Hello from ZeroChannel!'

Float64Writer/Float64Reader measure entry_length in elements rather than bytes, and read straight into a numpy array:

import numpy as np
from zerochannel import Float64Writer, Float64Reader

writer = Float64Writer("/telemetry", entry_length=7, entry_count=256)
reader = Float64Reader("/telemetry")

writer.write(np.arange(7, dtype=np.float64))

entry_seq, payload = reader.read()       # payload is a float64 ndarray

# Or read into a buffer you own, avoiding the per-read allocation:
out = np.empty(7, dtype=np.float64)
reader.read(out=out)

Zero-Copy Reading

For reduced latency, opt the reader into zero-copy mode. This claims the channel's exclusive zero-copy reader role, so only one such reader may exist at a time; ordinary readers remain unlimited.

import numpy as np
from zerochannel import Float64Reader

reader = Float64Reader("/telemetry", enable_zero_copy=True)

handle = reader.try_acquire()
if handle is not None:
    with handle:
        # `payload` is a memoryview straight into the ring slot. The writer
        # skips the slot for as long as the handle holds it.
        total = np.asarray(handle.payload).sum()
    # The slot is returned to the ring at the end of the with block.

Any numpy array or memoryview derived from payload must be gone before the handle is released — otherwise it would alias a slot the writer is free to recycle. Releasing while one is alive raises BufferError rather than allowing it, so copy the data out (or del the view) inside the block.

Rust

Add to your Cargo.toml:

[dependencies]
zerochannel = "0.1"
use zerochannel::{BytesReader, BytesWriter};

fn main() -> Result<(), zerochannel::ZeroChannelError> {
    // entry_length, entry_count, delayed_connect
    let mut writer = BytesWriter::new("/my_channel", Some(64), Some(256), false)?;
    let mut reader = BytesReader::new("/my_channel", None, None, true)?;

    writer.write(&[0u8; 64])?;

    if let Some((entry_seq, payload)) = reader.read(None)? {
        println!("{entry_seq}: {} bytes", payload.len());
    }
    Ok(())
}

A consumer that maps its own shared memory — or has no operating system to map it with — can depend on zerochannel-core instead, which is #![no_std], has no dependencies, and operates on a caller-supplied pointer.

Building from source

Prerequisites

Tool Version
Rust 1.87+ (stable)
Python 3.11+

Development build

# Build the extension
pip install maturin
maturin develop            # editable install into current venv

# Run tests
cargo test
pytest tests/

Note that the PyO3 bindings sit behind a non-default python feature, so a lint or check sweep must pass --all-features to cover them:

cargo clippy --all-targets --all-features

Release build

maturin build --release

Supported platforms

Platform Architecture Status
Linux x86_64 ✅
Linux aarch64 ✅
Windows x86_64 ✅

License

MIT – see LICENSE.

Release files for zerochannel 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for zerochannel 0.1.0
File Interpreter ABI Platform
zerochannel-0.1.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
zerochannel-0.1.0-cp311-abi3-manylinux_2_34_x86_64.whl CPython 3.11 abi3 Linux glibc 2.34+ x86-64 Details

Total release size: 577.3 kB

Release files / zerochannel-0.1.0-cp311-abi3-win_amd64.whl

Download URL zerochannel-0.1.0-cp311-abi3-win_amd64.whl
Size 218.7 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b4b492eb62846178d543a06d25dda72f90ef8d31a61961d67009a3cf9c857fe8
BLAKE2b-256 checksum
How to use checksums
174524cb458696d06e017ee1085d9d6dc2bc1446110c4398327f8bd641df7ef9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / zerochannel-0.1.0-cp311-abi3-manylinux_2_34_x86_64.whl

Download URL zerochannel-0.1.0-cp311-abi3-manylinux_2_34_x86_64.whl
Size 358.6 kB
Tags CPython 3.11 Linux glibc 2.34+ x86-64 abi3
SHA-256 checksum
How to use checksums
76042983a96ef30e4e77d6fa189c32d61784735d5ccd52da1ffab6f3025a730c
BLAKE2b-256 checksum
How to use checksums
0eef880c6771dee8df0501b1e006af43cc21bb5d006886d9c8052a7fc5058c0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page