-
-
Save x42005e1f/44b1e178c4bec96c3d02e9f52729b10e to your computer and use it in GitHub Desktop.
| #!/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() |
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.
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.