Skip to main content

Lock-free SPMC byte stream buffer with C++ interoperability via shared memory

Project description

slick-stream-buffer-py

A Python implementation of slick-stream-buffer — a lock-free single-producer multi-consumer (SPMC) byte stream buffer with shared memory support.

Maintains exact binary compatibility with the C++ version: a Python producer and a C++ consumer (or vice versa) communicate seamlessly through the same shared memory segment, using the same std::atomic synchronization primitives.

Features

  • Byte stream, message records: the producer writes raw bytes (prepare/commit) and publishes them as discrete message records (consume), like a network receive buffer feeding a framing layer
  • Lock-free SPMC: one producer, any number of consumers, no locks anywhere
  • Binary-compatible with C++: identical 64-byte header, 32-byte control records, and acquire/release memory orderings (slick/stream_buffer.hpp, header magic 'SSB1')
  • Shared memory IPC: built on multiprocessing.shared_memory, matching the C++ slick-shm naming conventions on Windows, Linux, and macOS
  • Lossy by design: slow consumers skip overwritten data and count the loss instead of blocking the producer
  • Local memory mode: same API without shared memory for single-process use
  • No runtime dependencies: Python 3.8+ standard library plus a small bundled C++ extension for the atomics

Requirements

  • Python 3.8+
  • 64-bit platform (Windows x86-64, Linux x86-64/ARM64, macOS x86-64/ARM64)
  • A C++17 compiler to build the ssb_atomic_ops_ext extension (MSVC 2017+, GCC 5+, Clang 3.8+)

Installation

pip install -e .
# or just build the extension in place:
python setup.py build_ext --inplace

Quick Start

Producer (creates the shared memory segment)

from slick_stream_buffer_py import SlickStreamBuffer

# 64 MB data ring, 65536 message records
stream = SlickStreamBuffer(capacity=1 << 26, control_size=1 << 16, name="market_data")

while True:
    mv = stream.prepare(64 * 1024)      # contiguous writable memoryview (zero-copy)
    n = sock.recv_into(mv)              # write network bytes directly into the ring
    stream.commit(n)

    # publish every complete package as one message record
    while (package_size := find_complete_package(stream.data(), stream.size())):
        stream.consume(package_size)

Consumer (opens the existing segment — can be C++ or Python)

from slick_stream_buffer_py import SlickStreamBuffer

stream = SlickStreamBuffer(name="market_data")   # geometry read from the segment header

cursor = stream.initial_reading_index()          # skip history; use 0 to read from the start
while True:
    data, length, cursor = stream.read(cursor)
    if data is None:
        continue
    handle_package(data, length)

Local memory mode (single process)

buf = SlickStreamBuffer(capacity=1024, control_size=16)

mv = buf.prepare(5)
mv[:] = b"hello"
buf.commit(5)
buf.consume(5)                     # publish as one record

data, length, cursor = buf.read(0)  # -> b"hello", 5, 1

C++ Interoperability

The C++ side uses the identical layout — either side can create the segment, the other attaches to it:

#include <slick/stream_buffer.hpp>

slick::stream_buffer stream("market_data");   // open segment created by Python

uint64_t cursor = stream.initial_reading_index();
for (;;) {
    auto [data, length] = stream.read(cursor);
    if (data == nullptr) continue;
    handle_package(data, length);
}

Shared memory naming: use get_shm_name() to obtain the exact name to pass to C++ (on POSIX it includes the leading / that shm_open() requires; on Windows it is the raw name).

Shared memory layout

[HEADER: 64 bytes]
  0-7   atomic<uint64> committed_    - monotonic end of committed bytes
  8-15  atomic<uint64> consumed_     - monotonic publish boundary
  16-23 atomic<uint64> next_seq_     - next record sequence number
  24-31 atomic<uint64> reserve_end_  - prepared-region high-water mark
  32-39 uint64 capacity_             - data ring size in bytes (power of 2)
  40-43 uint32 control_size_         - control ring record count (power of 2)
  44-47 uint32 header_magic          - 0x53534231 ('SSB1')
  48-51 atomic<uint32> init_state    - 0=uninit, 2=initializing, 3=ready
  52-63 padding
