Skip to main content

Release Status Style Status Test Status

I’ve wondered why isn’t it easier to have multiple processes be able to have a shared memory space.

I’ve also wondered about how to pass shared memory definitions between processes.

Examples

import multiprocessing
from contextlib import suppress
from concurrent.futures import ProcessPoolExecutor, as_completed
from shmutils import MappedMemory, MapFlags, MapProtections
from shmutils.utils import cffiwrapper
from shmutils.lock import Lock
from shmutils.shm import shm_open, shm_unlink


def _increment_to(m, lock: Lock, value: cffiwrapper, limit: int):
    """
    Returns the number of times we increments the number
    """
    count = value[0]
    num_incr = 0
    while count < limit:
        with lock:
            count = value[0]
            if count >= limit:
                break
            num_incr += 1
            value[0] = count + 1
    return num_incr


if __name__ == "__main__":
    with suppress(FileNotFoundError):
        shm_unlink("test-lock")

    fd = shm_open("test-lock", "w+")
    size = 4096 * 100
    fd.truncate(size)
    assert fd.size() == size
    with MappedMemory(0, size, MapProtections.READ_WRITE, MapFlags.SHARED, fd) as m:
        with ProcessPoolExecutor(mp_context=multiprocessing.get_context("fork")) as exe:
            lock = Lock(m)
            counter = m.new("size_t *", 0)
            with lock:
                result1 = exe.submit(_increment_to, m, lock, cffiwrapper(counter, m), 2_0000)
                result2 = exe.submit(_increment_to, m, lock, cffiwrapper(counter, m), 2_0000)
            tuple(as_completed((result1, result2)))
            r1, r2 = result1.result(), result2.result()
        assert (result1.exception(), result2.exception()) == (None, None)
        assert counter[0] == 2_0000
        assert r1 + r2 == 2_0000

A MappedMemory is helpful for straddling the relative/absolute address world.

