summaryrefslogtreecommitdiff
path: root/python-atomics.spec
diff options
context:
space:
mode:
Diffstat (limited to 'python-atomics.spec')
-rw-r--r--python-atomics.spec1488
1 files changed, 1488 insertions, 0 deletions
diff --git a/python-atomics.spec b/python-atomics.spec
new file mode 100644
index 0000000..edff393
--- /dev/null
+++ b/python-atomics.spec
@@ -0,0 +1,1488 @@
+%global _empty_manifest_terminate_build 0
+Name: python-atomics
+Version: 1.0.2
+Release: 1
+Summary: Atomic lock-free primitives
+License: GNU General Public License v3 (GPLv3)
+URL: https://github.com/doodspav/atomics
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/cf/15/317e77646da3b316d49c93b8ea89e57c21b128e918f640c7130e7bff1c9e/atomics-1.0.2.tar.gz
+
+Requires: python3-cffi
+
+%description
+# atomics
+This library implements a wrapper around the lower level
+[patomic](https://github.com/doodspav/patomic) C library (which is provided as
+part of this library through the `build_patomic` command in `setup.py`).
+
+It exposes hardware level lock-free (and address-free) atomic operations on a
+memory buffer, either internally allocated or externally provided, via a set of
+atomic classes.
+
+The operations in these classes are both thread-safe and process-safe,
+meaning that they can be used on a shared memory buffer for interprocess
+communication (including with other languages such as C/C++).
+
+## Table of Contents
+<!--ts-->
+* [Installing](#installing)
+* [Examples](#examples)
+ * [Incorrect](#incorrect)
+ * [Multi-Threading](#multi-threading)
+ * [Multi-Processing](#multi-processing)
+* [Docs](#docs)
+ * [Types](#types)
+ * [Construction](#construction)
+ * [Lifetime](#lifetime)
+ * [Contract](#contract)
+ * [Alignment](#alignment)
+ * [Properties](#properties)
+ * [Operations](#operations)
+ * [Special Methods](#special-methods)
+ * [Memory Order](#memory-order)
+ * [Exceptions](#exceptions)
+* [Building](#building)
+* [Future Thoughts](#future-thoughts)
+* [Contributing](#contributing)
+<!--te-->
+
+## Installing
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install atomics
+```
+Windows:
+```shell
+$ py -m pip install atomics
+```
+This library requires Python3.6+, and has a dependency on the `cffi` library.
+While the code here has no dependency on any implementation specific features,
+the `cffi` library functions used are likely to not work outside of CPython and
+PyPy.
+
+Binaries are provided for the following platforms:
+- Windows `[x86, amd64]`
+- MacOSX `[x86_64, universal2]`
+- Linux `[i686, x86_64, aarch64, ppc64le, s390x]` `[manylinux2014, musllinux_1_1]`
+- Linux `[i686, x86_64]` `[manylinux1]`
+
+If you are on one of these platforms and `pip` tries to build from source or
+fails to install, make sure that you have the latest version of `pip` installed.
+This can be done like so:
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install --upgrade pip
+```
+Windows:
+```shell
+$ py -m pip install --upgrade pip
+```
+
+If you need to build from source, check out the [Building](#building) section
+as there are additional requirements for that.
+
+## Examples
+
+### Incorrect
+The following example has a data race (`a`is modified from multiple threads).
+The program is not correct, and `a`'s value will not equal `total` at the end.
+```python
+from threading import Thread
+
+
+a = 0
+
+
+def fn(n: int) -> None:
+ global a
+ for _ in range(n):
+ a += 1
+
+
+if __name__ == "__main__":
+ # setup
+ total = 10_000_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(total // 2,))
+ t2 = Thread(target=fn, args=(total // 2,))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a}] != total[{total}]")
+```
+
+### Multi-Threading
+This example implements the previous example but `a` is now an `AtomicInt` which
+can be safely modified from multiple threads (as opposed to `int` which can't).
+The program is correct, and `a` will equal `total` at the end.
+```python
+import atomics
+from threading import Thread
+
+
+def fn(ai: atomics.INTEGRAL, n: int) -> None:
+ for _ in range(n):
+ ai.inc()
+
+
+if __name__ == "__main__":
+ # setup
+ a = atomics.atomic(width=4, atype=atomics.INT)
+ total = 10_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(a, total // 2))
+ t2 = Thread(target=fn, args=(a, total // 2))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a.load()}] == total[{total}]")
+```
+
+### Multi-Processing
+This example is the counterpart to the above correct code, but using processes
+to demonstrate that atomic operations are also safe across processes. This
+program is also correct, and `a` will equal `total` at the end. It is also how
+one might communicate with processes written in other languages such as C/C++.
+```python
+import atomics
+from multiprocessing import Process, shared_memory
+
+
+def fn(shmem_name: str, width: int, n: int) -> None:
+ shmem = shared_memory.SharedMemory(name=shmem_name)
+ buf = shmem.buf[:width]
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ for _ in range(n):
+ a.inc()
+ del buf
+ shmem.close()
+
+
+if __name__ == "__main__":
+ # setup
+ width = 4
+ shmem = shared_memory.SharedMemory(create=True, size=width)
+ buf = shmem.buf[:width]
+ total = 10_000
+ # run processes to completion
+ p1 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p2 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p1.start(), p2.start()
+ p1.join(), p2.join()
+ # print results and cleanup
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ print(f"a[{a.load()}] == total[{total}]")
+ del buf
+ shmem.close()
+ shmem.unlink()
+```
+**NOTE:** Although `shared_memory` is showcased here, `atomicview` accepts any
+type that supports the buffer protocol as its buffer argument, so other sources
+of shared memory such as `mmap` could be used instead.
+
+## Docs
+
+### Types
+The following helper (abstract-ish base) types are available in `atomics`:
+- [`ANY`, `INTEGRAL`, `BYTES`, `INT`, `UINT`]
+
+This library provides the following `Atomic` classes in `atomics.base`:
+- `Atomic --- ANY`
+- `AtomicIntegral --- INTEGRAL`
+- `AtomicBytes --- BYTES`
+- `AtomicInt --- INT`
+- `AtomicUint --- UINT`
+
+These `Atomic` classes are constructable on their own, but it is strongly
+suggested using the `atomic()` function to construct them. Each class
+corresponds to one of the above helper types (as indicated).
+
+This library also provides `Atomic*View` (in `atomics.view`) and
+`Atomic*ViewContext` (in `atomics.ctx`) counterparts to the `Atomic*` classes,
+corresponding to the same helper types.
+
+The latter of the two sets of classes can be constructed manually, although it
+is strongly suggested using the `atomicview()` function to construct them. The
+former set of classes cannot be constructed manually with the available types,
+and should only be obtained by called `.__enter__()` on a corresponding
+`Atomic*ViewContext` object.
+
+Even though you should never need to directly use these classes (apart from the
+helper types), they are provided to be used in type hinting. The inheritance
+hierarchies are detailed in the [ARCHITECTURE.md](ARCHITECTURE.md) file
+(available on GitHub).
+
+### Construction
+This library provides the functions `atomic` and `atomicview`, along with the
+types `BYTES`, `INT`, and `UINT` (as well as `ANY` and `INTEGRAL`) to construct
+atomic objects like so:
+```python
+import atomics
+
+a = atomics.atomic(width=4, atype=atomics.INT)
+print(a) # AtomicInt(value=0, width=4, readonly=False, signed=True)
+
+buf = bytearray(2)
+with atomics.atomicview(buffer=buf, atype=atomics.BYTES) as a:
+ print(a) # AtomicBytesView(value=b'\x00\x00', width=2, readonly=True)
+```
+You should only need to construct objects with an `atype` of `BYTES`, `INT`, or
+`UINT`. Using an `atype` of `ANY` or `INTGERAL` will require additional kwargs,
+and an `atype` of `ANY` will result in an object that doesn't actually expose
+any atomic operations (only properties, explained in sections further on).
+
+The `atomic()` function returns a corresponding `Atomic*` object.
+
+The `atomicview()` function returns a corresponding `Atomic*ViewContext` object.
+You can use this context object in a `with` statement to obtain an `Atomic*View`
+object. The `buffer` parameter may be any object that supports the buffer
+protocol.
+
+Construction can raise `UnsupportedWidthException` and `AlignmentError`.
+
+**NOTE:** the `width` property of `Atomic*View` objects is derived from the
+buffer's length as if it were contiguous. It is equivalent to calling
+`memoryview(buf).nbytes`.
+
+### Lifetime
+Objects of `Atomic*` classes (i.e. objects returned by the `atomic()` function)
+have a self-contained buffer which is automatically freed. They can be passed
+around and stored liked regular variables, and there is nothing special about
+their lifetime.
+
+Objects of `Atomic*ViewContext` classes (i.e. objects returned by the
+`atomicview()` function) and `Atomic*View` objects obtained from said objects
+have a much stricter usage contract.
+
+#### Contract
+
+The buffer used to construct an `Atomic*ViewContext` object (either directly or
+through `atomicview()`) **MUST NOT** be invalidated until `.release()` is
+called. This is aided by the fact that `.release()` is called automatically
+in `.__exit__(...)` and `.__del__()`. As long as you immediately use the context
+object in a `with` statement, and **DO NOT** invalidate the buffer inside that
+`with` scope, you will always be safe.
+
+The protections implemented are shown in this example:
+```python
+import atomics
+
+
+buf = bytearray(4)
+ctx = atomics.atomicview(buffer=buf, atype=atomics.INT)
+
+# ctx.release() here will cause ctx.__enter__() to raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+with ctx as a: # this calls ctx.__enter__()
+ # ctx.release() here will raise:
+ # ValueError("Cannot call 'release' while context is open.")
+
+ # ctx.__enter__() here will raise:
+ # ValueError("Cannot open context multiple times.")
+
+ print(a.load()) # ok
+
+# ctx.__exit__(...) now called
+# we can safely invalidate object 'buf' now
+
+# ctx.__enter__() will raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+# accessing object 'a' in any way will also raise an exception
+```
+
+Furthermore, in CPython, all built-in types supporting the buffer protocol will
+throw a `BufferError` exception if you try to invalidate them while they're in
+use (i.e. before calling `.release()`).
+
+As a last resort, if you absolutely must invalidate the buffer inside the `with`
+context (where you can't call `.release()`), you may call `.__exit__(...)`
+manually on the `Atomic*ViewContext` object. This is to force explicitness
+about something considered to be bad practice and dangerous.
+
+Where it's allowed, `.release()` may be called multiple times with no
+ill-effects. This also applies to `.__exit__(...)`, which has no restrictions
+on where it can be called.
+
+### Alignment
+Different platforms may each have their own alignment requirements for atomic
+operations of given widths. This library provides the `Alignment` class in
+`atomics` to ensure that a given buffer meets these requirements.
+```python
+from atomics import Alignment
+
+buf = bytearray(8)
+align = Alignment(len(buf))
+assert align.is_valid(buf)
+```
+If an atomic class is constructed from a misaligned buffer, the constructor will
+raise `AlignmentError`.
+
+By default, `.is_valid` calls `.is_valid_recommended`. The class `Alignment`
+also exposes `.is_valid_minimum`. Currently, no atomic class makes use of the
+minimum alignment, so checking for it is pointless. Support for it will be
+added in a future release.
+
+### Properties
+
+All `Atomic*` and `Atomic*View` classes have the following properties:
+- `width`: width in bytes of the underlying buffer (as if it were contiguous)
+- `readonly`: whether the object supports modifying operations
+- `ops_supported`: a sorted list of `OpType` enum values representing which
+ operations are supported on the object
+
+Integral `Atomic*` and `Atomic*View` classes also have the following property:
+- `signed`: whether arithmetic operations are signed or unsigned
+
+In both cases, the behaviour on overflow is defined to wraparound.
+
+### Operations
+
+Base `Atomic` and `AtomicView` objects (corresponding to `ANY`) expose no atomic
+operations.
+
+`AtomicBytes` and `AtomicBytesView` objects support the following operations:
+- **[base]**: `load`, `store`
+- **[xchg]**: `exchange`, `cmpxchg_weak`, `cmpxchg_strong`
+- **[bitwise]**: `bit_test`, `bit_compl`, `bit_set`, `bit_reset`
+- **[binary]**: `bin_or`, `bin_xor`, `bin_and`, `bin_not`
+- **[binary]**: `bin_fetch_or`, `bin_fetch_xor`, `bin_fetch_and`,
+ `bin_fetch_not`
+
+Integral `Atomic*` and `Atomic*View` classes additionally support the following
+operations:
+- **[arithmetic]**: `add`, `sub`, `inc`, `dec`, `neg`
+- **[arithmetic]**: `fetch_add`, `fetch_sub`, `fetch_inc`, `fetch_dec`,
+ `fetch_neg`
+
+The usage of (most of) these functions is modelled directly on the C++11
+`std::atomic` implementation found
+[here](https://en.cppreference.com/w/cpp/atomic/atomic).
+
+#### Compare Exchange (`cmpxchg_*`)
+
+The `cmpxchg_*` functions return `CmpxchgResult`. This has the attributes
+`.success: bool` which indicates whether the exchange took place, and
+`.expected: T` which holds the original value of the atomic object.
+The `cmpxchg_weak` function may fail spuriously, even if `expected` matches
+the actual value. It should be used as shown below:
+```python
+import atomics
+
+
+def atomic_mul(a: atomics.INTEGRAL, operand: int):
+ res = atomics.CmpxchgResult(success=False, expected=a.load())
+ while not res:
+ desired = res.expected * operand
+ res = a.cmpxchg_weak(expected=res.expected, desired=desired)
+```
+In a real implementation of `atomic_mul`, care should be taken to ensure that
+`desired` fits in `a` (i.e. `desired.bit_length() < (a.width * 8)`, assuming 8
+bits in a byte).
+
+#### Exceptions
+
+All operations can raise `UnsupportedOperationException` (so check
+`.ops_supported` if you need to be sure).
+
+Operations `load`, `store`, and `cmpxchg_*` can raise `MemoryOrderError` if
+called with an invalid memory order. `MemoryOrder` enum values expose the
+functions `is_valid_store_order()`, `is_valid_load_order()`, and
+`is_valid_fail_order()` to check with.
+
+### Special Methods
+`AtomicBytes` and `AtomicBytesView` implement the `__bytes__` special method.
+
+Integral `Atomic*` and `Atomic*View` classes implement the `__int__` special
+method. They intentionally do not implement `__index__`.
+
+There is a notable lack of any classes implementing special methods
+corresponding to atomic operations; this is intentional. Assignment in Python is
+not available as a special method, and we do not want to encourage people to
+use other special methods with this class, lest it lead to them accidentally
+using assignment when they meant `.store(...)`.
+
+### Memory Order
+
+The `MemoryOrder` enum class is provided in `atomics`, and the memory orders
+are directly copied from C++11's `std::memory_order` documentation found
+[here](https://en.cppreference.com/w/cpp/atomic/memory_order), except for
+`CONSUME` (which would be pointless to expose in this library).
+
+All operations have a default memory order, `SEQ_CST`. This will enforce
+sequential consistency, and essentially make your multi-threaded and/or
+multi-processed program be as correct as if it were to run in a single thread.
+
+**IF YOU DO NOT UNDERSTAND THE LINKED DOCUMENTATION, DO NOT USE YOUR OWN
+MEMORY ORDERS!!!**
+Stick with the defaults to be safe. (And realistically, this is Python, you
+won't get a noticeable performance boost from using a more lax memory order).
+
+The following helper functions are provided:
+- `.is_valid_store_order()` (for `store` op)
+- `.is_valid_load_order()` ( for `load` op)
+- `.is_valid_fail_order()` (for the `fail` ordering in `cmpxchg_*` ops)
+
+Passing an invalid memory order to one of these ops will raise
+`MemoryOrderError`.
+
+### Exceptions
+The following exceptions are available in `atomics.exc`:
+- `AlignmentError`
+- `MemoryOrderError`
+- `UnsupportedWidthException`
+- `UnsupportedOperationException`
+
+## Building
+
+**IMPORTANT:** Make sure you have the latest version of `pip` installed.
+
+Using `setup.py`'s `build` or `bdist_wheel` commands will run the
+`build_patomic` command (which you can also run directly).
+
+This clones the `patomic` library into a temporary directory, builds it, and
+then copies the shared library into `atomics._clib`.
+
+This requires that `git` be installed on your system (a requirement of the
+`GitPython` module). You will also need an ANSI/C90 compliant C compiler
+(although ideally a more recent compiler should be used). `CMake` is also
+required but should be automatically `pip install`'d if not available.
+
+If you absolutely cannot get `build_patomic` to work, go to
+[patomic](https://github.com/doodspav/patomic), follow the instructions on
+building it (making sure to build the shared library version), and then
+copy-paste the shared library file into `atomics._clib` manually.
+
+**NOTE:**
+Currently, the library builds a dummy extension in order to trick `setuptools`
+into building a non-purepython wheel. If you are ok with a purepython wheel,
+then feel free to remove the code for that from `setup.py` (at the bottom).
+Otherwise, you will need a C99 compliant C compiler, and probably the
+development libraries/headers for whichever version of Python you're using.
+
+## Future Thoughts
+- add docstrings
+- add tests
+- add support for `minimum` alignment
+- add support for constructing `Atomic` classes' buffers in shared memory
+- add support for passing `Atomic` objects to sub-processes and sub-interpreters
+- reimplement in C or Cython for performance gains (preliminary benchmarks
+put such implementations at 2x the speed of a raw `int`)
+
+## Contributing
+I don't have a guide for contributing yet. This section is here to make the
+following two points:
+- new operations must first be implemented in `patomic` before this library can
+be updated
+- new architectures, widths, and existing unsupported operations must be
+supported in `patomic` (no change required in this library)
+
+
+
+
+%package -n python3-atomics
+Summary: Atomic lock-free primitives
+Provides: python-atomics
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+BuildRequires: python3-cffi
+BuildRequires: gcc
+BuildRequires: gdb
+%description -n python3-atomics
+# atomics
+This library implements a wrapper around the lower level
+[patomic](https://github.com/doodspav/patomic) C library (which is provided as
+part of this library through the `build_patomic` command in `setup.py`).
+
+It exposes hardware level lock-free (and address-free) atomic operations on a
+memory buffer, either internally allocated or externally provided, via a set of
+atomic classes.
+
+The operations in these classes are both thread-safe and process-safe,
+meaning that they can be used on a shared memory buffer for interprocess
+communication (including with other languages such as C/C++).
+
+## Table of Contents
+<!--ts-->
+* [Installing](#installing)
+* [Examples](#examples)
+ * [Incorrect](#incorrect)
+ * [Multi-Threading](#multi-threading)
+ * [Multi-Processing](#multi-processing)
+* [Docs](#docs)
+ * [Types](#types)
+ * [Construction](#construction)
+ * [Lifetime](#lifetime)
+ * [Contract](#contract)
+ * [Alignment](#alignment)
+ * [Properties](#properties)
+ * [Operations](#operations)
+ * [Special Methods](#special-methods)
+ * [Memory Order](#memory-order)
+ * [Exceptions](#exceptions)
+* [Building](#building)
+* [Future Thoughts](#future-thoughts)
+* [Contributing](#contributing)
+<!--te-->
+
+## Installing
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install atomics
+```
+Windows:
+```shell
+$ py -m pip install atomics
+```
+This library requires Python3.6+, and has a dependency on the `cffi` library.
+While the code here has no dependency on any implementation specific features,
+the `cffi` library functions used are likely to not work outside of CPython and
+PyPy.
+
+Binaries are provided for the following platforms:
+- Windows `[x86, amd64]`
+- MacOSX `[x86_64, universal2]`
+- Linux `[i686, x86_64, aarch64, ppc64le, s390x]` `[manylinux2014, musllinux_1_1]`
+- Linux `[i686, x86_64]` `[manylinux1]`
+
+If you are on one of these platforms and `pip` tries to build from source or
+fails to install, make sure that you have the latest version of `pip` installed.
+This can be done like so:
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install --upgrade pip
+```
+Windows:
+```shell
+$ py -m pip install --upgrade pip
+```
+
+If you need to build from source, check out the [Building](#building) section
+as there are additional requirements for that.
+
+## Examples
+
+### Incorrect
+The following example has a data race (`a`is modified from multiple threads).
+The program is not correct, and `a`'s value will not equal `total` at the end.
+```python
+from threading import Thread
+
+
+a = 0
+
+
+def fn(n: int) -> None:
+ global a
+ for _ in range(n):
+ a += 1
+
+
+if __name__ == "__main__":
+ # setup
+ total = 10_000_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(total // 2,))
+ t2 = Thread(target=fn, args=(total // 2,))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a}] != total[{total}]")
+```
+
+### Multi-Threading
+This example implements the previous example but `a` is now an `AtomicInt` which
+can be safely modified from multiple threads (as opposed to `int` which can't).
+The program is correct, and `a` will equal `total` at the end.
+```python
+import atomics
+from threading import Thread
+
+
+def fn(ai: atomics.INTEGRAL, n: int) -> None:
+ for _ in range(n):
+ ai.inc()
+
+
+if __name__ == "__main__":
+ # setup
+ a = atomics.atomic(width=4, atype=atomics.INT)
+ total = 10_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(a, total // 2))
+ t2 = Thread(target=fn, args=(a, total // 2))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a.load()}] == total[{total}]")
+```
+
+### Multi-Processing
+This example is the counterpart to the above correct code, but using processes
+to demonstrate that atomic operations are also safe across processes. This
+program is also correct, and `a` will equal `total` at the end. It is also how
+one might communicate with processes written in other languages such as C/C++.
+```python
+import atomics
+from multiprocessing import Process, shared_memory
+
+
+def fn(shmem_name: str, width: int, n: int) -> None:
+ shmem = shared_memory.SharedMemory(name=shmem_name)
+ buf = shmem.buf[:width]
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ for _ in range(n):
+ a.inc()
+ del buf
+ shmem.close()
+
+
+if __name__ == "__main__":
+ # setup
+ width = 4
+ shmem = shared_memory.SharedMemory(create=True, size=width)
+ buf = shmem.buf[:width]
+ total = 10_000
+ # run processes to completion
+ p1 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p2 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p1.start(), p2.start()
+ p1.join(), p2.join()
+ # print results and cleanup
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ print(f"a[{a.load()}] == total[{total}]")
+ del buf
+ shmem.close()
+ shmem.unlink()
+```
+**NOTE:** Although `shared_memory` is showcased here, `atomicview` accepts any
+type that supports the buffer protocol as its buffer argument, so other sources
+of shared memory such as `mmap` could be used instead.
+
+## Docs
+
+### Types
+The following helper (abstract-ish base) types are available in `atomics`:
+- [`ANY`, `INTEGRAL`, `BYTES`, `INT`, `UINT`]
+
+This library provides the following `Atomic` classes in `atomics.base`:
+- `Atomic --- ANY`
+- `AtomicIntegral --- INTEGRAL`
+- `AtomicBytes --- BYTES`
+- `AtomicInt --- INT`
+- `AtomicUint --- UINT`
+
+These `Atomic` classes are constructable on their own, but it is strongly
+suggested using the `atomic()` function to construct them. Each class
+corresponds to one of the above helper types (as indicated).
+
+This library also provides `Atomic*View` (in `atomics.view`) and
+`Atomic*ViewContext` (in `atomics.ctx`) counterparts to the `Atomic*` classes,
+corresponding to the same helper types.
+
+The latter of the two sets of classes can be constructed manually, although it
+is strongly suggested using the `atomicview()` function to construct them. The
+former set of classes cannot be constructed manually with the available types,
+and should only be obtained by called `.__enter__()` on a corresponding
+`Atomic*ViewContext` object.
+
+Even though you should never need to directly use these classes (apart from the
+helper types), they are provided to be used in type hinting. The inheritance
+hierarchies are detailed in the [ARCHITECTURE.md](ARCHITECTURE.md) file
+(available on GitHub).
+
+### Construction
+This library provides the functions `atomic` and `atomicview`, along with the
+types `BYTES`, `INT`, and `UINT` (as well as `ANY` and `INTEGRAL`) to construct
+atomic objects like so:
+```python
+import atomics
+
+a = atomics.atomic(width=4, atype=atomics.INT)
+print(a) # AtomicInt(value=0, width=4, readonly=False, signed=True)
+
+buf = bytearray(2)
+with atomics.atomicview(buffer=buf, atype=atomics.BYTES) as a:
+ print(a) # AtomicBytesView(value=b'\x00\x00', width=2, readonly=True)
+```
+You should only need to construct objects with an `atype` of `BYTES`, `INT`, or
+`UINT`. Using an `atype` of `ANY` or `INTGERAL` will require additional kwargs,
+and an `atype` of `ANY` will result in an object that doesn't actually expose
+any atomic operations (only properties, explained in sections further on).
+
+The `atomic()` function returns a corresponding `Atomic*` object.
+
+The `atomicview()` function returns a corresponding `Atomic*ViewContext` object.
+You can use this context object in a `with` statement to obtain an `Atomic*View`
+object. The `buffer` parameter may be any object that supports the buffer
+protocol.
+
+Construction can raise `UnsupportedWidthException` and `AlignmentError`.
+
+**NOTE:** the `width` property of `Atomic*View` objects is derived from the
+buffer's length as if it were contiguous. It is equivalent to calling
+`memoryview(buf).nbytes`.
+
+### Lifetime
+Objects of `Atomic*` classes (i.e. objects returned by the `atomic()` function)
+have a self-contained buffer which is automatically freed. They can be passed
+around and stored liked regular variables, and there is nothing special about
+their lifetime.
+
+Objects of `Atomic*ViewContext` classes (i.e. objects returned by the
+`atomicview()` function) and `Atomic*View` objects obtained from said objects
+have a much stricter usage contract.
+
+#### Contract
+
+The buffer used to construct an `Atomic*ViewContext` object (either directly or
+through `atomicview()`) **MUST NOT** be invalidated until `.release()` is
+called. This is aided by the fact that `.release()` is called automatically
+in `.__exit__(...)` and `.__del__()`. As long as you immediately use the context
+object in a `with` statement, and **DO NOT** invalidate the buffer inside that
+`with` scope, you will always be safe.
+
+The protections implemented are shown in this example:
+```python
+import atomics
+
+
+buf = bytearray(4)
+ctx = atomics.atomicview(buffer=buf, atype=atomics.INT)
+
+# ctx.release() here will cause ctx.__enter__() to raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+with ctx as a: # this calls ctx.__enter__()
+ # ctx.release() here will raise:
+ # ValueError("Cannot call 'release' while context is open.")
+
+ # ctx.__enter__() here will raise:
+ # ValueError("Cannot open context multiple times.")
+
+ print(a.load()) # ok
+
+# ctx.__exit__(...) now called
+# we can safely invalidate object 'buf' now
+
+# ctx.__enter__() will raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+# accessing object 'a' in any way will also raise an exception
+```
+
+Furthermore, in CPython, all built-in types supporting the buffer protocol will
+throw a `BufferError` exception if you try to invalidate them while they're in
+use (i.e. before calling `.release()`).
+
+As a last resort, if you absolutely must invalidate the buffer inside the `with`
+context (where you can't call `.release()`), you may call `.__exit__(...)`
+manually on the `Atomic*ViewContext` object. This is to force explicitness
+about something considered to be bad practice and dangerous.
+
+Where it's allowed, `.release()` may be called multiple times with no
+ill-effects. This also applies to `.__exit__(...)`, which has no restrictions
+on where it can be called.
+
+### Alignment
+Different platforms may each have their own alignment requirements for atomic
+operations of given widths. This library provides the `Alignment` class in
+`atomics` to ensure that a given buffer meets these requirements.
+```python
+from atomics import Alignment
+
+buf = bytearray(8)
+align = Alignment(len(buf))
+assert align.is_valid(buf)
+```
+If an atomic class is constructed from a misaligned buffer, the constructor will
+raise `AlignmentError`.
+
+By default, `.is_valid` calls `.is_valid_recommended`. The class `Alignment`
+also exposes `.is_valid_minimum`. Currently, no atomic class makes use of the
+minimum alignment, so checking for it is pointless. Support for it will be
+added in a future release.
+
+### Properties
+
+All `Atomic*` and `Atomic*View` classes have the following properties:
+- `width`: width in bytes of the underlying buffer (as if it were contiguous)
+- `readonly`: whether the object supports modifying operations
+- `ops_supported`: a sorted list of `OpType` enum values representing which
+ operations are supported on the object
+
+Integral `Atomic*` and `Atomic*View` classes also have the following property:
+- `signed`: whether arithmetic operations are signed or unsigned
+
+In both cases, the behaviour on overflow is defined to wraparound.
+
+### Operations
+
+Base `Atomic` and `AtomicView` objects (corresponding to `ANY`) expose no atomic
+operations.
+
+`AtomicBytes` and `AtomicBytesView` objects support the following operations:
+- **[base]**: `load`, `store`
+- **[xchg]**: `exchange`, `cmpxchg_weak`, `cmpxchg_strong`
+- **[bitwise]**: `bit_test`, `bit_compl`, `bit_set`, `bit_reset`
+- **[binary]**: `bin_or`, `bin_xor`, `bin_and`, `bin_not`
+- **[binary]**: `bin_fetch_or`, `bin_fetch_xor`, `bin_fetch_and`,
+ `bin_fetch_not`
+
+Integral `Atomic*` and `Atomic*View` classes additionally support the following
+operations:
+- **[arithmetic]**: `add`, `sub`, `inc`, `dec`, `neg`
+- **[arithmetic]**: `fetch_add`, `fetch_sub`, `fetch_inc`, `fetch_dec`,
+ `fetch_neg`
+
+The usage of (most of) these functions is modelled directly on the C++11
+`std::atomic` implementation found
+[here](https://en.cppreference.com/w/cpp/atomic/atomic).
+
+#### Compare Exchange (`cmpxchg_*`)
+
+The `cmpxchg_*` functions return `CmpxchgResult`. This has the attributes
+`.success: bool` which indicates whether the exchange took place, and
+`.expected: T` which holds the original value of the atomic object.
+The `cmpxchg_weak` function may fail spuriously, even if `expected` matches
+the actual value. It should be used as shown below:
+```python
+import atomics
+
+
+def atomic_mul(a: atomics.INTEGRAL, operand: int):
+ res = atomics.CmpxchgResult(success=False, expected=a.load())
+ while not res:
+ desired = res.expected * operand
+ res = a.cmpxchg_weak(expected=res.expected, desired=desired)
+```
+In a real implementation of `atomic_mul`, care should be taken to ensure that
+`desired` fits in `a` (i.e. `desired.bit_length() < (a.width * 8)`, assuming 8
+bits in a byte).
+
+#### Exceptions
+
+All operations can raise `UnsupportedOperationException` (so check
+`.ops_supported` if you need to be sure).
+
+Operations `load`, `store`, and `cmpxchg_*` can raise `MemoryOrderError` if
+called with an invalid memory order. `MemoryOrder` enum values expose the
+functions `is_valid_store_order()`, `is_valid_load_order()`, and
+`is_valid_fail_order()` to check with.
+
+### Special Methods
+`AtomicBytes` and `AtomicBytesView` implement the `__bytes__` special method.
+
+Integral `Atomic*` and `Atomic*View` classes implement the `__int__` special
+method. They intentionally do not implement `__index__`.
+
+There is a notable lack of any classes implementing special methods
+corresponding to atomic operations; this is intentional. Assignment in Python is
+not available as a special method, and we do not want to encourage people to
+use other special methods with this class, lest it lead to them accidentally
+using assignment when they meant `.store(...)`.
+
+### Memory Order
+
+The `MemoryOrder` enum class is provided in `atomics`, and the memory orders
+are directly copied from C++11's `std::memory_order` documentation found
+[here](https://en.cppreference.com/w/cpp/atomic/memory_order), except for
+`CONSUME` (which would be pointless to expose in this library).
+
+All operations have a default memory order, `SEQ_CST`. This will enforce
+sequential consistency, and essentially make your multi-threaded and/or
+multi-processed program be as correct as if it were to run in a single thread.
+
+**IF YOU DO NOT UNDERSTAND THE LINKED DOCUMENTATION, DO NOT USE YOUR OWN
+MEMORY ORDERS!!!**
+Stick with the defaults to be safe. (And realistically, this is Python, you
+won't get a noticeable performance boost from using a more lax memory order).
+
+The following helper functions are provided:
+- `.is_valid_store_order()` (for `store` op)
+- `.is_valid_load_order()` ( for `load` op)
+- `.is_valid_fail_order()` (for the `fail` ordering in `cmpxchg_*` ops)
+
+Passing an invalid memory order to one of these ops will raise
+`MemoryOrderError`.
+
+### Exceptions
+The following exceptions are available in `atomics.exc`:
+- `AlignmentError`
+- `MemoryOrderError`
+- `UnsupportedWidthException`
+- `UnsupportedOperationException`
+
+## Building
+
+**IMPORTANT:** Make sure you have the latest version of `pip` installed.
+
+Using `setup.py`'s `build` or `bdist_wheel` commands will run the
+`build_patomic` command (which you can also run directly).
+
+This clones the `patomic` library into a temporary directory, builds it, and
+then copies the shared library into `atomics._clib`.
+
+This requires that `git` be installed on your system (a requirement of the
+`GitPython` module). You will also need an ANSI/C90 compliant C compiler
+(although ideally a more recent compiler should be used). `CMake` is also
+required but should be automatically `pip install`'d if not available.
+
+If you absolutely cannot get `build_patomic` to work, go to
+[patomic](https://github.com/doodspav/patomic), follow the instructions on
+building it (making sure to build the shared library version), and then
+copy-paste the shared library file into `atomics._clib` manually.
+
+**NOTE:**
+Currently, the library builds a dummy extension in order to trick `setuptools`
+into building a non-purepython wheel. If you are ok with a purepython wheel,
+then feel free to remove the code for that from `setup.py` (at the bottom).
+Otherwise, you will need a C99 compliant C compiler, and probably the
+development libraries/headers for whichever version of Python you're using.
+
+## Future Thoughts
+- add docstrings
+- add tests
+- add support for `minimum` alignment
+- add support for constructing `Atomic` classes' buffers in shared memory
+- add support for passing `Atomic` objects to sub-processes and sub-interpreters
+- reimplement in C or Cython for performance gains (preliminary benchmarks
+put such implementations at 2x the speed of a raw `int`)
+
+## Contributing
+I don't have a guide for contributing yet. This section is here to make the
+following two points:
+- new operations must first be implemented in `patomic` before this library can
+be updated
+- new architectures, widths, and existing unsupported operations must be
+supported in `patomic` (no change required in this library)
+
+
+
+
+%package help
+Summary: Development documents and examples for atomics
+Provides: python3-atomics-doc
+%description help
+# atomics
+This library implements a wrapper around the lower level
+[patomic](https://github.com/doodspav/patomic) C library (which is provided as
+part of this library through the `build_patomic` command in `setup.py`).
+
+It exposes hardware level lock-free (and address-free) atomic operations on a
+memory buffer, either internally allocated or externally provided, via a set of
+atomic classes.
+
+The operations in these classes are both thread-safe and process-safe,
+meaning that they can be used on a shared memory buffer for interprocess
+communication (including with other languages such as C/C++).
+
+## Table of Contents
+<!--ts-->
+* [Installing](#installing)
+* [Examples](#examples)
+ * [Incorrect](#incorrect)
+ * [Multi-Threading](#multi-threading)
+ * [Multi-Processing](#multi-processing)
+* [Docs](#docs)
+ * [Types](#types)
+ * [Construction](#construction)
+ * [Lifetime](#lifetime)
+ * [Contract](#contract)
+ * [Alignment](#alignment)
+ * [Properties](#properties)
+ * [Operations](#operations)
+ * [Special Methods](#special-methods)
+ * [Memory Order](#memory-order)
+ * [Exceptions](#exceptions)
+* [Building](#building)
+* [Future Thoughts](#future-thoughts)
+* [Contributing](#contributing)
+<!--te-->
+
+## Installing
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install atomics
+```
+Windows:
+```shell
+$ py -m pip install atomics
+```
+This library requires Python3.6+, and has a dependency on the `cffi` library.
+While the code here has no dependency on any implementation specific features,
+the `cffi` library functions used are likely to not work outside of CPython and
+PyPy.
+
+Binaries are provided for the following platforms:
+- Windows `[x86, amd64]`
+- MacOSX `[x86_64, universal2]`
+- Linux `[i686, x86_64, aarch64, ppc64le, s390x]` `[manylinux2014, musllinux_1_1]`
+- Linux `[i686, x86_64]` `[manylinux1]`
+
+If you are on one of these platforms and `pip` tries to build from source or
+fails to install, make sure that you have the latest version of `pip` installed.
+This can be done like so:
+
+Linux/MacOS:
+```shell
+$ python3 -m pip install --upgrade pip
+```
+Windows:
+```shell
+$ py -m pip install --upgrade pip
+```
+
+If you need to build from source, check out the [Building](#building) section
+as there are additional requirements for that.
+
+## Examples
+
+### Incorrect
+The following example has a data race (`a`is modified from multiple threads).
+The program is not correct, and `a`'s value will not equal `total` at the end.
+```python
+from threading import Thread
+
+
+a = 0
+
+
+def fn(n: int) -> None:
+ global a
+ for _ in range(n):
+ a += 1
+
+
+if __name__ == "__main__":
+ # setup
+ total = 10_000_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(total // 2,))
+ t2 = Thread(target=fn, args=(total // 2,))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a}] != total[{total}]")
+```
+
+### Multi-Threading
+This example implements the previous example but `a` is now an `AtomicInt` which
+can be safely modified from multiple threads (as opposed to `int` which can't).
+The program is correct, and `a` will equal `total` at the end.
+```python
+import atomics
+from threading import Thread
+
+
+def fn(ai: atomics.INTEGRAL, n: int) -> None:
+ for _ in range(n):
+ ai.inc()
+
+
+if __name__ == "__main__":
+ # setup
+ a = atomics.atomic(width=4, atype=atomics.INT)
+ total = 10_000
+ # run threads to completion
+ t1 = Thread(target=fn, args=(a, total // 2))
+ t2 = Thread(target=fn, args=(a, total // 2))
+ t1.start(), t2.start()
+ t1.join(), t2.join()
+ # print results
+ print(f"a[{a.load()}] == total[{total}]")
+```
+
+### Multi-Processing
+This example is the counterpart to the above correct code, but using processes
+to demonstrate that atomic operations are also safe across processes. This
+program is also correct, and `a` will equal `total` at the end. It is also how
+one might communicate with processes written in other languages such as C/C++.
+```python
+import atomics
+from multiprocessing import Process, shared_memory
+
+
+def fn(shmem_name: str, width: int, n: int) -> None:
+ shmem = shared_memory.SharedMemory(name=shmem_name)
+ buf = shmem.buf[:width]
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ for _ in range(n):
+ a.inc()
+ del buf
+ shmem.close()
+
+
+if __name__ == "__main__":
+ # setup
+ width = 4
+ shmem = shared_memory.SharedMemory(create=True, size=width)
+ buf = shmem.buf[:width]
+ total = 10_000
+ # run processes to completion
+ p1 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p2 = Process(target=fn, args=(shmem.name, width, total // 2))
+ p1.start(), p2.start()
+ p1.join(), p2.join()
+ # print results and cleanup
+ with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
+ print(f"a[{a.load()}] == total[{total}]")
+ del buf
+ shmem.close()
+ shmem.unlink()
+```
+**NOTE:** Although `shared_memory` is showcased here, `atomicview` accepts any
+type that supports the buffer protocol as its buffer argument, so other sources
+of shared memory such as `mmap` could be used instead.
+
+## Docs
+
+### Types
+The following helper (abstract-ish base) types are available in `atomics`:
+- [`ANY`, `INTEGRAL`, `BYTES`, `INT`, `UINT`]
+
+This library provides the following `Atomic` classes in `atomics.base`:
+- `Atomic --- ANY`
+- `AtomicIntegral --- INTEGRAL`
+- `AtomicBytes --- BYTES`
+- `AtomicInt --- INT`
+- `AtomicUint --- UINT`
+
+These `Atomic` classes are constructable on their own, but it is strongly
+suggested using the `atomic()` function to construct them. Each class
+corresponds to one of the above helper types (as indicated).
+
+This library also provides `Atomic*View` (in `atomics.view`) and
+`Atomic*ViewContext` (in `atomics.ctx`) counterparts to the `Atomic*` classes,
+corresponding to the same helper types.
+
+The latter of the two sets of classes can be constructed manually, although it
+is strongly suggested using the `atomicview()` function to construct them. The
+former set of classes cannot be constructed manually with the available types,
+and should only be obtained by called `.__enter__()` on a corresponding
+`Atomic*ViewContext` object.
+
+Even though you should never need to directly use these classes (apart from the
+helper types), they are provided to be used in type hinting. The inheritance
+hierarchies are detailed in the [ARCHITECTURE.md](ARCHITECTURE.md) file
+(available on GitHub).
+
+### Construction
+This library provides the functions `atomic` and `atomicview`, along with the
+types `BYTES`, `INT`, and `UINT` (as well as `ANY` and `INTEGRAL`) to construct
+atomic objects like so:
+```python
+import atomics
+
+a = atomics.atomic(width=4, atype=atomics.INT)
+print(a) # AtomicInt(value=0, width=4, readonly=False, signed=True)
+
+buf = bytearray(2)
+with atomics.atomicview(buffer=buf, atype=atomics.BYTES) as a:
+ print(a) # AtomicBytesView(value=b'\x00\x00', width=2, readonly=True)
+```
+You should only need to construct objects with an `atype` of `BYTES`, `INT`, or
+`UINT`. Using an `atype` of `ANY` or `INTGERAL` will require additional kwargs,
+and an `atype` of `ANY` will result in an object that doesn't actually expose
+any atomic operations (only properties, explained in sections further on).
+
+The `atomic()` function returns a corresponding `Atomic*` object.
+
+The `atomicview()` function returns a corresponding `Atomic*ViewContext` object.
+You can use this context object in a `with` statement to obtain an `Atomic*View`
+object. The `buffer` parameter may be any object that supports the buffer
+protocol.
+
+Construction can raise `UnsupportedWidthException` and `AlignmentError`.
+
+**NOTE:** the `width` property of `Atomic*View` objects is derived from the
+buffer's length as if it were contiguous. It is equivalent to calling
+`memoryview(buf).nbytes`.
+
+### Lifetime
+Objects of `Atomic*` classes (i.e. objects returned by the `atomic()` function)
+have a self-contained buffer which is automatically freed. They can be passed
+around and stored liked regular variables, and there is nothing special about
+their lifetime.
+
+Objects of `Atomic*ViewContext` classes (i.e. objects returned by the
+`atomicview()` function) and `Atomic*View` objects obtained from said objects
+have a much stricter usage contract.
+
+#### Contract
+
+The buffer used to construct an `Atomic*ViewContext` object (either directly or
+through `atomicview()`) **MUST NOT** be invalidated until `.release()` is
+called. This is aided by the fact that `.release()` is called automatically
+in `.__exit__(...)` and `.__del__()`. As long as you immediately use the context
+object in a `with` statement, and **DO NOT** invalidate the buffer inside that
+`with` scope, you will always be safe.
+
+The protections implemented are shown in this example:
+```python
+import atomics
+
+
+buf = bytearray(4)
+ctx = atomics.atomicview(buffer=buf, atype=atomics.INT)
+
+# ctx.release() here will cause ctx.__enter__() to raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+with ctx as a: # this calls ctx.__enter__()
+ # ctx.release() here will raise:
+ # ValueError("Cannot call 'release' while context is open.")
+
+ # ctx.__enter__() here will raise:
+ # ValueError("Cannot open context multiple times.")
+
+ print(a.load()) # ok
+
+# ctx.__exit__(...) now called
+# we can safely invalidate object 'buf' now
+
+# ctx.__enter__() will raise:
+# ValueError("Cannot open context after calling 'release'.")
+
+# accessing object 'a' in any way will also raise an exception
+```
+
+Furthermore, in CPython, all built-in types supporting the buffer protocol will
+throw a `BufferError` exception if you try to invalidate them while they're in
+use (i.e. before calling `.release()`).
+
+As a last resort, if you absolutely must invalidate the buffer inside the `with`
+context (where you can't call `.release()`), you may call `.__exit__(...)`
+manually on the `Atomic*ViewContext` object. This is to force explicitness
+about something considered to be bad practice and dangerous.
+
+Where it's allowed, `.release()` may be called multiple times with no
+ill-effects. This also applies to `.__exit__(...)`, which has no restrictions
+on where it can be called.
+
+### Alignment
+Different platforms may each have their own alignment requirements for atomic
+operations of given widths. This library provides the `Alignment` class in
+`atomics` to ensure that a given buffer meets these requirements.
+```python
+from atomics import Alignment
+
+buf = bytearray(8)
+align = Alignment(len(buf))
+assert align.is_valid(buf)
+```
+If an atomic class is constructed from a misaligned buffer, the constructor will
+raise `AlignmentError`.
+
+By default, `.is_valid` calls `.is_valid_recommended`. The class `Alignment`
+also exposes `.is_valid_minimum`. Currently, no atomic class makes use of the
+minimum alignment, so checking for it is pointless. Support for it will be
+added in a future release.
+
+### Properties
+
+All `Atomic*` and `Atomic*View` classes have the following properties:
+- `width`: width in bytes of the underlying buffer (as if it were contiguous)
+- `readonly`: whether the object supports modifying operations
+- `ops_supported`: a sorted list of `OpType` enum values representing which
+ operations are supported on the object
+
+Integral `Atomic*` and `Atomic*View` classes also have the following property:
+- `signed`: whether arithmetic operations are signed or unsigned
+
+In both cases, the behaviour on overflow is defined to wraparound.
+
+### Operations
+
+Base `Atomic` and `AtomicView` objects (corresponding to `ANY`) expose no atomic
+operations.
+
+`AtomicBytes` and `AtomicBytesView` objects support the following operations:
+- **[base]**: `load`, `store`
+- **[xchg]**: `exchange`, `cmpxchg_weak`, `cmpxchg_strong`
+- **[bitwise]**: `bit_test`, `bit_compl`, `bit_set`, `bit_reset`
+- **[binary]**: `bin_or`, `bin_xor`, `bin_and`, `bin_not`
+- **[binary]**: `bin_fetch_or`, `bin_fetch_xor`, `bin_fetch_and`,
+ `bin_fetch_not`
+
+Integral `Atomic*` and `Atomic*View` classes additionally support the following
+operations:
+- **[arithmetic]**: `add`, `sub`, `inc`, `dec`, `neg`
+- **[arithmetic]**: `fetch_add`, `fetch_sub`, `fetch_inc`, `fetch_dec`,
+ `fetch_neg`
+
+The usage of (most of) these functions is modelled directly on the C++11
+`std::atomic` implementation found
+[here](https://en.cppreference.com/w/cpp/atomic/atomic).
+
+#### Compare Exchange (`cmpxchg_*`)
+
+The `cmpxchg_*` functions return `CmpxchgResult`. This has the attributes
+`.success: bool` which indicates whether the exchange took place, and
+`.expected: T` which holds the original value of the atomic object.
+The `cmpxchg_weak` function may fail spuriously, even if `expected` matches
+the actual value. It should be used as shown below:
+```python
+import atomics
+
+
+def atomic_mul(a: atomics.INTEGRAL, operand: int):
+ res = atomics.CmpxchgResult(success=False, expected=a.load())
+ while not res:
+ desired = res.expected * operand
+ res = a.cmpxchg_weak(expected=res.expected, desired=desired)
+```
+In a real implementation of `atomic_mul`, care should be taken to ensure that
+`desired` fits in `a` (i.e. `desired.bit_length() < (a.width * 8)`, assuming 8
+bits in a byte).
+
+#### Exceptions
+
+All operations can raise `UnsupportedOperationException` (so check
+`.ops_supported` if you need to be sure).
+
+Operations `load`, `store`, and `cmpxchg_*` can raise `MemoryOrderError` if
+called with an invalid memory order. `MemoryOrder` enum values expose the
+functions `is_valid_store_order()`, `is_valid_load_order()`, and
+`is_valid_fail_order()` to check with.
+
+### Special Methods
+`AtomicBytes` and `AtomicBytesView` implement the `__bytes__` special method.
+
+Integral `Atomic*` and `Atomic*View` classes implement the `__int__` special
+method. They intentionally do not implement `__index__`.
+
+There is a notable lack of any classes implementing special methods
+corresponding to atomic operations; this is intentional. Assignment in Python is
+not available as a special method, and we do not want to encourage people to
+use other special methods with this class, lest it lead to them accidentally
+using assignment when they meant `.store(...)`.
+
+### Memory Order
+
+The `MemoryOrder` enum class is provided in `atomics`, and the memory orders
+are directly copied from C++11's `std::memory_order` documentation found
+[here](https://en.cppreference.com/w/cpp/atomic/memory_order), except for
+`CONSUME` (which would be pointless to expose in this library).
+
+All operations have a default memory order, `SEQ_CST`. This will enforce
+sequential consistency, and essentially make your multi-threaded and/or
+multi-processed program be as correct as if it were to run in a single thread.
+
+**IF YOU DO NOT UNDERSTAND THE LINKED DOCUMENTATION, DO NOT USE YOUR OWN
+MEMORY ORDERS!!!**
+Stick with the defaults to be safe. (And realistically, this is Python, you
+won't get a noticeable performance boost from using a more lax memory order).
+
+The following helper functions are provided:
+- `.is_valid_store_order()` (for `store` op)
+- `.is_valid_load_order()` ( for `load` op)
+- `.is_valid_fail_order()` (for the `fail` ordering in `cmpxchg_*` ops)
+
+Passing an invalid memory order to one of these ops will raise
+`MemoryOrderError`.
+
+### Exceptions
+The following exceptions are available in `atomics.exc`:
+- `AlignmentError`
+- `MemoryOrderError`
+- `UnsupportedWidthException`
+- `UnsupportedOperationException`
+
+## Building
+
+**IMPORTANT:** Make sure you have the latest version of `pip` installed.
+
+Using `setup.py`'s `build` or `bdist_wheel` commands will run the
+`build_patomic` command (which you can also run directly).
+
+This clones the `patomic` library into a temporary directory, builds it, and
+then copies the shared library into `atomics._clib`.
+
+This requires that `git` be installed on your system (a requirement of the
+`GitPython` module). You will also need an ANSI/C90 compliant C compiler
+(although ideally a more recent compiler should be used). `CMake` is also
+required but should be automatically `pip install`'d if not available.
+
+If you absolutely cannot get `build_patomic` to work, go to
+[patomic](https://github.com/doodspav/patomic), follow the instructions on
+building it (making sure to build the shared library version), and then
+copy-paste the shared library file into `atomics._clib` manually.
+
+**NOTE:**
+Currently, the library builds a dummy extension in order to trick `setuptools`
+into building a non-purepython wheel. If you are ok with a purepython wheel,
+then feel free to remove the code for that from `setup.py` (at the bottom).
+Otherwise, you will need a C99 compliant C compiler, and probably the
+development libraries/headers for whichever version of Python you're using.
+
+## Future Thoughts
+- add docstrings
+- add tests
+- add support for `minimum` alignment
+- add support for constructing `Atomic` classes' buffers in shared memory
+- add support for passing `Atomic` objects to sub-processes and sub-interpreters
+- reimplement in C or Cython for performance gains (preliminary benchmarks
+put such implementations at 2x the speed of a raw `int`)
+
+## Contributing
+I don't have a guide for contributing yet. This section is here to make the
+following two points:
+- new operations must first be implemented in `patomic` before this library can
+be updated
+- new architectures, widths, and existing unsupported operations must be
+supported in `patomic` (no change required in this library)
+
+
+
+
+%prep
+%autosetup -n atomics-1.0.2
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-atomics -f filelist.lst
+%dir %{python3_sitearch}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 1.0.2-1
+- Package Spec generated