Skip to main content

Lock-free shared memory primitives for real-time C++ and Python

Project description

safe-shm

Lock-free shared memory primitives for real-time C++ and Python.

Header-only C++23 library providing seqlock, cyclic ring buffer, time-series temporal queries, and lifecycle management — all over POSIX shared memory with zero-copy cross-process communication. Uses Linux futex for blocking waits, std::atomic_ref for lock-free access, and per-slot seqlocks to eliminate contention.

Works on x86_64 and ARM64 Linux.

Dependencies

  • shm — POSIX shared memory wrapper
  • exception-rt — runtime exception utilities
  • fmt — formatting
  • nanobind (optional) — Python bindings
  • doctest (test only) — fetched automatically
  • nanobench (bench only) — fetched automatically

Components

Core Primitives

Header Class Description
flat_type.hpp FlatType Concept: trivially_copyable && standard_layout
seqlock.hpp SeqlockWriter<T>, SeqlockReader<T> Lock-free single-value read/write with torn-read detection
cyclic_buffer.hpp CyclicBufferWriter<T,N>, CyclicBufferReader<T,N> Lock-free ring buffer with per-slot seqlock
time_series.hpp TimeSeries<T,N> Temporal queries on cyclic buffer (closest, interpolation, freshness)
stamped.hpp Stamped<T> Metadata envelope: {timestamp_ns, sequence, data}
sanitized_key.hpp SanitizedKey<Tag> Strong-typed temporal key with external validator (opt-in)
shm_lifecycle.hpp OwnedShm<T>, ShmHeader RAII ownership, heartbeat liveness, cleanup utilities

Legacy / Specialized

Header Class Description
storage.hpp Storage<T> Write-side shared memory (futex-locked)
dblbuf_loader.hpp DblBufLoader<T> Double-buffered async reader with background jthread
image_shm.hpp DoubleBufferShm<T> Combined reader/writer with double buffering
image.hpp Image<W,H,TYPE> Compile-time image types (FHD/4K, RGB/RGBA/NV12)
shared_memory.hpp SharedMemory<T> Simple typed shm wrapper (no sync)
producer_consumer.hpp ProducerConsumer<T> Semaphore-based producer/consumer
shm_stats.hpp ShmStats Diagnostic counters with zero-cost opt-out via [[no_unique_address]]

Architecture

Seqlock (single value, lock-free)

Writer                                  Reader
┌────────────────────┐                 ┌────────────────────┐
│ seq++ (odd)        │                 │ read seq (acquire)  │
│ memcpy data        │  /dev/shm/name  │ memcpy data         │
│ seq++ (even)       ├────────────────►│ read seq (acquire)  │
│ futex_wake         │                 │ if changed → retry  │
└────────────────────┘                 └────────────────────┘

CyclicBuffer (ring buffer, per-slot seqlock)

Writer                                     Reader
┌──────────────────────┐                  ┌──────────────────────────┐
│ idx = writes & (N-1) │                  │ get_latest()             │
│ slot[idx].seq++ (odd)│  /dev/shm/name   │ get(reverse_index)       │
│ memcpy slot[idx].data├─────────────────►│ try_get() → optional<T>  │
│ slot[idx].seq++ (even│                  │ wait_for_write() [futex] │
│ total_writes++       │                  │                          │
│ futex_wake           │                  │ TimeSeries layer:        │
└──────────────────────┘                  │  find_closest(key)       │
                                          │  find_interpolation_pair │
  SHM layout:                             │  get_latest_if_fresh     │
  [total_writes] [slot₀] [slot₁] .. [slotₙ₋₁]                      │
  Each slot: [seq_counter | T data]       └──────────────────────────┘

Lifecycle Management

OwnedShm<T>
┌──────────────────────────────────┐
│ ShmHeader (32 bytes)             │
│   magic: 0x53484D48             │
│   version, heartbeat_ns         │
│   writer_pid                     │
├──────────────────────────────────┤
│ T data (user payload)            │
└──────────────────────────────────┘
  RAII: unlinks /dev/shm on destruction
  is_writer_alive(): PID check + heartbeat

Choosing a Transport

Need Use Latency
Single latest value, maximum speed Seqlock<T> ~140 ns (64 B)
Ring buffer history + temporal queries CyclicBuffer<T,N> + TimeSeries ~45 ns lookup
Double-buffered async loading (images) DoubleBufferShm<T> ~1 µs (FHD)
SHM with ownership tracking OwnedShm<T>
Simple typed shm (no sync) SharedMemory<T> ~1 ns

Quick Start

Seqlock (cross-process, lock-free)

#include "safe-shm/seqlock.hpp"

// Writer process
safe_shm::SeqlockWriter<SensorData> writer("sensor_shm");
writer.store(SensorData{20.5, 1013.25, 45.0, now_ms()});

// Reader process
safe_shm::SeqlockReader<SensorData> reader("sensor_shm");
auto data = reader.load();                              // non-blocking
auto data2 = reader.load_blocking(last_seq, timeout);   // blocks until new data

CyclicBuffer + TimeSeries (ring buffer with temporal queries)

#include "safe-shm/cyclic_buffer.hpp"
#include "safe-shm/stamped.hpp"
#include "safe-shm/time_series.hpp"

using StampedIMU = safe_shm::Stamped<IMUReading>;

// Writer: insert stamped readings at 200 Hz
safe_shm::CyclicBufferWriter<StampedIMU, 64> writer("imu_shm");
writer.insert(safe_shm::stamp(imu_reading, seq++));

// Reader: temporal queries
safe_shm::TimeSeries<StampedIMU, 64> ts("imu_shm");

auto latest = ts.get_latest();
auto closest = ts.find_closest(target_ns);           // binary search O(log N)
auto closest = ts.find_closest(target_ns, 100'000);  // with max_distance guard
auto interp = ts.find_interpolation_pair(target_ns);  // {before, after, alpha}
auto fresh = ts.get_latest_if_fresh(min_timestamp);    // staleness check

Lifecycle Management

#include "safe-shm/shm_lifecycle.hpp"

// RAII-managed SHM segment with header
safe_shm::OwnedShm<Config> owned("my_config");
owned.data() = {1.5, 0.0, 3};
owned.update_heartbeat();

// Remote liveness check
if (safe_shm::is_writer_alive(header, /*max_stale_ns=*/1'000'000'000))
    fmt::print("Writer is alive\n");

// Cleanup utilities
for (auto const& seg : safe_shm::shm_list("stale_"))
    safe_shm::shm_remove(seg);

Python Bindings

import safe_shm_py as shm

# Seqlock
writer = shm.SeqlockWriterF64("my_shm")
writer.store(3.14159)

reader = shm.SeqlockReaderF64("my_shm")
value = reader.load()  # 3.14159

# CyclicBuffer + TimeSeries
cb_writer = shm.CyclicBufferWriterStampedF64("ring_shm")
s = shm.StampedF64()
s.timestamp_ns = shm.monotonic_now_ns()
s.sequence = 0
s.data = 42.0
cb_writer.insert(s)

ts = shm.TimeSeriesStampedF64("ring_shm")
closest = ts.find_closest(target_ns)
interp = ts.find_interpolation_pair(target_ns)

Temporal Query Safety

Two layers of protection against unit/clock-type mismatches:

Layer 1: Runtime — max_distance parameter

// BUG: querying with milliseconds (5000) instead of nanoseconds
auto bad = ts.find_closest(5000);               // silently returns oldest element
auto bad = ts.find_closest(5000, 100'000'000);  // returns nullopt — mismatch caught!

// CORRECT: consistent nanosecond units
auto good = ts.find_closest(5'000'000'000, 100'000'000);  // works, within 100ms

Layer 2: Compile-time — SanitizedKey<Tag> (opt-in)

SanitizedKey<Tag> is a strong type that can only be created through an external validator function. The compiler prevents passing raw uint64_t where SanitizedKey is expected.

#include "safe-shm/sanitized_key.hpp"

// Step 1: Sanitize — the ONLY way to create a SanitizedKey
auto key = safe_shm::sanitize<safe_shm::MonotonicNsTag>(
    raw_ns, safe_shm::MonotonicNsValidator{});

if (!key) { /* rejected: wrong units or clock type */ }

// Step 2: Pass to TimeSeries — type-safe overload
auto result = ts.find_closest(*key);
auto interp = ts.find_interpolation_pair(*key);
auto fresh  = ts.get_latest_if_fresh(*key);

Built-in validators:

Validator Catches
MonotonicNsValidator{} Values that aren't plausible CLOCK_MONOTONIC nanoseconds
RangeValidator{min, max} Values outside expected range
ProximityValidator{ref, tol} Values differing from reference by > tolerance
AllOf(v1, v2, ...) Composes validators with AND logic

Different Tag types make different sanitization policies type-incompatible at compile time — SanitizedKey<MonotonicNsTag> cannot be passed where SanitizedKey<RangeCheckedTag> is expected.

Custom validators are just callables:

auto key = safe_shm::sanitize<MyTag>(raw_ns, [latest_ts](uint64_t v) {
    return (v > latest_ts ? v - latest_ts : latest_ts - v) <= 2'000'000'000ULL;
});

The raw uint64_t API is unchanged — SanitizedKey is purely opt-in.

Build

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
ctest

Sanitizers

cmake .. -DSANITIZER=asan    # Address + UB (default)
cmake .. -DSANITIZER=tsan    # Thread sanitizer
cmake .. -DSANITIZER=none    # No sanitizer

Fuzz Testing

# Requires clang
CC=clang CXX=clang++ cmake .. -DSANITIZER=fuzzer
make fuzz_cyclic_buffer fuzz_time_series
./fuzz_cyclic_buffer corpus_cyclic/ -max_total_time=60
./fuzz_time_series corpus_ts/ -max_total_time=60

Docker (CI)

docker build -t safe-shm-test -f Dockerfile.test .
docker run --rm -v $(pwd):/src safe-shm-test bash -c \
  "cd /tmp && cmake /src -DSANITIZER=none && make -j\$(nproc) && ctest"

Examples

All examples are buildable targets — see examples/ directory.

Example Language What it demonstrates
seqlock_example C++ Cross-process writer/reader with blocking reads
cyclic_buffer_example C++ Ring buffer + Stamped + TimeSeries temporal queries
lifecycle_example C++ OwnedShm RAII, heartbeat, cleanup utilities
python_seqlock.py Python Seqlock read/write + Stamped + monotonic clock
python_cyclic_buffer.py Python CyclicBuffer + TimeSeries from Python
# C++ examples (two terminals)
./seqlock_example writer    # terminal 1
./seqlock_example reader    # terminal 2

# Python examples
PYTHONPATH=build python3 examples/python_seqlock.py
PYTHONPATH=build python3 examples/python_cyclic_buffer.py

Tests

Test Type Assertions What it covers
flat_type_test Unit FlatType concept acceptance/rejection
double_buffer_swapper_test Unit Swap mechanics, structs, large arrays
swap_runner_test Unit Trigger/wait, exceptions, concurrent threads
safe_shm_test Integration Storage + DblBufLoader round-trip
shared_memory_test Integration SharedMemory typed wrapper
image_shm_test Integration DoubleBufferShm with FHD images
integration_test Integration Cross-process (fork) all components
seqlock_test Integration Seqlock store/load, cross-process, concurrent, blocking
stamped_test Integration 28 Stamped layout, stamp(), monotonic clock, ShmStats
cyclic_buffer_test Integration 24,252 Insert/get, overflow, structs, cross-process, concurrent
time_series_test Integration 66 Binary search, interpolation, freshness, edge cases, unit confusion
shm_lifecycle_test Integration 28 OwnedShm, ShmHeader, heartbeat, liveness detection
cross_process_test Integration 69 All producer/consumer combos, 1-writer-3-readers, FHD images
sanitized_key_test Integration 41 SanitizedKey construction, validators, tag safety, TimeSeries integration

Benchmarks

Run with: ./benchmark && ./benchmark_competitors (built with -O3 -DNDEBUG, nanobench)

Throughput (store + load cycle)

Method 64 B 4 KB 1 MB FHD RGB (6 MB)
memcpy (baseline) 55-60 GB/s 120-125 GB/s 26-29 GB/s 36 GB/s
raw POSIX shm (no sync) 63 GB/s 128 GB/s 28 GB/s 35 GB/s
Seqlock (lock-free) 0.4 GB/s 9.3 GB/s 9.2 GB/s 38 GB/s
CyclicBuffer (per-slot seqlock) 0.4 GB/s 11.5 GB/s 9.1 GB/s 39 GB/s
DoubleBufferShm (futex) ~6 MB/s 0.15 GB/s 6.8 GB/s 12 GB/s
POSIX mq (kernel IPC) 0.16 GB/s 7.4 GB/s
Unix domain socket 0.13 GB/s 6.2 GB/s
pipe (kernel IPC) 0.17 GB/s 9.4 GB/s 8.4 GB/s

TimeSeries Query Latency

Operation Latency Throughput
find_closest (binary search, 64 elements) 45 ns 22 Mop/s
find_interpolation_pair 66 ns 15 Mop/s
get_latest_if_fresh 11 ns 91 Mop/s

Key Takeaways

  • CyclicBuffer at FHD: 39 GB/s — matches raw memcpy with full ring buffer history
  • Seqlock vs CyclicBuffer: equivalent at small payloads, CyclicBuffer slightly faster at 4 KB
  • vs kernel IPC: 2.7x faster than pipe at 64 B, 60x faster than POSIX mq at 4 KB
  • TimeSeries lookups: 45 ns per binary search across 64 elements — suitable for real-time control loops
  • Zero-copy: no intermediate buffers, no kernel transitions, no thread wakeups on read path

Design Decisions

Per-slot seqlock — Each CyclicBuffer slot has its own 32-bit sequence counter. Readers on slot i never contend with writers on slot j. The alternative (single global seqlock) forces retry on any concurrent write.

Power-of-two capacityN & (N-1) == 0 enables bitmask modulo (idx & (N-1)) instead of expensive integer division. Enforced at compile time via static_assert.

Stamped<T> composabilityStamped<T> is itself a FlatType, so Seqlock<Stamped<SensorData>>, CyclicBuffer<Stamped<IMU>, 64>, and OwnedShm<Stamped<Config>> all work without adapter code.

std::optional return typestry_get(), find_closest(), find_interpolation_pair() return std::optional<T> instead of throwing. Compatible with std::expected patterns and real-time constraints (no heap allocation).

Futex for blockingwait_for_write() and load_blocking() use raw FUTEX_WAIT/FUTEX_WAKE (not _PRIVATE) for cross-process signaling. Uncontended path is a single atomic compare — no syscall.

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

safe_shm-0.1.0.tar.gz (60.5 kB view details)

Uploaded Source

Built Distributions

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

safe_shm-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (294.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

safe_shm-0.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (273.8 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

safe_shm-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (294.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

safe_shm-0.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (273.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

safe_shm-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (296.2 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

safe_shm-0.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (275.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

safe_shm-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (296.7 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

safe_shm-0.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (276.4 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

safe_shm-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (297.2 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

safe_shm-0.1.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (276.7 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

File details

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

File metadata

  • Download URL: safe_shm-0.1.0.tar.gz
  • Upload date:
  • Size: 60.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for safe_shm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b4133954a1f15fd95b0bb2134f26da7462aa64514b6e62dd03dc5937d6d09201
MD5 db7ce18de318f78f39ceea0059e44e7f
BLAKE2b-256 d48986381d3a259caab060c635ea99c308acc22995420e06339473e3303cf22c

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0.tar.gz:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e430aba9aaaeecf2a54de7c9652c70ea2215f2162e525627ff54234962b7f5e7
MD5 b3543216bb793f1209079f28dc7ede50
BLAKE2b-256 2346dc1071b5b315b3ca74edb5d535c78415bbc6d824843f0ff5b4ce05fe9234

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 9ced4e2372022f406dd55053e6c8330fd0ac889ec23c1c1ee92cb7d22e92dc55
MD5 1b1054ac49cc6f76c4edffb69160a2da
BLAKE2b-256 2b085fe2dcb53586056185548740d91f16ca04ca26860af5654e3bac02997809

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 baf2d61575bde36b486feba922a2bf1a33a892c509a14fe948ee390533e7c441
MD5 5635845ebea8c1de8e8fee38a4c330d1
BLAKE2b-256 c88c0312b131d585568e53d8c9effef381273e818d9d218d1a28f2888a9b3530

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 26a2b2d28c8d2deedad56c191ec133144ef2b5bbe3cb378841daa490a4099a00
MD5 a8bd532bc0ee7c5c054e84ec2f64e339
BLAKE2b-256 8ef38fb5f56e660437095003b3eed66e7e3ed5591f23a3587e7a08aff3426429

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 09322813ce3a8f6a9bdd66d82fc81f0fbdb91b14cac6cbac4e50ee672bb1dc50
MD5 c75598788c208f6d7de43f598c344a6c
BLAKE2b-256 dfcd6f9919d526e0d871a4f66d59c1bdc3d65934ad2821b06d035bf7878e8f7e

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 5145683394125b4ee2777b7590a2c766b88af204b08b8f1813a9e3b89aee6199
MD5 eeb0ceaa23f5391846a36d00cf2c0c22
BLAKE2b-256 932d2bd606b2f5ecf1440a01c11ee48d09e1b97566181600972ae0c2036a4e97

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a414491cc4d97b7be42d428940de325ad2fdccc2946d7c063f5f601c48967772
MD5 68b51436ca13b1c96f221d52494025d5
BLAKE2b-256 28d2b1856909ff503e95bb40870ddc893ba4b960c6781e81d5d0ef69b4605add

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 3f31f06f83735a2c1aa407bcb36f10f28c761896f1653dd82546a913f029d542
MD5 509ca050fc7e26d667030b0988f1f5d2
BLAKE2b-256 7e99a479a129addc482b30cff7cac736e884911a1bb05d9bd3e4e23fde252b93

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 1ce503577c0fb20df56f366526b550c67e363acddf69c23277bf2b209f8184f4
MD5 8295878e6c448264287defac7fb47692
BLAKE2b-256 aad0f85b8502a9d541b9d5eac0ec1625e0d5191de17071baa18fb2a0e3667503

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file safe_shm-0.1.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for safe_shm-0.1.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 4728d956c6c465708b8e5e7738e024de13656762856ab8fdaf1f95ec26abd5d5
MD5 7c0918611554db09fc8d4b0717f0d14d
BLAKE2b-256 9a45c254233f357a8171cc5d325ed5ad1465ace6a3f924c9f380e56eb9e31a97

See more details on using hashes here.

Provenance

The following attestation bundles were made for safe_shm-0.1.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: pypi.yml on PavelGuzenfeld/safe-shm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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