Roadmap

  • ✅ Virtual address stability

    • MappedMemory.at (relative address to bytes)

    • MappedMemory.absolute_at (absolute address to bytes)

    • MappedMemory.abs_address_at (relative address -> absolute address)

    • MappedMemory.relative_address_at (absolute address -> relative address)

  • 🚧 Support Python multiprocessing.get_context("spawn")

  • ☐ figure out pickling of cffi.CData

  • ☐ switch to instruct for internal classes

    • implement something like:

      from instruct import CBase, class_metadata, astuple
      from shmutils import MemoryMap, MemoryFlags, ffi as shmffi
      
      ...
      fd = shm_open(...).truncate(PAGE_SIZE)
      page = MemoryMap(None, PAGE_SIZE, flags=MemoryFlags.SHARED, fd=fd)
      ...
      
      ffi = cffi.FFI()
      # You can include other ffi's to reuse c type declarations
      ffi.include(shmffi)
      ffi.cdef('''
          typedef enum {INACTIVE, ACTIVE, DELETED} OrgUserStatus_t;
      ''')
      # pass into the instruct.CBase class an ffi instead of ``instruct.default_ffi``
      class User(CBase, ffi=ffi):
          __slots__ = '''
          struct org_user_t {
              uint64_t id;
              char     *fullname;
              uint8_t  fullname_len;
              OrgUserStatus_t status;
          };
          '''
      
      assert User.__slots__ == ()
      assert ffi.typeof(class_metadata(User, "cdecl")) is ffi.typeof('struct org_user_t)
      assert ffi.sizeof('struct org_user_t') == class_metadata(User, "csizeof")
      assert ffi is class_metadata(User, "default_ffi")
      
      lib = ffi.dlopen(None)
      # Allocate using ``ffi.new``
      u = User.new(12345, b"Autumn", 6, lib.ACTIVE)
      assert User.typeof(u) == 'struct org_user_t*'
      assert ffi.typeof(u.id) is ffi.typeof('uint64_t')
      assert ffi.typeof(u.fullname) is ffi.typeof('char*')
      assert ffi.typeof(u.fullname_len) is ffi.typeof('uint8_t')
      assert len(memoryview(User.getbuffer(u))) == User.sizeof()
      assert len(memoryview(User.getbuffer(u))) == User.sizeof(u)
      assert not hasattr(u, 'sizeof')
      assert u.__internals__["heap"] is None
      
      # Allocate using an alternate function
      # in this case, use the ``.new`` malloc for the
      # shared page
      SharedUser: Type[User] = User.with_heap(page)
      u2 = SharedUser.new()
      assert u2.__internals__["heap"] is page
      assert u2.id == 0
      assert u2.fullname == ffi.NULL
      assert u2.fullname_len == 0
      assert u2.status == 0
      
      # as far as the cdata is concerned, it points into the ``page``'s heap
      # User the ``CBase``'s ``.addressof`` call to get a pointer to the entity
      abs_ptr = ffi.cast('uintptr_t', User.addressof(u2))
      assert page.address.begin <= int(abs_ptr) < page.address.end
      # page contents and buffer match each other
      assert page.absolute_at[abs_ptr: abs_ptr + User.sizeof()] == User.getbuffer()[0: User.sizeof()]
      
      # demo assign
      # allocate space for a name
      raw_u2_fullname = page.new('char*', b'Autumn Jolitz')
      u2.id = 4123
      u2.fullname = u2_fullname  # assign the pointer
      u2.fullname_len = 13
      u2.status = lib.ACTIVE
      assert astuple(u2) == (u2.id, raw_u2_fullname, u2.fullname_len, lib.ACTIVE)
      u2_copy = pickle.loads(pickle.dumps(u2))
      assert u2_copy.__internals__["heap"] is page
      assert astuple(u2) == astuple(u2_copy)
  • ☐ split Locks into RawLock|RawRLock (consumes a memoryview|bytebuffer|ffi.buffer, allocates from 0 to length of lock size)

  • ☐ split Locks into Lock|RLock

  • ☐ reimplement locking in terms of a condition variable

  • ☐ use liblfs for a freelist

  • ☐ make a shared heap process-safe

Limitations

  • Toy allocator, does not synchronize writes between processes (only parent is expected to have allocated ahead of time).

spawn vs fork

Use “fork” multiprocessing method instead.

The “spawn” multiprocessing method is subject to ASLR and sometimes the kernel locates a child process starting much higher than our process. This has the effect of breaking the requirement for absolute pointers working.

However, it is observed that if a parent process manages to get a high enough memory page, the probability of the child process being able to mmap(2) the same address increases significantly.

import multiprocessing
from contextlib import suppress
from concurrent.futures import ProcessPoolExecutor, wait
from mmap import PAGESIZE

from shmutils import MappedMemory, MapFlags
from shmutils.mmap import munmap, round_to_page_size
from shmutils.shm import shm_open, ffi, shm_unlink
from shmutils.utils import cffiwrapper

# cffiwrapper - use to pickle/unpickle cffi objects between processes


def _set_data_to(value: cffiwrapper, to: int) -> int:
    was = value[0]
    for i in range(was, to):
        value[0] = i
    value[0] = to
    return was


if __name__ == "__main__":
    with suppress(FileNotFoundError):
        shm_unlink("test-mmap-spawn")
    with shm_open("test-mmap-spawn", "x+") as fd:
        shared_size = round_to_page_size(1024 * 1024 * 1024)
        fd.truncate(shared_size)
        # Allocate a dummy 512 MiB blockrange
        unused_space = MappedMemory(None, 512 * 1024 * 1024)
        # write to the pages to ensure we're not being fooled
        unused_space[len(unused_space) - PAGESIZE : len(unused_space) - PAGESIZE + 4] = b"sink"

        # Calculate the last page in the unused space range
        start_address: int = unused_space.abs_address_at[len(unused_space) - PAGESIZE]
        # detach the unused space guts so we can free all bu the last page
        raw_address, size = unused_space.detach()
        # free all BUT the last page
        munmap(raw_address, size - PAGESIZE)
        del unused_space

        # Prove our start address is the last page of the mostly freed range
        # (our last page is still mapped.)
        assert int(ffi.cast("uintptr_t", raw_address)) + size - PAGESIZE == start_address

        with MappedMemory(
            start_address, shared_size, flags=MapFlags.SHARED | MapFlags.FIXED, fd=fd
        ) as m:
            with ProcessPoolExecutor(1, mp_context=multiprocessing.get_context("spawn")) as exe:
                value = m.new("int64_t*", 1923)
                assert value[0] == 1923
                # The child process will now be able to mess with this counter
                future = exe.submit(_set_data_to, cffiwrapper(value, m), 8900)
                wait([future])
                # And we can see the results both on the value in memory and from the
                # return
                assert future.done() and not future.exception()
                assert (future.result(), value[0]) == (1923, 8900)

Metadata

Release files for shmutils 0.0.2

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

Source distribution (sdist)

Source distribution for shmutils 0.0.2
File Size Uploaded Provenance
shmutils-0.0.2.tar.gz 24.4 kB Without provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for shmutils 0.0.2
File
shmutils-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl CPython 3.10 CPython 3.10 Linux musl 1.1+ x86-64 Details
shmutils-0.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 Details
shmutils-0.0.2-cp310-cp310-macosx_10_9_x86_64.whl CPython 3.10 CPython 3.10 macOS 10.9+ x86-64 Details
shmutils-0.0.2-cp39-cp39-musllinux_1_1_x86_64.whl CPython 3.9 CPython 3.9 Linux musl 1.1+ x86-64 Details
shmutils-0.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 CPython 3.9 Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 Details
shmutils-0.0.2-cp39-cp39-macosx_10_9_x86_64.whl CPython 3.9 CPython 3.9 macOS 10.9+ x86-64 Details
shmutils-0.0.2-cp38-cp38-musllinux_1_1_x86_64.whl CPython 3.8 CPython 3.8 Linux musl 1.1+ x86-64 Details
shmutils-0.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.8 CPython 3.8 Linux glibc 2.5+ x86-64, Linux glibc 2.17+ x86-64 Details
shmutils-0.0.2-cp38-cp38-macosx_10_9_x86_64.whl CPython 3.8 CPython 3.8 macOS 10.9+ x86-64 Details
shmutils-0.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl CPython 3.7 CPython 3.7 pymalloc Linux musl 1.1+ x86-64 Details
shmutils-0.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.7 CPython 3.7 pymalloc Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 Details
shmutils-0.0.2-cp37-cp37m-macosx_10_9_x86_64.whl CPython 3.7 CPython 3.7 pymalloc macOS 10.9+ x86-64 Details

Total release size: 638.0 kB

Release files / shmutils-0.0.2.tar.gz

Download URL shmutils-0.0.2.tar.gz
Size 24.4 kB
Tags Source
SHA-256 checksum
How to use checksums
ac3b977c3f187ba3677fa8c7833c83beeedf348edb42aeca755986de4b8e2678
BLAKE2b-256 checksum
How to use checksums
9b83c519cc7a5aa97e91d30bcb1a7f2242d44c4e5e088afb71ec68b55ecaa21f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl

Download URL shmutils-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl
Size 61.2 kB
Tags CPython 3.10 Linux musl 1.1+ x86-64
SHA-256 checksum
How to use checksums
7dfeeda298efc9d455fc6ce1a720a8c001494a1f382e7c41c8030e9f47f6462a
BLAKE2b-256 checksum
How to use checksums
cfa16144fea89f7aab5a2008c411a94295b68caf696ad205f30652b1ee20e302
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL shmutils-0.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 59.8 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
9a2402130937158cbe61da1f87bc4c8999b0176f74d3a372d7d1ae25fdd5a74d
BLAKE2b-256 checksum
How to use checksums
5626fd359f15136a800b085e07ce1e61fe5ad5593b6dad274b35273448e65f5a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp310-cp310-macosx_10_9_x86_64.whl

Download URL shmutils-0.0.2-cp310-cp310-macosx_10_9_x86_64.whl
Size 32.3 kB
Tags CPython 3.10 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
36510615f267e06bceeb300bf55c896522e63ba4ebb973012413710b68b455ce
BLAKE2b-256 checksum
How to use checksums
4aa475d1be8dbad3c944e21a10e03077e27259d683a90546ba5b689512c34f9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp39-cp39-musllinux_1_1_x86_64.whl

Download URL shmutils-0.0.2-cp39-cp39-musllinux_1_1_x86_64.whl
Size 61.3 kB
Tags CPython 3.9 Linux musl 1.1+ x86-64
SHA-256 checksum
How to use checksums
c6be8e6a8ca1ababfe16c0f1c436d09648a98599bfe1123e836762d77aea59ef
BLAKE2b-256 checksum
How to use checksums
c58f43060d06350571b35eb564da031bfa206c8588e370df6a7c0680f794b972
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL shmutils-0.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 59.8 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
24e5e96f10d5625d0ea1704c3b484471ed02b6f7913aa1ffd7b8224f27123113
BLAKE2b-256 checksum
How to use checksums
8a6065c5ea8615653c95d5a100278b4f1aaf1e8bf7e4e9cc0088d92c43cf89d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp39-cp39-macosx_10_9_x86_64.whl

Download URL shmutils-0.0.2-cp39-cp39-macosx_10_9_x86_64.whl
Size 32.3 kB
Tags CPython 3.9 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
73a9b076f36799ce7671d6770fa2af66adee9e43f4bd0510347f9cad42f155ae
BLAKE2b-256 checksum
How to use checksums
8755572491a8aef88eb292380d7e528fa783bcde1bc894e0df40d3e7102f758e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp38-cp38-musllinux_1_1_x86_64.whl

Download URL shmutils-0.0.2-cp38-cp38-musllinux_1_1_x86_64.whl
Size 61.7 kB
Tags CPython 3.8 Linux musl 1.1+ x86-64
SHA-256 checksum
How to use checksums
199a3d3916771aac341a96a3243ae2dc79813ced721fa12ab864762cbd7ae9d0
BLAKE2b-256 checksum
How to use checksums
827b1093fbb576545f130c03506967a1b1df9e80dff72cab8dc9e655d160729f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL shmutils-0.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 59.9 kB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
0d5215d68c380c05e1bf758dcfe4e90dc465158a4dae9e1ce3dbf6058bd1547e
BLAKE2b-256 checksum
How to use checksums
171c94a657db26c7dca23e9852d365805e55d0b6e2b66b6b3b0a6d6f7cb49ba7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp38-cp38-macosx_10_9_x86_64.whl

Download URL shmutils-0.0.2-cp38-cp38-macosx_10_9_x86_64.whl
Size 32.3 kB
Tags CPython 3.8 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
61a84c3ee80216a8f536fed33269d7ee840571b7eff50767b4afd29f6801f916
BLAKE2b-256 checksum
How to use checksums
cf4123924229cc59f9a00baa1acba9ea18255df505503c0a6196744b466cc91f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl

Download URL shmutils-0.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl
Size 61.2 kB
Tags CPython 3.7 CPython 3.7 pymalloc Linux musl 1.1+ x86-64
SHA-256 checksum
How to use checksums
4f8345eb917f3113b5aac86b82b4602376c16d82968cb9e6b1bd5e786fbbcbd5
BLAKE2b-256 checksum
How to use checksums
6e610d521583149da4e6d4b6f212442722cd35d8a476819faf349ad92c22be1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL shmutils-0.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 59.7 kB
Tags CPython 3.7 CPython 3.7 pymalloc Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
4bb5c93a3258209d86e8c477e7e1dc8e6bd9c9d1321fd05ef3ef28320ac8b173
BLAKE2b-256 checksum
How to use checksums
93e436ffe833503a962056d65877f4c923d3b342434b60a253433bb1f429c883
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Release files / shmutils-0.0.2-cp37-cp37m-macosx_10_9_x86_64.whl

Download URL shmutils-0.0.2-cp37-cp37m-macosx_10_9_x86_64.whl
Size 32.2 kB
Tags CPython 3.7 CPython 3.7 pymalloc macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
54cd88acc60dbf2812d7ec8e0d2c319578d5616673eb6d9f2a543038fabe50d6
BLAKE2b-256 checksum
How to use checksums
3b96af5dcdbdf0c41184a572e0ffc068d0dd8737b276ba6f3b9b6d8a3c8b7ae4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.13

Security for shmutils 0.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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