[CONTROL RING: 32 bytes x control_size]
  each record: atomic<uint64> seq | uint64 offset | uint32 length | 12 bytes padding
[DATA RING: capacity bytes]

Total segment size: 64 + 32 * control_size + capacity.

API Summary

Method Role Description
prepare(n) -> memoryview producer contiguous writable region; may relocate unconsumed bytes on wrap
commit(n) producer move n prepared bytes into the readable region (clamped)
consume(n) -> PublishedRecord producer publish the first n readable bytes as ONE record (clamped)
discard() producer drop unconsumed + prepared bytes without publishing
data() -> memoryview / size() producer the committed-but-unconsumed region
read(cursor) -> (data, length, cursor) consumer next record, or (None, 0, cursor); skips lost records
read_last() -> (data, length) consumer newest record without a cursor
initial_reading_index() consumer cursor for a late joiner (skips history)
loss_count() consumer records skipped by this instance due to overwrite
reset() owner clear all state (not thread-safe)
close() / unlink() lifecycle detach / delete the segment

See API_DIFFERENCES.md for the exact deviations from the C++ API (cursor passed by value, bytes copies vs pointers, exceptions).

Caveats (same as C++)

  • Producer methods (prepare/commit/consume/discard/data/size/reset) must be called from a single thread.
  • The buffer is lossy: if the producer outruns a consumer by more than the control ring or data ring size, the consumer skips ahead and the loss is counted.
  • prepare() may relocate the readable region: memoryviews previously returned by data() or prepare() are invalidated.
  • A single message (one consume() call) is limited to < 4 GiB.

Building and Testing

# 1. Build the atomics extension
python setup.py build_ext --inplace

# 2. Pure-Python tests
python tests/test_atomic_ops.py
python tests/test_local_mode.py
python tests/test_shm_mode.py

# 3. C++ interop tests (requires CMake + a C++20 compiler)
cmake -S . -B build
cmake --build build --config Debug
cd build && ctest -C Debug --output-on-failure

The interop tests fetch slick-stream-buffer (which pulls slick-shm) from GitHub and build real C++ producer/consumer binaries against the actual stream_buffer.hpp. To build against local checkouts instead (no network):

cmake -S . -B build \
  -DFETCHCONTENT_SOURCE_DIR_SLICK-STREAM-BUFFER=/path/to/slick-stream-buffer \
  -DFETCHCONTENT_SOURCE_DIR_SLICK-SHM=/path/to/slick-shm

If a failed test run leaves segments behind: python tests/cleanup_shm.py

Related Projects

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slick_stream_buffer_py-0.1.0.tar.gz (31.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

slick_stream_buffer_py-0.1.0-cp313-cp313-win_amd64.whl (23.7 kB view details)

Uploaded CPython 3.13Windows x86-64

slick_stream_buffer_py-0.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (38.8 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp313-cp313-macosx_10_13_universal2.whl (23.2 kB view details)

Uploaded CPython 3.13macOS 10.13+ universal2 (ARM64, x86-64)

slick_stream_buffer_py-0.1.0-cp312-cp312-win_amd64.whl (23.8 kB view details)

Uploaded CPython 3.12Windows x86-64

slick_stream_buffer_py-0.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (38.8 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp312-cp312-macosx_10_13_universal2.whl (23.2 kB view details)

Uploaded CPython 3.12macOS 10.13+ universal2 (ARM64, x86-64)

slick_stream_buffer_py-0.1.0-cp311-cp311-win_amd64.whl (23.8 kB view details)

Uploaded CPython 3.11Windows x86-64

slick_stream_buffer_py-0.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (38.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp311-cp311-macosx_10_9_universal2.whl (23.2 kB view details)

Uploaded CPython 3.11macOS 10.9+ universal2 (ARM64, x86-64)

slick_stream_buffer_py-0.1.0-cp310-cp310-win_amd64.whl (23.8 kB view details)

Uploaded CPython 3.10Windows x86-64

slick_stream_buffer_py-0.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (38.9 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp310-cp310-macosx_10_9_universal2.whl (23.2 kB view details)

Uploaded CPython 3.10macOS 10.9+ universal2 (ARM64, x86-64)

slick_stream_buffer_py-0.1.0-cp39-cp39-win_amd64.whl (23.8 kB view details)

Uploaded CPython 3.9Windows x86-64

slick_stream_buffer_py-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (38.6 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp39-cp39-macosx_10_9_universal2.whl (23.2 kB view details)

Uploaded CPython 3.9macOS 10.9+ universal2 (ARM64, x86-64)

slick_stream_buffer_py-0.1.0-cp38-cp38-win_amd64.whl (23.7 kB view details)

Uploaded CPython 3.8Windows x86-64

slick_stream_buffer_py-0.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (39.0 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

slick_stream_buffer_py-0.1.0-cp38-cp38-macosx_10_9_universal2.whl (23.0 kB view details)

Uploaded CPython 3.8macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file slick_stream_buffer_py-0.1.0.tar.gz.

File metadata

  • Download URL: slick_stream_buffer_py-0.1.0.tar.gz
  • Upload date:
  • Size: 31.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for slick_stream_buffer_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d1989f9bec9237754713753abc6e9cf666ecec942f0e8ea55cbe1106b97f68b7
MD5 5f60b38acec60c11f132574a5effc7ad
BLAKE2b-256 ccf9f9849887e32c91e1101df4d862f8151077dd3d323506de64ee3186c9aaaf

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 a3d560d308231d78381fcd8d50628bc6cb02446f086a3e7c86a8eed7c80fdf2b
MD5 1a38dedc08a0bb901aafef54dbb3af98
BLAKE2b-256 187f1e50cd2c825de1969cc9845a349b0247b404c61a6cd1f5bad59e6bae57fe

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5feada4c7daa8a4c7292e0f857fb51307dc20c14406f5b5faf740a9215ae8bc5
MD5 813565c97aa1861b62d45ca6aacae4e8
BLAKE2b-256 5aa0d446d83e71bb3a6abf6ef0959bf0ee9c253c83c7beb41128fa35cecf9f0f

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp313-cp313-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp313-cp313-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 3b340e50e9262325d57ac9c1fd092bc3419c0f49f0819dac8dc6563607817b73
MD5 41658de4b7fd5d19761e14b6f21d7b08
BLAKE2b-256 ae4f0f64734b07ec85ed771dc0e2431a9e27b90d13b0818ac955ca4f21ad7d46

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 fbd9914b54f3a4649faf65c5e173a07f9a37b76af982b3dfeeef7110ab8fb27f
MD5 9657e778f7aaac37c09e6b515734c24b
BLAKE2b-256 1615f8f2ff40fa5e397df81ebff918af548fbe3de30532769b17682167eeff46

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b3d78a858e27d05900a65b379467c325faeef50ec5fea616399cd1a8f9911a66
MD5 616eb034e65a732a11f7ec3d18283a40
BLAKE2b-256 2639e8f52e7dc285b03c9a3c84fcbdd5a6c3f2ade229a40001972816ac833ebc

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp312-cp312-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 0c8c6018667c4f2c24906b25143dad8db4fb2a818fb9c766c9037678aaa455b6
MD5 3ff00796a35edcc5383cd3c7e332baa8
BLAKE2b-256 a6d155f7dbcfde81f9caadd17d2694edd6d2d4e4fa0a963af633b49c1a122fa5

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 a238470694bc1f03dd523eb580d302068957f3664c79ad17ffaf4ceefdb711ca
MD5 812a6e2b4d5d5a8ed1c25ac49ce41c69
BLAKE2b-256 e7fda490324523f2363073361add4105eb504d4322c48386192ca864ce895173

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 29c1dde2b18f7b86f96cb10a1cc8b4ab039a1c9c31eec1a16c913357c6178ddf
MD5 d1fba6c6b63d2036c77bf41afe4d3e5a
BLAKE2b-256 df96bda315e60b0cc4875803b255b4a4df2ac0cc3956815263fc32c83f588fc0

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 5401e5763825f04360c7fc1a326f82475530c9921b6bc2788ad43253a65decc0
MD5 17543f369e9d0195b32272f3c8fa9324
BLAKE2b-256 0b4ca5e2b4174f457b1a4c97d119f57a6c6100256281016d8830788af9790d9e

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 be326f5268f431bab99e654a679693e99aebadfb867274826521b611cc5a0ed5
MD5 80605799f16fe3926a7689cff7e1556a
BLAKE2b-256 33a27ade178031ff967359660cef4d1514b6139942ceb6f13cc86db3d76ed15c

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 62a8aeabaf6acf388d283a32d9cd048e9ffaa6daf74ca99f2e62cc4ffd246927
MD5 83fe08e4315b9f96931e2267398c0804
BLAKE2b-256 f7702730cd283dc05b5b4c14ae1a6f17b483b3dbd663a34c19584fe69662d161

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 b11137a1ffa9f2532ccd86e4a006cadc3a1f909853996c260bc3d342dd4bdd83
MD5 1890658da18c27a669d33702b1e173f0
BLAKE2b-256 5f0411afdf0df1917ee1cef2eede15beab3e80494eea53ebfc7d139a7d6c87e1

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 816efc709622002d84ca0a634e94256c90936f22dbdf3a3a4b1743c101fb299e
MD5 bf7514dea8df995884eafcb0258c7310
BLAKE2b-256 ae8164c085914454aeed09a47cbd98a776d03ba2589786367eb8cef80db45f4b

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 74dc9af979ca59b2b8cb4a94c5ca1075904edb29cd6d1192b513a66ca90150de
MD5 31476edccf233e41477ac4a9af8bd3d1
BLAKE2b-256 02ae9b0131ddb54d3c1ca3494f21f68a4d0d69fffeacd71d10620e872a1eeff2

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp39-cp39-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp39-cp39-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 126646da8b691a4aa574801f4c6a581fe0132da0341b1b58c30242e56a967df6
MD5 02d4711c696ddaacebf4a0d90298d172
BLAKE2b-256 faa488921571619384498e60e5d200063716aff98dcdba66c85d031a65a30e07

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp38-cp38-win_amd64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 b632796d9a33466d0b1108cb3a1260538f90b262a491c41d9e6fb32113f3a98f
MD5 60cc5dea3d121abd91dc853376f47a14
BLAKE2b-256 ba061a5db48f2799fe94e24b0bf6ef35cf4ef4b633793e4b53c3901e3f118473

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 30ce1cec6c6dcee407a0594c122ac4ed760e0d08b7685f94e75de4ce4e2e9eb5
MD5 c57e911b80534cedb6a1dcbc353e25b0
BLAKE2b-256 1873038d10b96f45ff69a918ee5658f44e7c912d172f207d4689d0e2c22da645

See more details on using hashes here.

File details

Details for the file slick_stream_buffer_py-0.1.0-cp38-cp38-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for slick_stream_buffer_py-0.1.0-cp38-cp38-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 91021870889a293ee806bc7ea562e19132d15d0d94d110a9b62adc71bc771c99
MD5 4952947181d8ba79433b8f8f01a30691
BLAKE2b-256 58282b059bf1ffcd73a528068e4a2a9f9f0873ff19d5422536f545e72f425440

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page