Skip to content

Instantly share code, notes, and snippets.

@x42005e1f
Created April 4, 2026 04:53
Show Gist options
  • Select an option

  • Save x42005e1f/44b1e178c4bec96c3d02e9f52729b10e to your computer and use it in GitHub Desktop.

Select an option

Save x42005e1f/44b1e178c4bec96c3d02e9f52729b10e to your computer and use it in GitHub Desktop.
A proof of concept: atomics over shared memory
#!/usr/bin/env cython
# SPDX-FileCopyrightText: 2026 Ilya Egorov <0x42005e1f@gmail.com>
# SPDX-License-Identifier: 0BSD
# cython: freethreading_compatible = True
# cython: subinterpreters_compatible = own_gil
# distutils: extra_compile_args = -DCYTHON_USE_MODULE_STATE=1
cdef extern from "stdatomic.h" nogil:
cdef enum memory_order:
memory_order_relaxed
char atomic_fetch_add_explicit(char *obj, char arg, memory_order order)
cpdef char fetch_inc(char[:] mem) nogil:
return atomic_fetch_add_explicit(&mem[0], 1, memory_order_relaxed)
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2026 Ilya Egorov <0x42005e1f@gmail.com>
# SPDX-License-Identifier: 0BSD
import os
import time
from concurrent.futures import (
InterpreterPoolExecutor,
ProcessPoolExecutor,
ThreadPoolExecutor,
)
from multiprocessing import RawArray, set_start_method
from _testatomic import fetch_inc
arr = RawArray("B", [0])
mem = memoryview(arr).cast("B")
def work(mem=None):
if mem is None: # thread | process (fork)
fetch_inc(arr)
else: # subinterpreter
fetch_inc(mem)
def main():
expected = 0
for cls in [
ThreadPoolExecutor,
InterpreterPoolExecutor,
ProcessPoolExecutor,
]:
start = time.perf_counter()
with cls(n := min(max(4, os.process_cpu_count()), 256)) as executor:
while time.perf_counter() - start < 1: # one second
expected += n
expected %= 256
if cls is InterpreterPoolExecutor:
futures = [executor.submit(work, mem) for _ in range(n)]
else:
futures = [executor.submit(work) for _ in range(n)]
exceptions = []
for future in futures:
if future.exception() is not None:
exceptions.append(future.exception())
if exceptions:
msg = "unhandled errors from workers"
raise BaseExceptionGroup(msg, exceptions)
assert expected == mem[0], f"{expected} != {mem[0]}"
print(f"{cls.__name__}: passed")
if __name__ == "__main__":
set_start_method("fork")
main()
@Vizonex

Vizonex commented Apr 9, 2026

Copy link
Copy Markdown

https://gist.github.com/Vizonex/17bbfe7e6bb6a36d82d0d99217eaa049
Mine was a little bit more detailed even though it would've just been a rewrite of pyatomics. The cyatomics notation was mostly just incase of redefinitions being a problem.

@x42005e1f

Copy link
Copy Markdown
Author

Interesting, thanks. Perhaps it would be worth including a copyright notice and some information about the changes required by PSF-2.0 for the borrowed files. But overall, I understand the idea.

@Vizonex

Vizonex commented Apr 10, 2026 •

Copy link
Copy Markdown

Interesting, thanks. Perhaps it would be worth including a copyright notice and some information about the changes required by PSF-2.0 for the borrowed files. But overall, I understand the idea.

I'll be sure to update the license a little later.

After seeing my old attempts of trying to bridge atomics to cython I think I'm going to make it so that there is a better unified system for atomics vs the one python provides for 3.13+. One of the things that I do not like about python's version is that the code doesn't feel completely natural at all and the functions have some pretty unusual names given. I did have the idea however of making my own version as a C Library that could be a header only that I would call "uatomics.h" meaning unified atomics that could contain proper msvc, gcc & std support. This way the process of binding everything to cython can be a bit easier with the benefit of binding it to other languages for users who end up needing it, an example would be NodeJS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment