├── .github ├── check-basic-style.sh └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── atomig-macro ├── Cargo.toml └── src │ └── lib.rs ├── examples ├── custom_type.rs └── simple.rs └── src ├── impls.rs ├── lib.rs └── tests.rs /.github/check-basic-style.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Checks 4 | # - trailing whitespaces (not allowed) 5 | # - single trailing newline (required) 6 | # - bad windows/mac line endings 7 | # - tab characters 8 | # - lines longer than XX chars 9 | 10 | # config 11 | COLS=100 12 | FOLDER="." 13 | FILES='.+\.\(rs\|toml\|sh\)' 14 | EXCLUDE='^\(\.\/\)?target\/.*' 15 | 16 | 17 | # Exit script on the first error 18 | set -o errexit -o nounset 19 | 20 | ERROR=0 21 | ### Trailing Whitespaces =========================== 22 | echo "" 23 | echo "=== Searching for lines with trailing whitespace... ===================" 24 | FOUNDTW=0 25 | for f in $(find $FOLDER -regex $FILES -not -regex $EXCLUDE); do 26 | if egrep -q " +$" $f ; then 27 | echo "! Has trailing whitespace: $f" 28 | FOUNDTW=1 29 | fi 30 | done 31 | 32 | if [ $FOUNDTW -eq 0 ] ; then 33 | echo "=== None found! :-)" 34 | else 35 | echo "" 36 | echo "!!! Some lines were found. Please remove the trailing whitespace!" 37 | ERROR=1 38 | fi 39 | 40 | ### Trailing newlines =============================== 41 | echo "" 42 | echo "=== Searching for files without trailing newline... ===================" 43 | FOUND=0 44 | for f in $(find $FOLDER -regex $FILES -not -regex $EXCLUDE); do 45 | lastline=$(tail -n 1 $f; echo x) 46 | lastline=${lastline%x} 47 | if [ "${lastline: -1}" != $'\n' ] ; then 48 | echo "! Has no single trailing newline: $f" 49 | FOUND=1 50 | fi 51 | done 52 | 53 | if [ $FOUND -eq 0 ] ; then 54 | echo "=== None found! :-)" 55 | else 56 | echo "" 57 | echo "!!! Some files were found. Please add a single trailing newline!" 58 | ERROR=1 59 | fi 60 | 61 | ### windows and mac OS line endings ======================= 62 | echo "" 63 | echo "=== Searching for files with wrong line endings ===================" 64 | 65 | FOUNDLE=0 66 | for f in $(find $FOLDER -regex $FILES -not -regex $EXCLUDE); do 67 | if grep -q $'\r' $f ; then 68 | echo "! Has windows/mac line ending: $f" 69 | FOUNDLE=1 70 | fi 71 | done 72 | 73 | if [ $FOUNDLE -eq 0 ] ; then 74 | echo "=== None found! :-)" 75 | else 76 | echo "" 77 | echo "!!! Some lines were found. Please use unix line endings!" 78 | ERROR=1 79 | fi 80 | 81 | ## tab characters ======================= 82 | echo "" 83 | echo "=== Searching for files with tab characters =================" 84 | 85 | FOUNDTAB=0 86 | for f in $(find $FOLDER -regex $FILES -not -regex $EXCLUDE); do 87 | if grep -q $'\t' $f ; then 88 | echo "! Has tab character: $f" 89 | FOUNDTAB=1 90 | fi 91 | done 92 | 93 | if [ $FOUNDTAB -eq 0 ] ; then 94 | echo "=== None found! :-)" 95 | else 96 | echo "" 97 | echo "!!! Some files were found. Please indent with spaces only!" 98 | ERROR=1 99 | fi 100 | 101 | 102 | 103 | ### char limit =================================== 104 | echo "" 105 | echo "=== Searching for files with too long lines... ========================" 106 | FOUND=0 107 | for f in $(find $FOLDER -regex $FILES -not -regex $EXCLUDE); do 108 | if [ $(wc -L $f | cut -d" " -f1) -gt $COLS ] ; then 109 | echo "! Line with more than $COLS chars in $f" 110 | FOUND=1 111 | fi 112 | done 113 | 114 | if [ $FOUND -eq 0 ] ; then 115 | echo "=== None found! :-)" 116 | else 117 | echo "" 118 | echo "!!! Some files were found. Please shorten those lines!" 119 | ERROR=1 120 | fi 121 | 122 | test $ERROR == 0 123 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [pull_request, push] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | RUSTFLAGS: --deny warnings 8 | 9 | jobs: 10 | style: 11 | name: Check basic style 12 | runs-on: ubuntu-24.04 13 | steps: 14 | - uses: actions/checkout@v2 15 | - run: ./.github/check-basic-style.sh 16 | 17 | check: 18 | name: 'Build & test' 19 | runs-on: ubuntu-24.04 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - run: cargo build 24 | - run: cargo test 25 | - run: cargo doc 26 | 27 | - run: cargo build --features="derive serde" 28 | - run: cargo test --features="derive serde" 29 | - run: cargo doc --features="derive serde" 30 | 31 | - run: rustup target add thumbv7em-none-eabi 32 | - run: cargo build --target=thumbv7em-none-eabi 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | examples/test.rs 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.4.3] - 2025-05-17 10 | - Update `syn` dependency to 2.x (only relevant when using `derive` feature) in #15 (thanks `@edwloef`) 11 | 12 | ## [0.4.2] - 2024-10-18 13 | - Add `Send + Sync + Unpin` bound to `PrimitiveAtom::Impl`. 14 | As a consequence, these traits are now implemented for `Atomic` unconditionally. 15 | ([#14](https://github.com/LukasKalbertodt/atomig/issues/14)) 16 | 17 | ## [0.4.1] - 2024-03-04 18 | - Add const fn `Atomic::from_impl` to allow creating `static` `Atomic`s [#11](https://github.com/LukasKalbertodt/atomig/pull/11) 19 | - Add `Atom` & `AtomLogic` impl for small, pow2-sized integer arrays [#12](https://github.com/LukasKalbertodt/atomig/pull/12) 20 | - Fix CI badge in README 21 | 22 | ## [0.4.0] - 2022-04-09 23 | ### Changed 24 | - **Breaking**: the minimal supported Rust version (MSRV) is now 1.60 25 | 26 | - Use `cfg(target_has_atomic)` to conditionally disable some impls. Previously, 27 | the whole crate just failed to compile on targets like `thumbv7em-none-eabi`, 28 | as `AtomicU64` is not available there. Now it compiles, but there is no 29 | `Atom` impl for `u64` or `f64`, for example. This is not yet an optimal 30 | solution, as platform support for atomics is not just differentiated by size, 31 | but also by whether they support some features. Sadly, for those, there are 32 | no `cfg` flags available on stable yet. 33 | 34 | - Relax `derive` for structs by only requiring `Atom` for the inner field, not 35 | `PrimitiveAtom`. 36 | 37 | - Derive macro uses `syn`, `quote` and `proc-macro2` in version 1.x now. This is 38 | semantically irrelevant for you, but might remove the pre-1.x versions of 39 | these crates from your dependency graph if atomig was the last to use them. 40 | 41 | 42 | ## [0.3.3] - 2021-12-30 43 | ### Changed 44 | - This library is now `no_std` compatible. All paths to `std` items were replaced by `core`. 45 | (Thanks @eivindbergem https://github.com/LukasKalbertodt/atomig/pull/5) 46 | 47 | ## [0.3.2] - 2021-10-02 48 | ### Added 49 | - `Atom` and `AtomInteger` impl for `Option` 50 | 51 | ## [0.3.1] - 2021-06-18 52 | ### Added 53 | - `Atom` impl for `std::ptr::NonNull` and `Option>` 54 | - `Atom` and `AtomLogic` impl for `std::num::Wrapping` 55 | - `Atom` for `std::num::NonZero*` types 56 | 57 | ## [0.3.0] - 2021-06-18 58 | ### Changed 59 | - **Breaking**: the minimal supported Rust version (MSRV) is now 1.53 60 | - **Breaking**: Remove deprecated method `compare_and_swap`. This method is 61 | deprecated in std and can be easily replaced by `compare_exchange`. See 62 | `std` docs for the migration. 63 | - **Breaking**: all items of traits in the `impls` module are now considered 64 | implementation detail and not part of the public API. 65 | - **Breaking**: the traits in the `impls` module were refactored and a lot 66 | changed about all of them. But due to the previous point, you are not 67 | supposed to care anymore :P 68 | - Make `fetch_update` available to all `Atomic`, not only `T: AtomicInteger`. 69 | 70 | ## [0.2.0] - 2020-07-30 71 | ### Breaking 72 | - The minimal required Rust version is now 1.45 73 | 74 | ### Added 75 | - Add `serde` feature which implements `Serialize` and `Deserialize` for atomic 76 | types as appropriate (#2) 77 | 78 | ### Changed 79 | - Some methods previously gated by the `nightly` feature are now always 80 | available (they were stabilized in Rust 1.45). 81 | 82 | ### Removed 83 | - Remove `nightly` feature.This means `atomig` no longer uses 84 | `cfg(target_has_atomic)` gates. They will be added once they get stabilized. 85 | 86 | 87 | ## 0.1.0 - 2019-07-24 88 | ### Added 89 | - Everything. 90 | 91 | 92 | [Unreleased]: https://github.com/LukasKalbertodt/atomig/compare/v0.4.3...HEAD 93 | [0.4.3]: https://github.com/LukasKalbertodt/atomig/compare/v0.4.2...v0.4.3 94 | [0.4.2]: https://github.com/LukasKalbertodt/atomig/compare/v0.4.1...v0.4.2 95 | [0.4.1]: https://github.com/LukasKalbertodt/atomig/compare/v0.4.0...v0.4.1 96 | [0.4.0]: https://github.com/LukasKalbertodt/atomig/compare/v0.3.3...v0.4.0 97 | [0.3.3]: https://github.com/LukasKalbertodt/atomig/compare/v0.3.2...v0.3.3 98 | [0.3.2]: https://github.com/LukasKalbertodt/atomig/compare/v0.3.1...v0.3.2 99 | [0.3.1]: https://github.com/LukasKalbertodt/atomig/compare/v0.3.0...v0.3.1 100 | [0.3.0]: https://github.com/LukasKalbertodt/atomig/compare/v0.2.0...v0.3.0 101 | [0.2.0]: https://github.com/LukasKalbertodt/atomig/compare/v0.1.0...v0.2.0 102 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atomig" 3 | version = "0.4.3" 4 | authors = ["Lukas Kalbertodt "] 5 | edition = "2021" 6 | 7 | description = """ 8 | Generic, convenient and lock-free `std` atomics via `Atomic`. Can be used 9 | with many primitive types (including floats) and with custom types. 10 | """ 11 | documentation = "https://docs.rs/atomig/" 12 | repository = "https://github.com/LukasKalbertodt/atomig/" 13 | readme = "README.md" 14 | license = "MIT/Apache-2.0" 15 | 16 | keywords = ["atomic", "atom", "ordering", "cas", "generic"] 17 | categories = ["concurrency", "hardware-support", "no-std"] 18 | 19 | 20 | [features] 21 | derive = ["atomig-macro"] 22 | 23 | [dependencies] 24 | atomig-macro = { version = "=0.4.0", path = "atomig-macro", optional = true } 25 | serde = { version = "1", default-features = false, optional = true } 26 | 27 | [dev-dependencies] 28 | bincode = { version = "1" } 29 | 30 | [package.metadata.docs.rs] 31 | all-features = true 32 | 33 | 34 | [[example]] 35 | name = "custom_type" 36 | required-features = ["derive"] 37 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Atomig: generic and convenient `std` atomics 2 | ============================================ 3 | 4 | [CI status of master](https://github.com/LukasKalbertodt/atomig/actions/workflows/ci.yml) 5 | [Crates.io Version](https://crates.io/crates/atomig) 6 | [docs.rs](https://docs.rs/atomig) 7 | 8 | Offers `Atomic` that can be used with primitive and custom types. 9 | *However*, it only works with types that can actually use atomic operations: a lock-based fallback for other types is not used! 10 | This crate is based on `std`'s atomics and therefore does not contain any `unsafe` code! 11 | This crate also does not have any dependencies by default. 12 | If you enable the `serde` feature, then this crate will depend on `serde` and `Serialize` / `Deserialize` will be 13 | implemented for `Atomic` when appropriate, using sequentially-consistent ordering. 14 | 15 | Simple example with primitive types: 16 | 17 | ```rust 18 | use atomig::{Atomic, Ordering}; 19 | 20 | let x = Atomic::new(27); // `Atomic` 21 | x.store(39, Ordering::SeqCst); 22 | ``` 23 | 24 | This works with almost all primitive types, including `f32`, `f64` and `char`, 25 | but also with types like `std::ptr::NonNull` and `std::num::NonZero`. 26 | 27 | You can automatically derive `Atom` for your own enum or struct types to use them in `Atomic`. 28 | There are some limitations, however. 29 | 30 | ```rust 31 | // Requires the 'derive' feature: 32 | // atomig = { version = "_", features = ["derive"] } 33 | use atomig::{Atom, Atomic, Ordering}; 34 | 35 | #[derive(Atom)] 36 | #[repr(u8)] 37 | enum Animal { Dog, Cat, Fox } 38 | 39 | let animal = Atomic::new(Animal::Cat); 40 | animal.store(Animal::Fox, Ordering::SeqCst); 41 | 42 | #[derive(Atom)] 43 | struct Port(u16); 44 | 45 | let port = Atomic::new(Port(80)); 46 | port.store(Port(8080), Ordering::SeqCst); 47 | ``` 48 | 49 | For more examples and information see **[the documentation](https://docs.rs/atomig)**. 50 | 51 |
52 | 53 | --- 54 | 55 | ## License 56 | 57 | Licensed under either of Apache License, Version 58 | 2.0 or MIT license at your option. 59 | Unless you explicitly state otherwise, any contribution intentionally submitted 60 | for inclusion in this project by you, as defined in the Apache-2.0 license, 61 | shall be dual licensed as above, without any additional terms or conditions. 62 | -------------------------------------------------------------------------------- /atomig-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atomig-macro" 3 | version = "0.4.0" 4 | authors = ["Lukas Kalbertodt "] 5 | edition = "2021" 6 | 7 | description = """ 8 | Helper crate for `atomig`. Do not use directly, but only through main crate. 9 | This helper does not follow semantic versioning! 10 | """ 11 | repository = "https://github.com/LukasKalbertodt/atomig/" 12 | license = "MIT/Apache-2.0" 13 | 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | syn = "2" 20 | quote = "1" 21 | proc-macro2 = "1" 22 | -------------------------------------------------------------------------------- /atomig-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; 5 | use quote::quote; 6 | use syn::{ 7 | parse_macro_input, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, 8 | }; 9 | 10 | 11 | /// Custom derive for the `Atom` trait. Please see the trait's documentation 12 | /// for more information on this derive. 13 | #[proc_macro_derive(Atom)] 14 | pub fn derive_atom(input: TokenStream) -> TokenStream { 15 | let input = parse_macro_input!(input as DeriveInput); 16 | gen_atom_impl(&input) 17 | .unwrap_or_else(|e| e.to_compile_error()) 18 | .into() 19 | } 20 | 21 | /// Custom derive for the `AtomLogic` trait. Please see the trait's 22 | /// documentation for more information on this derive. 23 | #[proc_macro_derive(AtomLogic)] 24 | pub fn derive_atom_logic(input: TokenStream) -> TokenStream { 25 | let input = parse_macro_input!(input as DeriveInput); 26 | gen_marker_trait_impl("AtomLogic", &input) 27 | .unwrap_or_else(|e| e.to_compile_error()) 28 | .into() 29 | } 30 | 31 | /// Custom derive for the `AtomInteger` trait. Please see the trait's 32 | /// documentation for more information on this derive. 33 | #[proc_macro_derive(AtomInteger)] 34 | pub fn derive_atom_integer(input: TokenStream) -> TokenStream { 35 | let input = parse_macro_input!(input as DeriveInput); 36 | gen_marker_trait_impl("AtomInteger", &input) 37 | .unwrap_or_else(|e| e.to_compile_error()) 38 | .into() 39 | } 40 | 41 | fn gen_marker_trait_impl(trait_name: &str, input: &DeriveInput) -> Result { 42 | match input.data { 43 | Data::Struct(_) => { 44 | let type_name = &input.ident; 45 | let trait_name = Ident::new(trait_name, Span::call_site()); 46 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 47 | Ok(quote! { 48 | impl #impl_generics atomig::#trait_name 49 | for #type_name #ty_generics #where_clause {} 50 | }) 51 | } 52 | Data::Enum(_) => { 53 | let msg = format!( 54 | "`{}` cannot be derived for enums as this is almost always incorrect to do. \ 55 | Please read the documentation of `{}` carefully. If you still think you \ 56 | want to implement this trait, you have to do it manually.", 57 | trait_name, 58 | trait_name, 59 | ); 60 | Err(Error::new(Span::call_site(), msg)) 61 | } 62 | Data::Union(_) => { 63 | let msg = format!("`{}` cannot be derived for unions", trait_name); 64 | Err(Error::new(Span::call_site(), msg)) 65 | } 66 | } 67 | } 68 | 69 | /// The actual implementation for `derive(Atom)`. 70 | fn gen_atom_impl(input: &DeriveInput) -> Result { 71 | // Generate the body of the impl block. 72 | let impl_body = match &input.data { 73 | Data::Struct(s) => atom_impl_for_struct(s), 74 | Data::Enum(e) => atom_impl_for_enum(input, e), 75 | Data::Union(_) => Err(Error::new(Span::call_site(), "unions cannot derive `Atom`")), 76 | }?; 77 | 78 | // Combine everything into a finshed impl block. 79 | let type_name = &input.ident; 80 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 81 | Ok(quote! { 82 | impl #impl_generics atomig::Atom for #type_name #ty_generics #where_clause { 83 | #impl_body 84 | } 85 | }) 86 | } 87 | 88 | /// Generates the body of the `impl Atom` block for the given struct definition. 89 | fn atom_impl_for_struct(s: &DataStruct) -> Result { 90 | let mut it = s.fields.iter(); 91 | 92 | // Get first field 93 | let field = it.next().ok_or_else(|| { 94 | let msg = "struct has no fields, but `derive(Atom)` works only for \ 95 | structs with exactly one field"; 96 | Error::new(s.fields.span(), msg) 97 | })?; 98 | 99 | // Make sure there are no other fields 100 | if it.next().is_some() { 101 | let msg = "struct has more than one field, but `derive(Atom)` works only for \ 102 | structs with exactly one field"; 103 | return Err(Error::new(s.fields.span(), msg)); 104 | } 105 | 106 | // Generate the code for `pack` and `unpack` which depends on whether it is 107 | // a named or tuple-struct field. 108 | let (field_access, struct_init) = match &field.ident { 109 | Some(name) => (quote! { self.#name }, quote! { Self { #name: src } }), 110 | None => (quote! { self.0 }, quote!{ Self(src) }), 111 | }; 112 | 113 | let field_type = &field.ty; 114 | Ok(quote! { 115 | type Repr = <#field_type as atomig::Atom>::Repr; 116 | 117 | fn pack(self) -> Self::Repr { 118 | <#field_type as atomig::Atom>::pack(#field_access) 119 | } 120 | fn unpack(src: Self::Repr) -> Self { 121 | let src = <#field_type as atomig::Atom>::unpack(src); 122 | #struct_init 123 | } 124 | }) 125 | } 126 | 127 | /// Generates the body of the `impl Atom` block for the given enum definition. 128 | fn atom_impl_for_enum(input: &DeriveInput, e: &DataEnum) -> Result { 129 | const INTEGER_NAMES: &[&str] = &[ 130 | "u8", "u16", "u32", "u64", "u128", "usize", 131 | "i8", "i16", "i32", "i64", "i128", "isize", 132 | ]; 133 | 134 | let mut repr_type = None; 135 | for attr in &input.attrs { 136 | // Make sure we have a `repr` attribute on the enum. 137 | if attr.path().is_ident("repr") { 138 | // Make sure the `repr` attribute has the correct syntax and actually 139 | // specifies the primitive representation. 140 | attr.parse_nested_meta(|meta| { 141 | repr_type = Some( 142 | meta.path 143 | .get_ident() 144 | .filter(|ident| INTEGER_NAMES.iter().any(|int| ident == int)) 145 | .ok_or_else(|| { 146 | let msg = "`repr(_)` attribute does not specify the primitive \ 147 | representation (a primitive integer), but this is required \ 148 | for `derive(Atom)`"; 149 | Error::new(meta.input.span(), msg) 150 | })? 151 | .clone(), 152 | ); 153 | 154 | Ok(()) 155 | })? 156 | } 157 | } 158 | 159 | let repr_type = repr_type.ok_or_else(|| { 160 | let msg = format!( 161 | "no `repr(_)` attribute on enum '{}', but such an attribute is \ 162 | required to automatically derive `Atom`", 163 | input.ident, 164 | ); 165 | Error::new(Span::call_site(), msg) 166 | })?; 167 | 168 | // Check that all variants have no fields. In other words: that the enum is 169 | // C-like. 170 | let variant_with_fields = e.variants.iter().find(|variant| { 171 | match variant.fields { 172 | Fields::Unit => false, 173 | _ => true, 174 | } 175 | }); 176 | if let Some(v) = variant_with_fields { 177 | let msg = "this variant has fields, but `derive(Atom)` only works \ 178 | for C-like enums"; 179 | return Err(Error::new(v.span(), msg)); 180 | } 181 | 182 | // Generate the code for `unpack` which is more complicated than the `pack` 183 | // code. For `pack` we can simply use the `as` cast, but for unpack we have 184 | // to assemble a list of `if` statements. If you would hand code such a 185 | // method, you would use a `match` statement. But we use 'ifs' so that we 186 | // don't have to check for the discriminant values ourselves. That might be 187 | // very hard. 188 | let type_name = &input.ident; 189 | let unpack_code = { 190 | let checks: Vec<_> = e.variants.iter().map(|variant| { 191 | let variant_name = &variant.ident; 192 | quote! { 193 | if src == #type_name::#variant_name as #repr_type { 194 | return #type_name::#variant_name; 195 | } 196 | } 197 | }).collect(); 198 | 199 | let error = format!( 200 | "invalid '{}' value '{{}}' for enum '{}' in `Atom::unpack`", 201 | repr_type, 202 | type_name, 203 | ); 204 | quote! { 205 | #(#checks)* 206 | panic!(#error, src); 207 | } 208 | }; 209 | 210 | 211 | Ok(quote! { 212 | type Repr = #repr_type; 213 | 214 | fn pack(self) -> Self::Repr { 215 | self as #repr_type 216 | } 217 | fn unpack(src: Self::Repr) -> Self { 218 | #unpack_code 219 | } 220 | }) 221 | } 222 | -------------------------------------------------------------------------------- /examples/custom_type.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how you can use `Atomic` with your own types by 2 | //! deriving `Atom`. 3 | 4 | use std::{ 5 | num::NonZeroU16, 6 | sync::Arc, 7 | thread, 8 | time::Duration, 9 | }; 10 | use atomig::{Atom, Atomic, AtomLogic, Ordering}; 11 | 12 | 13 | #[derive(Debug, PartialEq, Atom)] 14 | #[repr(u8)] 15 | enum Animal { 16 | Cat, 17 | Dog, 18 | Fox, 19 | } 20 | 21 | #[derive(Debug, PartialEq, Atom, AtomLogic)] 22 | struct BitSet(u16); 23 | 24 | // The inner type only has to implement `Atom`, not `PrimitiveAtom`. 25 | #[derive(Debug, PartialEq, Atom)] 26 | struct Port(NonZeroU16); 27 | 28 | fn main() { 29 | // Example with `Animal` 30 | let animal = Arc::new(Atomic::new(Animal::Cat)); 31 | { 32 | let animal = animal.clone(); 33 | thread::spawn(move || { 34 | while animal 35 | .compare_exchange(Animal::Dog, Animal::Fox, Ordering::SeqCst, Ordering::SeqCst) 36 | .is_err() 37 | { 38 | thread::sleep(Duration::from_millis(2)); 39 | } 40 | println!("Changed dog to fox!"); 41 | }); 42 | } 43 | thread::sleep(Duration::from_millis(10)); 44 | animal.store(Animal::Dog, Ordering::SeqCst); 45 | println!("Changed to dog!"); 46 | 47 | thread::sleep(Duration::from_millis(10)); 48 | println!("Final animal: {:?}", animal); 49 | 50 | 51 | // Example with `BitSet` 52 | let integer_set = Arc::new(Atomic::new(BitSet(0b11001100_11110000))); 53 | { 54 | let integer_set = integer_set.clone(); 55 | thread::spawn(move || { 56 | let mut current = 1; 57 | while integer_set.load(Ordering::SeqCst) != BitSet(0xFFFF) { 58 | integer_set.fetch_or(BitSet(current), Ordering::SeqCst); 59 | current <<= 1; 60 | } 61 | println!("Set all bits!"); 62 | }); 63 | } 64 | thread::sleep(Duration::from_millis(10)); 65 | println!("{:b}", integer_set.load(Ordering::SeqCst).0); 66 | } 67 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | //! This simple example shows how to use `Atomic` with primitive types (`u32` 2 | //! and `bool` in this case). 3 | 4 | use std::{ 5 | sync::Arc, 6 | thread, 7 | time::Duration, 8 | }; 9 | use atomig::{Atomic, Ordering}; 10 | 11 | 12 | fn main() { 13 | // `Atomic` 14 | let a = Atomic::new(3u32); 15 | a.store(27, Ordering::SeqCst); 16 | println!("{:?}", a); 17 | 18 | 19 | // `Atomic` 20 | let b = Arc::new(Atomic::new(false)); 21 | { 22 | let b = b.clone(); 23 | thread::spawn(move || { 24 | while b.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst).is_err() {} 25 | println!("Reset it to false!"); 26 | }); 27 | } 28 | thread::sleep(Duration::from_millis(10)); 29 | b.fetch_or(true, Ordering::SeqCst); 30 | 31 | thread::sleep(Duration::from_millis(2)); 32 | println!("{}", b.load(Ordering::SeqCst)); 33 | } 34 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | //! Traits for abstracting over `std` atomics. Mostly hidden implementation detail. 2 | //! 3 | //! Most items of these traits are hidden and not part of the public API of this library. 4 | //! You cannot implement these traits yourself. 5 | 6 | use core::{num::Wrapping, sync::atomic::{self, Ordering}}; 7 | use super::{Atom, AtomLogic, AtomInteger}; 8 | 9 | 10 | // =============================================================================================== 11 | // ===== All `Atomic*Impl` traits and `PrimitiveAtom` 12 | // =============================================================================================== 13 | 14 | mod sealed { 15 | /// You cannot implement this trait. That is the point. 16 | pub trait Sealed {} 17 | } 18 | 19 | /// Primitive types that can directly be used in an atomic way. 20 | /// 21 | /// This trait is implemented exactly for every type that has a corresponding 22 | /// atomic type in `std::sync::atomic`. You cannot implement this trait for 23 | /// your own types; see [`Atom`] instead. This trait's items are not part of 24 | /// the public API -- see the module docs. 25 | pub trait PrimitiveAtom: Sized + Copy + sealed::Sealed { 26 | /// The standard library type that is the atomic version of `Self`. 27 | type Impl: Send + Sync + Unpin; 28 | 29 | #[doc(hidden)] 30 | fn into_impl(self) -> Self::Impl; 31 | #[doc(hidden)] 32 | fn from_impl(imp: Self::Impl) -> Self; 33 | 34 | #[doc(hidden)] 35 | fn get_mut(imp: &mut Self::Impl) -> &mut Self; 36 | #[doc(hidden)] 37 | fn load(imp: &Self::Impl, order: Ordering) -> Self; 38 | #[doc(hidden)] 39 | fn store(imp: &Self::Impl, v: Self, order: Ordering); 40 | 41 | #[doc(hidden)] 42 | fn swap(imp: &Self::Impl, v: Self, order: Ordering) -> Self; 43 | 44 | #[doc(hidden)] 45 | fn compare_exchange( 46 | imp: &Self::Impl, 47 | current: Self, 48 | new: Self, 49 | success: Ordering, 50 | failure: Ordering, 51 | ) -> Result; 52 | 53 | #[doc(hidden)] 54 | fn compare_exchange_weak( 55 | imp: &Self::Impl, 56 | current: Self, 57 | new: Self, 58 | success: Ordering, 59 | failure: Ordering, 60 | ) -> Result; 61 | 62 | #[doc(hidden)] 63 | fn fetch_update( 64 | imp: &Self::Impl, 65 | set_order: Ordering, 66 | fetch_order: Ordering, 67 | f: F, 68 | ) -> Result 69 | where 70 | F: FnMut(Self) -> Option; 71 | } 72 | 73 | /// Atomic types from `std::sync::atomic` which support logical operations. 74 | /// 75 | /// You cannot implement this trait for your own types; see [`AtomLogic`] 76 | /// instead. This trait's items are not part of the public API -- see the 77 | /// module docs. 78 | pub trait PrimitiveAtomLogic: PrimitiveAtom { 79 | #[doc(hidden)] 80 | fn fetch_and(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 81 | #[doc(hidden)] 82 | fn fetch_nand(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 83 | #[doc(hidden)] 84 | fn fetch_or(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 85 | #[doc(hidden)] 86 | fn fetch_xor(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 87 | } 88 | 89 | /// Atomic types from `std::sync::atomic` which support integer operations. 90 | /// 91 | /// You cannot implement this trait for your own types; see [`AtomInteger`] 92 | /// instead. This trait's items are not part of the public API -- see the 93 | /// module docs. 94 | pub trait PrimitiveAtomInteger: PrimitiveAtom { 95 | #[doc(hidden)] 96 | fn fetch_add(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 97 | #[doc(hidden)] 98 | fn fetch_sub(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 99 | 100 | #[doc(hidden)] 101 | fn fetch_max(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 102 | #[doc(hidden)] 103 | fn fetch_min(imp: &Self::Impl, val: Self, order: Ordering) -> Self; 104 | } 105 | 106 | 107 | 108 | // =============================================================================================== 109 | // ===== Implementations for standard library types 110 | // =============================================================================================== 111 | 112 | /// Expands to the `pack` and `unpack` methods implemented as ID function. 113 | macro_rules! id_pack_unpack { 114 | () => { 115 | fn pack(self) -> Self::Repr { 116 | self 117 | } 118 | fn unpack(src: Self::Repr) -> Self { 119 | src 120 | } 121 | }; 122 | } 123 | 124 | /// Expands to all methods from `AtomicImpl`, each forwarding to 125 | /// `self.that_method`. 126 | macro_rules! pass_through_methods { 127 | ($ty:ty) => { 128 | #[inline(always)] 129 | fn into_impl(self) -> Self::Impl { 130 | <$ty>::new(self) 131 | } 132 | 133 | #[inline(always)] 134 | fn from_impl(imp: Self::Impl) -> Self { 135 | imp.into_inner() 136 | } 137 | 138 | #[inline(always)] 139 | fn get_mut(imp: &mut Self::Impl) -> &mut Self { 140 | imp.get_mut() 141 | } 142 | 143 | #[inline(always)] 144 | fn load(imp: &Self::Impl, order: Ordering) -> Self { 145 | imp.load(order) 146 | } 147 | 148 | #[inline(always)] 149 | fn store(imp: &Self::Impl, v: Self, order: Ordering) { 150 | imp.store(v, order) 151 | } 152 | 153 | #[inline(always)] 154 | fn swap(imp: &Self::Impl, v: Self, order: Ordering) -> Self { 155 | imp.swap(v, order) 156 | } 157 | 158 | #[inline(always)] 159 | fn compare_exchange( 160 | imp: &Self::Impl, 161 | current: Self, 162 | new: Self, 163 | success: Ordering, 164 | failure: Ordering, 165 | ) -> Result { 166 | imp.compare_exchange(current, new, success, failure) 167 | } 168 | 169 | #[inline(always)] 170 | fn compare_exchange_weak( 171 | imp: &Self::Impl, 172 | current: Self, 173 | new: Self, 174 | success: Ordering, 175 | failure: Ordering, 176 | ) -> Result { 177 | imp.compare_exchange_weak(current, new, success, failure) 178 | } 179 | 180 | fn fetch_update( 181 | imp: &Self::Impl, 182 | set_order: Ordering, 183 | fetch_order: Ordering, 184 | f: F, 185 | ) -> Result 186 | where 187 | F: FnMut(Self) -> Option 188 | { 189 | imp.fetch_update(set_order, fetch_order, f) 190 | } 191 | }; 192 | } 193 | 194 | /// Expands to all methods from `AtomicLogicImpl`, each forwarding to 195 | /// `self.that_method`. 196 | macro_rules! logical_pass_through_methods { 197 | () => { 198 | #[inline(always)] 199 | fn fetch_and(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 200 | imp.fetch_and(val, order) 201 | } 202 | 203 | #[inline(always)] 204 | fn fetch_nand(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 205 | imp.fetch_nand(val, order) 206 | } 207 | 208 | #[inline(always)] 209 | fn fetch_or(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 210 | imp.fetch_or(val, order) 211 | } 212 | 213 | #[inline(always)] 214 | fn fetch_xor(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 215 | imp.fetch_xor(val, order) 216 | } 217 | }; 218 | } 219 | 220 | /// Expands to all methods from `AtomicIntegerImpl`, each forwarding to 221 | /// `self.that_method`. 222 | macro_rules! integer_pass_through_methods { 223 | () => { 224 | #[inline(always)] 225 | fn fetch_add(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 226 | imp.fetch_add(val, order) 227 | } 228 | 229 | #[inline(always)] 230 | fn fetch_sub(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 231 | imp.fetch_sub(val, order) 232 | } 233 | 234 | fn fetch_max(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 235 | imp.fetch_max(val, order) 236 | } 237 | 238 | fn fetch_min(imp: &Self::Impl, val: Self, order: Ordering) -> Self { 239 | imp.fetch_min(val, order) 240 | } 241 | }; 242 | } 243 | 244 | // ----- `*mut T` and `AtomicPtr` ----- 245 | #[cfg(target_has_atomic = "ptr")] 246 | impl Atom for *mut T { 247 | type Repr = Self; 248 | id_pack_unpack!(); 249 | } 250 | 251 | #[cfg(target_has_atomic = "ptr")] 252 | impl sealed::Sealed for *mut T {} 253 | 254 | #[cfg(target_has_atomic = "ptr")] 255 | impl PrimitiveAtom for *mut T { 256 | type Impl = atomic::AtomicPtr; 257 | pass_through_methods!(atomic::AtomicPtr); 258 | } 259 | 260 | 261 | 262 | // ----- Integers and `bool` ----- 263 | 264 | macro_rules! impl_std_atomics { 265 | ($ty:ty, $non_zero_ty:ident, $impl_ty:ident, $is_int:ident) => { 266 | impl Atom for $ty { 267 | type Repr = Self; 268 | id_pack_unpack!(); 269 | } 270 | 271 | impl sealed::Sealed for $ty {} 272 | impl AtomLogic for $ty {} 273 | impl PrimitiveAtom for $ty { 274 | type Impl = atomic::$impl_ty; 275 | pass_through_methods!(atomic::$impl_ty); 276 | } 277 | 278 | impl PrimitiveAtomLogic for $ty { 279 | logical_pass_through_methods!(); 280 | } 281 | 282 | impl_std_atomics!(@int_methods $ty, $non_zero_ty, $impl_ty, $is_int); 283 | }; 284 | (@int_methods $ty:ty, $non_zero_ty:ident, $impl_ty:ident, true) => { 285 | impl AtomInteger for $ty {} 286 | impl PrimitiveAtomInteger for $ty { 287 | integer_pass_through_methods!(); 288 | } 289 | 290 | impl Atom for core::num::$non_zero_ty { 291 | type Repr = $ty; 292 | fn pack(self) -> Self::Repr { 293 | self.get() 294 | } 295 | 296 | fn unpack(src: Self::Repr) -> Self { 297 | // Since `AtomLogic` and `AtomInteger` is not implemented for 298 | // NonZero types, there is no way zero can be the result of any 299 | // atomic operation. Thus this should never happen. 300 | Self::new(src).expect("zero value in `Atom::unpack` for NonZero type") 301 | } 302 | } 303 | }; 304 | (@int_methods $ty:ty, $non_zero_ty:ident, $impl_ty:ident, false) => {}; 305 | } 306 | 307 | #[cfg(target_has_atomic = "8")] impl_std_atomics!(bool, _Dummy, AtomicBool, false); 308 | #[cfg(target_has_atomic = "8")] impl_std_atomics!(u8, NonZeroU8, AtomicU8, true); 309 | #[cfg(target_has_atomic = "8")] impl_std_atomics!(i8, NonZeroI8, AtomicI8, true); 310 | #[cfg(target_has_atomic = "16")] impl_std_atomics!(u16, NonZeroU16, AtomicU16, true); 311 | #[cfg(target_has_atomic = "16")] impl_std_atomics!(i16, NonZeroI16, AtomicI16, true); 312 | #[cfg(target_has_atomic = "32")] impl_std_atomics!(u32, NonZeroU32, AtomicU32, true); 313 | #[cfg(target_has_atomic = "32")] impl_std_atomics!(i32, NonZeroI32, AtomicI32, true); 314 | #[cfg(target_has_atomic = "64")] impl_std_atomics!(u64, NonZeroU64, AtomicU64, true); 315 | #[cfg(target_has_atomic = "64")] impl_std_atomics!(i64, NonZeroI64, AtomicI64, true); 316 | #[cfg(target_has_atomic = "ptr")] impl_std_atomics!(usize, NonZeroUsize, AtomicUsize, true); 317 | #[cfg(target_has_atomic = "ptr")] impl_std_atomics!(isize, NonZeroIsize, AtomicIsize, true); 318 | 319 | // ----- Implementations for non-atomic primitive types ------------------------------------------ 320 | #[cfg(target_has_atomic = "32")] 321 | impl Atom for f32 { 322 | type Repr = u32; 323 | fn pack(self) -> Self::Repr { 324 | self.to_bits() 325 | } 326 | fn unpack(src: Self::Repr) -> Self { 327 | Self::from_bits(src) 328 | } 329 | } 330 | 331 | #[cfg(target_has_atomic = "64")] 332 | impl Atom for f64 { 333 | type Repr = u64; 334 | fn pack(self) -> Self::Repr { 335 | self.to_bits() 336 | } 337 | fn unpack(src: Self::Repr) -> Self { 338 | Self::from_bits(src) 339 | } 340 | } 341 | 342 | #[cfg(target_has_atomic = "32")] 343 | impl Atom for char { 344 | type Repr = u32; 345 | fn pack(self) -> Self::Repr { 346 | self.into() 347 | } 348 | fn unpack(src: Self::Repr) -> Self { 349 | Self::try_from(src).expect("invalid value in ::unpack") 350 | } 351 | } 352 | 353 | // We do not implement `AtomInteger` as, to me, it seems like the exact adding 354 | // and subtraction behavior of integer atomics is not defined anywhere. 355 | impl Atom for Wrapping { 356 | type Repr = T::Repr; 357 | fn pack(self) -> Self::Repr { 358 | self.0.pack() 359 | } 360 | fn unpack(src: Self::Repr) -> Self { 361 | Self(T::unpack(src)) 362 | } 363 | } 364 | impl AtomLogic for Wrapping where T::Repr: PrimitiveAtomLogic {} 365 | 366 | 367 | #[cfg(target_has_atomic = "ptr")] 368 | impl Atom for core::ptr::NonNull { 369 | type Repr = *mut T; 370 | fn pack(self) -> Self::Repr { 371 | self.as_ptr().pack() 372 | } 373 | fn unpack(src: Self::Repr) -> Self { 374 | Self::new(<*mut T>::unpack(src)) 375 | .expect("null value in ` as Atom>::unpack`") 376 | } 377 | } 378 | 379 | #[cfg(target_has_atomic = "ptr")] 380 | impl Atom for Option> { 381 | type Repr = *mut T; 382 | fn pack(self) -> Self::Repr { 383 | self.map(|nn| nn.as_ptr()) 384 | .unwrap_or(core::ptr::null_mut()) 385 | .pack() 386 | } 387 | fn unpack(src: Self::Repr) -> Self { 388 | if src.is_null() { 389 | None 390 | } else { 391 | Some(core::ptr::NonNull::new(<*mut T>::unpack(src)).unwrap()) 392 | } 393 | } 394 | } 395 | 396 | macro_rules! impl_option_non_zero { 397 | ($ty:ident = $repr:ty) => { 398 | impl Atom for Option { 399 | type Repr = $repr; 400 | fn pack(self) -> Self::Repr { 401 | self.map(core::num::$ty::get).unwrap_or(0).pack() 402 | } 403 | fn unpack(src: Self::Repr) -> Self { 404 | core::num::$ty::new(src) 405 | } 406 | } 407 | 408 | // Semantically, an `Option` represents `Foo` exactly. It 409 | // also has the exact same memory layout. It's just that we assign 410 | // the "symbol" `None` to 0. Any integer operation that leads to 0 on 411 | // the underlying type will result in `None`. 412 | impl AtomInteger for Option {} 413 | }; 414 | } 415 | 416 | #[cfg(target_has_atomic = "8")] impl_option_non_zero!(NonZeroU8 = u8); 417 | #[cfg(target_has_atomic = "8")] impl_option_non_zero!(NonZeroI8 = i8); 418 | #[cfg(target_has_atomic = "16")] impl_option_non_zero!(NonZeroU16 = u16); 419 | #[cfg(target_has_atomic = "16")] impl_option_non_zero!(NonZeroI16 = i16); 420 | #[cfg(target_has_atomic = "32")] impl_option_non_zero!(NonZeroU32 = u32); 421 | #[cfg(target_has_atomic = "32")] impl_option_non_zero!(NonZeroI32 = i32); 422 | #[cfg(target_has_atomic = "64")] impl_option_non_zero!(NonZeroU64 = u64); 423 | #[cfg(target_has_atomic = "64")] impl_option_non_zero!(NonZeroI64 = i64); 424 | #[cfg(target_has_atomic = "ptr")] impl_option_non_zero!(NonZeroUsize = usize); 425 | #[cfg(target_has_atomic = "ptr")] impl_option_non_zero!(NonZeroIsize = isize); 426 | 427 | macro_rules! impl_int8_arrays { 428 | ($elem:ident, $len:literal, $repr:ident) => { 429 | impl Atom for [$elem; $len] { 430 | type Repr = $repr; 431 | fn pack(self) -> Self::Repr { 432 | $repr::from_ne_bytes(self.map(|e| e as _)) 433 | } 434 | fn unpack(src: Self::Repr) -> Self { 435 | src.to_ne_bytes().map(|e| e as _) 436 | } 437 | } 438 | impl AtomLogic for [$elem; $len] {} 439 | }; 440 | } 441 | 442 | #[cfg(target_has_atomic = "16")] impl_int8_arrays!(u8, 2, u16); 443 | #[cfg(target_has_atomic = "32")] impl_int8_arrays!(u8, 4, u32); 444 | #[cfg(target_has_atomic = "64")] impl_int8_arrays!(u8, 8, u64); 445 | #[cfg(target_has_atomic = "16")] impl_int8_arrays!(i8, 2, u16); 446 | #[cfg(target_has_atomic = "32")] impl_int8_arrays!(i8, 4, u32); 447 | #[cfg(target_has_atomic = "64")] impl_int8_arrays!(i8, 8, u64); 448 | 449 | macro_rules! impl_int_arrays { 450 | ($unsigned_elem:ident, $signed_elem:ident, $len:literal, $repr:ident, $nested:tt, $flat:tt) => { 451 | impl_small_primitive_array!($unsigned_elem, $len, $repr, $nested, $flat); 452 | impl_small_primitive_array!($signed_elem, $len, $repr, $nested, $flat); 453 | }; 454 | } 455 | 456 | macro_rules! impl_small_primitive_array { 457 | ($elem:ident, $len:literal, $repr:ident, $nested:tt, $flat:tt) => { 458 | impl Atom for [$elem; $len] { 459 | type Repr = $repr; 460 | fn pack(self) -> Self::Repr { 461 | let $nested = self.map(|x| x.to_ne_bytes()); 462 | Self::Repr::from_ne_bytes($flat) 463 | } 464 | fn unpack(src: Self::Repr) -> Self { 465 | let $flat = src.to_ne_bytes(); 466 | $nested.map($elem::from_ne_bytes) 467 | } 468 | } 469 | impl AtomLogic for [$elem; $len] {} 470 | }; 471 | } 472 | #[cfg(target_has_atomic = "32")] impl_int_arrays!(u16, i16, 2, u32, 473 | [[b0, b1], [b2, b3]], [b0, b1, b2, b3]); 474 | #[cfg(target_has_atomic = "64")] impl_int_arrays!(u16, i16, 4, u64, 475 | [[b0, b1], [b2, b3], [b4, b5], [b6, b7]], [b0, b1, b2, b3, b4, b5, b6, b7]); 476 | #[cfg(target_has_atomic = "64")] impl_int_arrays!(u32, i32, 2, u64, 477 | [[b0, b1, b2, b3], [b4, b5, b6, b7]], [b0, b1, b2, b3, b4, b5, b6, b7]); 478 | 479 | /// This is just a dummy module to have doc tests. 480 | /// 481 | /// ``` 482 | /// use atomig::{Atom, AtomLogic, AtomInteger, impls::{PrimitiveAtomLogic, PrimitiveAtomInteger}}; 483 | /// 484 | /// fn assert_impl_atom() {} 485 | /// fn assert_impl_atom_logic() 486 | /// where 487 | /// T::Repr: PrimitiveAtomLogic, 488 | /// {} 489 | /// fn assert_impl_atom_all() 490 | /// where 491 | /// T::Repr: PrimitiveAtomInteger + PrimitiveAtomLogic, 492 | /// {} 493 | /// 494 | /// assert_impl_atom_all::(); 495 | /// assert_impl_atom_all::(); 496 | /// assert_impl_atom_all::(); 497 | /// assert_impl_atom_all::(); 498 | /// assert_impl_atom_all::(); 499 | /// assert_impl_atom_all::(); 500 | /// assert_impl_atom_all::(); 501 | /// assert_impl_atom_all::(); 502 | /// assert_impl_atom_all::(); 503 | /// assert_impl_atom_all::(); 504 | /// 505 | /// assert_impl_atom_logic::(); 506 | /// 507 | /// assert_impl_atom::<*mut ()>(); 508 | /// assert_impl_atom::<*mut String>(); 509 | /// assert_impl_atom::>(); 510 | /// assert_impl_atom::>(); 511 | /// assert_impl_atom::>>(); 512 | /// assert_impl_atom::>>(); 513 | /// 514 | /// assert_impl_atom::(); 515 | /// assert_impl_atom::(); 516 | /// assert_impl_atom::(); 517 | /// 518 | /// assert_impl_atom::<[u8; 2]>(); 519 | /// assert_impl_atom::<[u8; 4]>(); 520 | /// assert_impl_atom::<[u8; 8]>(); 521 | /// assert_impl_atom::<[i8; 2]>(); 522 | /// assert_impl_atom::<[i8; 4]>(); 523 | /// assert_impl_atom::<[i8; 8]>(); 524 | /// assert_impl_atom::<[u16; 2]>(); 525 | /// assert_impl_atom::<[u16; 4]>(); 526 | /// assert_impl_atom::<[i16; 2]>(); 527 | /// assert_impl_atom::<[i16; 4]>(); 528 | /// assert_impl_atom::<[u32; 2]>(); 529 | /// assert_impl_atom::<[i32; 2]>(); 530 | /// 531 | /// assert_impl_atom::(); 532 | /// assert_impl_atom::(); 533 | /// assert_impl_atom::(); 534 | /// assert_impl_atom::(); 535 | /// assert_impl_atom::(); 536 | /// assert_impl_atom::(); 537 | /// assert_impl_atom::(); 538 | /// assert_impl_atom::(); 539 | /// assert_impl_atom::(); 540 | /// assert_impl_atom::(); 541 | /// 542 | /// assert_impl_atom_logic::>(); 543 | /// assert_impl_atom_logic::>(); 544 | /// assert_impl_atom_logic::>(); 545 | /// assert_impl_atom_logic::>(); 546 | /// assert_impl_atom_logic::>(); 547 | /// assert_impl_atom_logic::>(); 548 | /// assert_impl_atom_logic::>(); 549 | /// assert_impl_atom_logic::>(); 550 | /// assert_impl_atom_logic::>(); 551 | /// assert_impl_atom_logic::>(); 552 | /// ``` 553 | mod tests {} 554 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Generic and convenient `std` atomics. 2 | //! 3 | //! This crate offers the generic [`Atomic`][Atomic] type which can perform 4 | //! atomic operations on `T`. There is an important difference to C++'s 5 | //! `atomic` and the `Atomic` type from the `atomic` crate: **the 6 | //! `Atomic` in this crate only works with types that actually support 7 | //! atomic operations on the target platform**. A lock-based fallback for other 8 | //! types is not used! 9 | //! 10 | //! This crate uses the atomic types from `std::sync::atomic` under the hood 11 | //! and actually does not contain any "interesting" runtime code itself. In 12 | //! other words: this is just a nicer API. Thanks to this, this crate does not 13 | //! use any `unsafe` code! 14 | //! 15 | //! 16 | //! # Quick example 17 | //! 18 | //! You can simply use `Atomic` with all types that implement [`Atom`] which 19 | //! are all types that support atomic operations on your platform, but also 20 | //! types that can be represented as the former kind of types (like `f32` and 21 | //! `char`). 22 | //! 23 | //! ``` 24 | //! use atomig::{Atomic, Ordering}; 25 | //! 26 | //! let a = Atomic::new(true); // Atomic 27 | //! a.store(false, Ordering::SeqCst); 28 | //! ``` 29 | //! 30 | //! The interface of [`Atomic`] very closely matches the interface of the 31 | //! atomic types in `std::sync::atomic` and you should be able to use this 32 | //! crate as a drop-in replacement. For more examples, see the `examples/` 33 | //! folder in the repository. 34 | //! 35 | //! As you can see in the example, `Ordering` (from `std::sync::atomic`) is 36 | //! reexported in this crate for your import convenience. 37 | //! 38 | //! 39 | //! # Traits 40 | //! 41 | //! This crate contains three main traits to safely abstract over different 42 | //! atomic types. There are also three traits for plumbing in the `impls` 43 | //! module but these are considered implementation detail for the most part and 44 | //! can usually be ignored. 45 | //! 46 | //! The most important trait is probably [`Atom`]: to use a type `T` in 47 | //! `Atomic`, is has to implement [`Atom`]. You can implement that trait for 48 | //! your own types as long as they can be represented by a type that implements 49 | //! [`impls::PrimitiveAtom`]. In many cases, you can also simply 50 | //! `#[derive(Atom)]` for your own types. See [`Atom`]'s documentation for more 51 | //! information. 52 | //! 53 | //! 54 | //! # Notes 55 | //! 56 | //! Not all types and modules from `std::sync::atomic` are available on all 57 | //! platforms. `std` itself uses `cfg(target_has_atomic)` attributes to do 58 | //! that. Unfortunately, these `cfg` attributes are still unstable, thus 59 | //! `atomig` does not use them right now. This has the unfortunate effect that 60 | //! this whole library will fail to compile on any targets where any of the 61 | //! atomic methods/types are unavailable. Once this is stabilized, `atomic` 62 | //! will use those `cfg` attributes as appropriate to support platforms with 63 | //! partial atomic support. 64 | //! 65 | //! # Cargo features 66 | //! 67 | //! This crate has some Cargo features which are disabled by default: 68 | //! - **`derive`**: enables the custom derives for [`Atom`], [`AtomLogic`] and 69 | //! [`AtomInteger`]. It is disabled by default because it requires compiling 70 | //! a few dependencies for procedural macros. 71 | //! - **`serde`**: enables the serde `Serialize` and `Deserialize` traits on 72 | //! `Atomic` if `T` is serializable or deserializable. 73 | //! 74 | 75 | #![forbid(unsafe_code)] 76 | #![no_std] 77 | 78 | #[cfg(test)] 79 | #[macro_use] 80 | extern crate std; 81 | 82 | use core::fmt; 83 | use crate::impls::{PrimitiveAtom, PrimitiveAtomLogic, PrimitiveAtomInteger}; 84 | 85 | pub mod impls; 86 | #[cfg(test)] 87 | mod tests; 88 | 89 | /// Reexported from `std` for import convenience. 90 | #[doc(no_inline)] 91 | pub use core::sync::atomic::Ordering; 92 | 93 | #[cfg(feature = "derive")] 94 | pub use atomig_macro::{Atom, AtomInteger, AtomLogic}; 95 | 96 | // =============================================================================================== 97 | // ===== User faced `Atom*` traits 98 | // =============================================================================================== 99 | 100 | /// Types that can be represented by a primitive type supporting atomic 101 | /// operations. 102 | /// 103 | /// This is trait is already implemented for all primitive types that support 104 | /// atomic operations. It is also implemented for `f32`, `f64` and `char` as 105 | /// all of those can be represented by a primitive atomic type. In addition to 106 | /// this, you can implement this trait for your own types as long as they can 107 | /// be represented as one such primitive type. 108 | /// 109 | /// The `pack` and `unpack` methods define the conversion to and from the 110 | /// atomic representation. The methods should be fairly fast, because they are 111 | /// called frequently by [`Atomic`]: at least once for every method of 112 | /// `Atomic`. 113 | /// 114 | /// 115 | /// # Example 116 | /// 117 | /// Imagine you have a `Port` type to represent a network port and use strong 118 | /// typing. It is simply a newtype around a `u16`, so it is easily possible to 119 | /// use atomic operations on it. 120 | /// 121 | /// ``` 122 | /// use atomig::{Atom, Atomic, Ordering}; 123 | /// 124 | /// struct Port(u16); 125 | /// 126 | /// impl Atom for Port { 127 | /// type Repr = u16; 128 | /// fn pack(self) -> Self::Repr { 129 | /// self.0 130 | /// } 131 | /// fn unpack(src: Self::Repr) -> Self { 132 | /// Port(src) 133 | /// } 134 | /// } 135 | /// 136 | /// // Implementing `Atom` means that we can use `Atomic` with our type 137 | /// let a = Atomic::new(Port(80)); 138 | /// a.store(Port(8080), Ordering::SeqCst); 139 | /// ``` 140 | /// 141 | /// 142 | /// # Deriving this trait 143 | /// 144 | /// Instead of implementing the trait manually (like shown above), you can 145 | /// derive it automatically in many cases. In order to use that feature, you 146 | /// have to enabled the Cargo feature 'derive'. 147 | /// 148 | /// ``` 149 | /// use atomig::{Atom, Atomic, Ordering}; 150 | /// # #[cfg(feature = "derive")] 151 | /// # fn main() { 152 | /// 153 | /// #[derive(Atom)] 154 | /// struct Port(u16); 155 | /// 156 | /// let a = Atomic::new(Port(80)); 157 | /// a.store(Port(8080), Ordering::SeqCst); 158 | /// # } 159 | /// 160 | /// # #[cfg(not(feature = "derive"))] 161 | /// # fn main() {} 162 | /// ``` 163 | /// 164 | /// The trait can be automatically derived for two kinds of types: 165 | /// - `struct` types with only *one* field. That field's type has to implement 166 | /// `Atom`. Works with tuple structs or normal structs with one named field. 167 | /// - `enum` types that have a `#[repr(_)]` attribute specified and are C-like 168 | /// (i.e. no variant has any fields). The primitive type specified in the 169 | /// `#[repr(_)]` attribute is used as `Repr` type. 170 | /// 171 | /// Example with enum: 172 | /// 173 | /// ``` 174 | /// use atomig::{Atom, Atomic, Ordering}; 175 | /// # #[cfg(feature = "derive")] 176 | /// # fn main() { 177 | /// 178 | /// #[derive(Atom)] 179 | /// #[repr(u8)] 180 | /// enum Animal { Dog, Cat, Fox } 181 | /// 182 | /// let a = Atomic::new(Animal::Cat); 183 | /// a.store(Animal::Fox, Ordering::SeqCst); 184 | /// # } 185 | /// 186 | /// # #[cfg(not(feature = "derive"))] 187 | /// # fn main() {} 188 | /// ``` 189 | pub trait Atom { 190 | /// The atomic representation of this type. 191 | /// 192 | /// In this trait's implementations for the primitive types themselves, 193 | /// `Repr` is set to `Self`. 194 | type Repr: PrimitiveAtom; 195 | 196 | /// Converts the type to its atomic representation. 197 | fn pack(self) -> Self::Repr; 198 | 199 | /// Creates an instance of this type from the atomic representation. 200 | /// 201 | /// This method is usually only called with values that were returned by 202 | /// `pack`. So in theory, you can assume that the argument is a valid 203 | /// representation. *However*, there are two exceptions. 204 | /// 205 | /// If your type also implements `AtomLogic` or `AtomInteger`, results of 206 | /// those operations might get passed to `unpack`. Furthermore, this method 207 | /// can be called by anyone. So at the very least, you have to make sure 208 | /// that invalid input values do not lead to undefined behavior (e.g. memory 209 | /// unsafety)! 210 | fn unpack(src: Self::Repr) -> Self; 211 | } 212 | 213 | /// `Atom`s for which logical operations on their atomic representation make 214 | /// sense. 215 | /// 216 | /// Implementing this marker trait for your type makes it possible to use 217 | /// [`Atomic::fetch_and`] and similar methods. Note that **the logical 218 | /// operation is performed on the atomic representation of your type and _not_ 219 | /// on your type directly**! 220 | /// 221 | /// Examples: 222 | /// - Imagine you have a `Set(u64)` type which represents an integer set for 223 | /// integers up to 63. The atomic representation is `u64` and the 224 | /// `pack`/`unpack` methods are implemented as you would expect. In this 225 | /// case, it makes sense to implement `AtomLogic` for `Set`: performing 226 | /// bit-wise logical operations on the `u64` representation makes sense. 227 | /// - Imagine you have `enum TriBool { Yes, Maybe, No }` which you represent by 228 | /// `u8`. The `pack`/`unpack` methods use `No = 0`, `Maybe = 1` and `Yes = 2` 229 | /// (or some other assignment). You also implement the logical operators from 230 | /// `std::ops` for `TriBool`. In this case, it is probably *very wrong* to 231 | /// implement `AtomLogic` for `TriBool`: the logical operations are performed 232 | /// bit-wise on the `u8` which will result in very strange results (maybe 233 | /// even in the value 3, which is not even valid). They will not use your 234 | /// `std::ops` implementations! 235 | /// 236 | /// 237 | /// # Deriving this trait 238 | /// 239 | /// Like [`Atom`], this trait can automatically derived if the 'derive' Cargo 240 | /// feature of this crate is enabled. This custom derive is simpler because 241 | /// this is only a marker trait. 242 | /// 243 | /// *However*, this trait cannot be derived for enums, as this is almost 244 | /// certainly incorrect. While in C, enums basically list some constants and 245 | /// often, these constants are used in bitwise logical operations, this is 246 | /// often not valid in Rust. In Rust, a C-like enum can only have one of the 247 | /// values listed in the definition and nothing else. Otherwise, UB is 248 | /// triggered. Implementing this trait incorrectly does not cause UB, but the 249 | /// `unpack` method will panic for unexpected values. If you still think you 250 | /// want to implement this trait for your type, you have to do it manually. 251 | pub trait AtomLogic: Atom 252 | where 253 | Self::Repr: PrimitiveAtomLogic, 254 | {} 255 | 256 | /// `Atom`s for which integer operations on their atomic representation make 257 | /// sense. 258 | /// 259 | /// Implementing this marker trait for your type makes it possible to use 260 | /// [`Atomic::fetch_add`] and similar methods. Note that **the integer 261 | /// operation is performed on the atomic representation of your type and _not_ 262 | /// on your type directly**! 263 | /// 264 | /// Examples: 265 | /// - The `Set` and `TriBool` examples from the [`AtomLogic`] documentation 266 | /// should both *not* implement `AtomInteger`, because addition on the 267 | /// underlying integer does not result in any meaningful value for them. 268 | /// - Imagine you have strong types for distance measurements, including 269 | /// `Meter(u64)`. It makes sense to implement `AtomInteger` for that type, 270 | /// because adding the representation (`u64`) makes sense. 271 | /// - As another example for types that should *not* implement this type, 272 | /// consider `f64`. It can be represented by `u64` and additions on `f64` do 273 | /// make sense. *But* add adding the `u64` representations of two `f64` does 274 | /// not yield any meaningful result! 275 | /// 276 | /// 277 | /// # Deriving this trait 278 | /// 279 | /// Like [`Atom`], this trait can automatically derived if the 'derive' Cargo 280 | /// feature of this crate is enabled. This custom derive is simpler because 281 | /// this is only a marker trait. 282 | /// 283 | /// *However*, this trait cannot be derived for enums, as this is almost 284 | /// certainly incorrect. While in C, enums basically list some constants and 285 | /// often, these constants are added or subtracted from one another, this is 286 | /// often not valid in Rust. In Rust, a C-like enum can only have one of the 287 | /// values listed in the definition and nothing else. Otherwise, UB is 288 | /// triggered. Implementing this trait incorrectly does not cause UB, but the 289 | /// `unpack` method will panic for unexpected values. If you still think you 290 | /// want to implement this trait for your type, you have to do it manually. 291 | pub trait AtomInteger: Atom 292 | where 293 | Self::Repr: PrimitiveAtomInteger, 294 | {} 295 | 296 | 297 | 298 | // =============================================================================================== 299 | // ===== The `Atomic` type 300 | // =============================================================================================== 301 | 302 | /// The main type of this library: a generic atomic type. 303 | /// 304 | /// Via the methods of this type you can perform various atomic operations. You 305 | /// can use any type `T` that implements [`Atom`]. This includes primitive 306 | /// atomics (the ones found in `std::sync::atomic`), other primitive types and 307 | /// potentially your own types. Additional methods are usable if `T` implements 308 | /// [`AtomLogic`] or [`AtomInteger`]. 309 | /// 310 | /// All methods use [`Atom::pack`] and [`Atom::unpack`] to convert between the 311 | /// underlying atomic value and the real value `T`. For types that implement 312 | /// [`PrimitiveAtom`][impls::PrimitiveAtom], these two methods are a simple ID 313 | /// function, meaning that there is no runtime overhead. Other types should 314 | /// make sure their `pack` and `unpack` operations are fast, as they are used a 315 | /// lot in this type. 316 | /// 317 | /// For all methods that do a comparison (e.g. `compare_exchange`), keep in 318 | /// mind that the comparison is performed on the bits of the underlying type 319 | /// which can sometimes lead to unexpected behavior. For example, for floats, 320 | /// there are many bit patterns that represent NaN. So the atomic might indeed 321 | /// store a NaN representation at a moment, but `compare_exchange` called with 322 | /// `current = NaN` might not swap, because both NaN differ in the bit 323 | /// representation. 324 | /// 325 | /// The interface of this type very closely matches the interface of the atomic 326 | /// types in `std::sync::atomic`. The documentation was copied (and slightly 327 | /// adjusted) from there. 328 | pub struct Atomic(<::Repr as PrimitiveAtom>::Impl); 329 | 330 | impl Atomic { 331 | /// Creates a new atomic value. 332 | /// 333 | /// # Examples 334 | /// 335 | /// ``` 336 | /// use atomig::Atomic; 337 | /// 338 | /// let x = Atomic::new(7u32); 339 | /// ``` 340 | pub fn new(v: T) -> Self { 341 | Self(T::Repr::into_impl(v.pack())) 342 | } 343 | 344 | /// Creates a new atomic value from the underlying `Atomic*` type from `std`. 345 | /// 346 | /// Since [`Atom`] is a `trait` and `const fn`s in `trait`s are not supported yet, 347 | /// the only way for this to be a `const fn` is 348 | /// to take the underyling atomic impl type directly. 349 | /// 350 | /// This allows `static` `Atomic`s to be created. 351 | /// 352 | /// # Examples 353 | /// 354 | /// ``` 355 | /// use atomig::Atomic; 356 | /// use std::sync::atomic::AtomicU32; 357 | /// 358 | /// static X: Atomic = Atomic::from_impl(AtomicU32::new(7)); 359 | /// ``` 360 | pub const fn from_impl(v: <::Repr as PrimitiveAtom>::Impl) -> Self { 361 | Self(v) 362 | } 363 | 364 | /// Consumes the atomic and returns the contained value. 365 | /// 366 | /// This is safe because passing `self` by value guarantees that no other 367 | /// threads are concurrently accessing the atomic data. 368 | pub fn into_inner(self) -> T { 369 | T::unpack(T::Repr::from_impl(self.0)) 370 | } 371 | 372 | /// Loads the value from the atomic. 373 | /// 374 | /// `load` takes an [`Ordering`] argument which describes the memory 375 | /// ordering of this operation. Possible values are `SeqCst`, `Acquire` and 376 | /// `Relaxed`. 377 | /// 378 | /// # Panics 379 | /// 380 | /// Panics if `order` is `Release` or `AcqRel`. 381 | /// 382 | /// # Examples 383 | /// 384 | /// ``` 385 | /// use atomig::{Atomic, Ordering}; 386 | /// 387 | /// let x = Atomic::new(5); 388 | /// assert_eq!(x.load(Ordering::SeqCst), 5); 389 | /// ``` 390 | pub fn load(&self, order: Ordering) -> T { 391 | T::unpack(T::Repr::load(&self.0, order)) 392 | } 393 | 394 | /// Stores a value into the atomic. 395 | /// 396 | /// `store` takes an [`Ordering`] argument which describes the memory 397 | /// ordering of this operation. Possible values are `SeqCst`, `Release` and 398 | /// `Relaxed`. 399 | /// 400 | /// # Panics 401 | /// 402 | /// Panics if `order` is `Acquire` or `AcqRel`. 403 | /// 404 | /// # Examples 405 | /// 406 | /// ``` 407 | /// use atomig::{Atomic, Ordering}; 408 | /// 409 | /// let x = Atomic::new(5); 410 | /// 411 | /// x.store(10, Ordering::SeqCst); 412 | /// assert_eq!(x.load(Ordering::SeqCst), 10); 413 | /// ``` 414 | pub fn store(&self, v: T, order: Ordering) { 415 | T::Repr::store(&self.0, v.pack(), order); 416 | } 417 | 418 | /// Stores a value into the atomic, returning the previous value. 419 | /// 420 | /// `swap` takes an [`Ordering`] argument which describes the memory 421 | /// ordering of this operation. All ordering modes are possible. Note that 422 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 423 | /// using `Release` makes the load part `Relaxed`. 424 | /// 425 | /// # Examples 426 | /// 427 | /// ``` 428 | /// use atomig::{Atomic, Ordering}; 429 | /// 430 | /// let x = Atomic::new(5); 431 | /// assert_eq!(x.swap(10, Ordering::SeqCst), 5); 432 | /// ``` 433 | pub fn swap(&self, v: T, order: Ordering) -> T { 434 | T::unpack(T::Repr::swap(&self.0, v.pack(), order)) 435 | } 436 | 437 | /// Stores a value into the atomic if the current value is the same as the 438 | /// `current` value. 439 | /// 440 | /// The return value is a result indicating whether the new value was 441 | /// written and containing the previous value. On success this value is 442 | /// guaranteed to be equal to `current`. 443 | /// 444 | /// `compare_exchange` takes two [`Ordering`] arguments to describe the 445 | /// memory ordering of this operation. The first describes the required 446 | /// ordering if the operation succeeds while the second describes the 447 | /// required ordering when the operation fails. Using `Acquire` as success 448 | /// ordering makes the store part of this operation `Relaxed`, and using 449 | /// `Release` makes the successful load `Relaxed`. The failure ordering can 450 | /// only be `SeqCst`, `Acquire` or `Relaxed` and must be equivalent to or 451 | /// weaker than the success ordering. 452 | /// 453 | /// # Examples 454 | /// 455 | /// ``` 456 | /// use atomig::{Atomic, Ordering}; 457 | /// 458 | /// let x = Atomic::new(5); 459 | /// 460 | /// assert_eq!( 461 | /// x.compare_exchange(5, 10, Ordering::Acquire, Ordering::Relaxed), 462 | /// Ok(5), 463 | /// ); 464 | /// assert_eq!(x.load(Ordering::Relaxed), 10); 465 | /// 466 | /// assert_eq!( 467 | /// x.compare_exchange(6, 12, Ordering::SeqCst, Ordering::Acquire), 468 | /// Err(10), 469 | /// ); 470 | /// assert_eq!(x.load(Ordering::Relaxed), 10); 471 | /// ``` 472 | pub fn compare_exchange( 473 | &self, 474 | current: T, 475 | new: T, 476 | success: Ordering, 477 | failure: Ordering, 478 | ) -> Result { 479 | T::Repr::compare_exchange(&self.0, current.pack(), new.pack(), success, failure) 480 | .map(T::unpack) 481 | .map_err(T::unpack) 482 | } 483 | 484 | /// Stores a value into the atomic if the current value is the same as the 485 | /// `current` value. 486 | /// 487 | /// Unlike `compare_exchange`, this function is allowed to spuriously fail 488 | /// even when the comparison succeeds, which can result in more efficient 489 | /// code on some platforms. The return value is a result indicating whether 490 | /// the new value was written and containing the previous value. 491 | /// 492 | /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe 493 | /// the memory ordering of this operation. The first describes the required 494 | /// ordering if the operation succeeds while the second describes the 495 | /// required ordering when the operation fails. Using `Acquire` as success 496 | /// ordering makes the store part of this operation `Relaxed`, and using 497 | /// `Release` makes the successful load `Relaxed`. The failure ordering can 498 | /// only be `SeqCst`, `Acquire` or `Relaxed` and must be equivalent to or 499 | /// weaker than the success ordering. 500 | /// 501 | /// # Examples 502 | /// 503 | /// ``` 504 | /// use atomig::{Atomic, Ordering}; 505 | /// 506 | /// let x = Atomic::new(4); 507 | /// 508 | /// let mut old = x.load(Ordering::Relaxed); 509 | /// loop { 510 | /// let new = old * 2; 511 | /// match x.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { 512 | /// Ok(_) => break, 513 | /// Err(x) => old = x, 514 | /// } 515 | /// } 516 | /// ``` 517 | pub fn compare_exchange_weak( 518 | &self, 519 | current: T, 520 | new: T, 521 | success: Ordering, 522 | failure: Ordering, 523 | ) -> Result { 524 | T::Repr::compare_exchange_weak(&self.0, current.pack(), new.pack(), success, failure) 525 | .map(T::unpack) 526 | .map_err(T::unpack) 527 | } 528 | 529 | /// Fetches the value, and applies a function to it that returns an 530 | /// optional new value. Returns a `Result` of `Ok(previous_value)` if the 531 | /// function returned `Some(_)`, else `Err(previous_value)`. 532 | /// 533 | /// Note: This may call the function multiple times if the value has been 534 | /// changed from other threads in the meantime, as long as the function 535 | /// returns `Some(_)`, but the function will have been applied but once to 536 | /// the stored value. 537 | /// 538 | /// `fetch_update` takes two [`Ordering`] arguments to describe the memory 539 | /// ordering of this operation. The first describes the required ordering 540 | /// for loads and failed updates while the second describes the required 541 | /// ordering when the operation finally succeeds. Beware that this is 542 | /// different from the two modes in `compare_exchange`! 543 | /// 544 | /// Using `Acquire` as success ordering makes the store part of this 545 | /// operation `Relaxed`, and using `Release` makes the final successful 546 | /// load `Relaxed`. The (failed) load ordering can only be `SeqCst`, 547 | /// `Acquire` or `Relaxed` and must be equivalent to or weaker than the 548 | /// success ordering. 549 | /// 550 | /// # Examples 551 | /// 552 | /// ``` 553 | /// use atomig::{Atomic, Ordering}; 554 | /// 555 | /// let x = Atomic::new(7); 556 | /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); 557 | /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); 558 | /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); 559 | /// assert_eq!(x.load(Ordering::SeqCst), 9); 560 | /// ``` 561 | pub fn fetch_update( 562 | &self, 563 | set_order: Ordering, 564 | fetch_order: Ordering, 565 | mut f: F, 566 | ) -> Result 567 | where 568 | F: FnMut(T) -> Option 569 | { 570 | let f = |repr| f(T::unpack(repr)).map(Atom::pack); 571 | T::Repr::fetch_update(&self.0, set_order, fetch_order, f) 572 | .map(Atom::unpack) 573 | .map_err(Atom::unpack) 574 | } 575 | } 576 | 577 | // TODO: the `where` bound should not be necessary as the `AtomLogic` trait 578 | // already specifies this. Maybe we can fix this in the future. 579 | impl Atomic 580 | where 581 | T::Repr: PrimitiveAtomLogic, 582 | { 583 | /// Bitwise "and" with the current value. 584 | /// 585 | /// Performs a bitwise "and" operation on the current value and the 586 | /// argument `val`, and sets the new value to the result. 587 | /// 588 | /// Returns the previous value. 589 | /// 590 | /// `fetch_and` takes an [`Ordering`] argument which describes the memory 591 | /// ordering of this operation. All ordering modes are possible. Note that 592 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 593 | /// using `Release` makes the load part `Relaxed`. 594 | /// 595 | /// Examples 596 | /// 597 | /// ``` 598 | /// use atomig::{Atomic, Ordering}; 599 | /// 600 | /// let x = Atomic::new(0b101101); 601 | /// assert_eq!(x.fetch_and(0b110011, Ordering::SeqCst), 0b101101); 602 | /// assert_eq!(x.load(Ordering::SeqCst), 0b100001); 603 | /// ``` 604 | pub fn fetch_and(&self, val: T, order: Ordering) -> T { 605 | T::unpack(T::Repr::fetch_and(&self.0, val.pack(), order)) 606 | } 607 | 608 | /// Bitwise "nand" with the current value. 609 | /// 610 | /// Performs a bitwise "nand" operation on the current value and the 611 | /// argument `val`, and sets the new value to the result. 612 | /// 613 | /// Returns the previous value. 614 | /// 615 | /// `fetch_nand` takes an [`Ordering`] argument which describes the memory 616 | /// ordering of this operation. All ordering modes are possible. Note that 617 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 618 | /// using `Release` makes the load part `Relaxed`. 619 | /// 620 | /// Examples 621 | /// 622 | /// ``` 623 | /// use atomig::{Atomic, Ordering}; 624 | /// 625 | /// let x = Atomic::new(0x13); 626 | /// assert_eq!(x.fetch_nand(0x31, Ordering::SeqCst), 0x13); 627 | /// assert_eq!(x.load(Ordering::SeqCst), !(0x13 & 0x31)); 628 | /// ``` 629 | pub fn fetch_nand(&self, val: T, order: Ordering) -> T { 630 | T::unpack(T::Repr::fetch_nand(&self.0, val.pack(), order)) 631 | } 632 | 633 | /// Bitwise "or" with the current value. 634 | /// 635 | /// Performs a bitwise "or" operation on the current value and the 636 | /// argument `val`, and sets the new value to the result. 637 | /// 638 | /// Returns the previous value. 639 | /// 640 | /// `fetch_or` takes an [`Ordering`] argument which describes the memory 641 | /// ordering of this operation. All ordering modes are possible. Note that 642 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 643 | /// using `Release` makes the load part `Relaxed`. 644 | /// 645 | /// Examples 646 | /// 647 | /// ``` 648 | /// use atomig::{Atomic, Ordering}; 649 | /// 650 | /// let x = Atomic::new(0b101101); 651 | /// assert_eq!(x.fetch_or(0b110011, Ordering::SeqCst), 0b101101); 652 | /// assert_eq!(x.load(Ordering::SeqCst), 0b111111); 653 | /// ``` 654 | pub fn fetch_or(&self, val: T, order: Ordering) -> T { 655 | T::unpack(T::Repr::fetch_or(&self.0, val.pack(), order)) 656 | } 657 | 658 | /// Bitwise "xor" with the current value. 659 | /// 660 | /// Performs a bitwise "xor" operation on the current value and the 661 | /// argument `val`, and sets the new value to the result. 662 | /// 663 | /// Returns the previous value. 664 | /// 665 | /// `fetch_xor` takes an [`Ordering`] argument which describes the memory 666 | /// ordering of this operation. All ordering modes are possible. Note that 667 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 668 | /// using `Release` makes the load part `Relaxed`. 669 | /// 670 | /// Examples 671 | /// 672 | /// ``` 673 | /// use atomig::{Atomic, Ordering}; 674 | /// 675 | /// let x = Atomic::new(0b101101); 676 | /// assert_eq!(x.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); 677 | /// assert_eq!(x.load(Ordering::SeqCst), 0b011110); 678 | /// ``` 679 | pub fn fetch_xor(&self, val: T, order: Ordering) -> T { 680 | T::unpack(T::Repr::fetch_xor(&self.0, val.pack(), order)) 681 | } 682 | } 683 | 684 | 685 | // TODO: the `where` bound should not be necessary as the `AtomInteger` trait 686 | // already specifies this. Maybe we can fix this in the future. 687 | impl Atomic 688 | where 689 | T::Repr: PrimitiveAtomInteger, 690 | { 691 | /// Adds to the current value, returning the previous value. 692 | /// 693 | /// This operation wraps around on overflow. 694 | /// 695 | /// `fetch_add` takes an [`Ordering`] argument which describes the memory 696 | /// ordering of this operation. All ordering modes are possible. Note that 697 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 698 | /// using `Release` makes the load part `Relaxed`. 699 | /// 700 | /// # Examples 701 | /// 702 | /// ``` 703 | /// use atomig::{Atomic, Ordering}; 704 | /// 705 | /// let x = Atomic::new(0); 706 | /// assert_eq!(x.fetch_add(10, Ordering::SeqCst), 0); 707 | /// assert_eq!(x.load(Ordering::SeqCst), 10); 708 | /// ``` 709 | pub fn fetch_add(&self, val: T, order: Ordering) -> T { 710 | T::unpack(T::Repr::fetch_add(&self.0, val.pack(), order)) 711 | } 712 | 713 | /// Subtracts from the current value, returning the previous value. 714 | /// 715 | /// This operation wraps around on overflow. 716 | /// 717 | /// `fetch_sub` takes an [`Ordering`] argument which describes the memory 718 | /// ordering of this operation. All ordering modes are possible. Note that 719 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 720 | /// using `Release` makes the load part `Relaxed`. 721 | /// 722 | /// # Examples 723 | /// 724 | /// ``` 725 | /// use atomig::{Atomic, Ordering}; 726 | /// 727 | /// let x = Atomic::new(20); 728 | /// assert_eq!(x.fetch_sub(10, Ordering::SeqCst), 20); 729 | /// assert_eq!(x.load(Ordering::SeqCst), 10); 730 | /// ``` 731 | pub fn fetch_sub(&self, val: T, order: Ordering) -> T { 732 | T::unpack(T::Repr::fetch_sub(&self.0, val.pack(), order)) 733 | } 734 | 735 | /// Maximum with the current value. 736 | /// 737 | /// Finds the maximum of the current value and the argument `val`, and sets 738 | /// the new value to the result. 739 | /// 740 | /// Returns the previous value. 741 | /// 742 | /// `fetch_max` takes an [`Ordering`] argument which describes the memory 743 | /// ordering of this operation. All ordering modes are possible. Note that 744 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 745 | /// using `Release` makes the load part `Relaxed`. 746 | /// 747 | /// # Examples 748 | /// 749 | /// ``` 750 | /// use atomig::{Atomic, Ordering}; 751 | /// 752 | /// let foo = Atomic::new(23); 753 | /// assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); 754 | /// assert_eq!(foo.load(Ordering::SeqCst), 42); 755 | /// ``` 756 | /// 757 | /// If you want to obtain the maximum value in one step, you can use the 758 | /// following: 759 | /// 760 | /// ``` 761 | /// use atomig::{Atomic, Ordering}; 762 | /// 763 | /// let foo = Atomic::new(23); 764 | /// let bar = 42; 765 | /// let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); 766 | /// assert!(max_foo == 42); 767 | /// ``` 768 | pub fn fetch_max(&self, val: T, order: Ordering) -> T { 769 | T::unpack(T::Repr::fetch_max(&self.0, val.pack(), order)) 770 | } 771 | 772 | /// Minimum with the current value. 773 | /// 774 | /// Finds the minimum of the current value and the argument `val`, and sets 775 | /// the new value to the result. 776 | /// 777 | /// Returns the previous value. 778 | /// 779 | /// `fetch_min` takes an [`Ordering`] argument which describes the memory 780 | /// ordering of this operation. All ordering modes are possible. Note that 781 | /// using `Acquire` makes the store part of this operation `Relaxed`, and 782 | /// using `Release` makes the load part `Relaxed`. 783 | /// 784 | /// # Examples 785 | /// 786 | /// ``` 787 | /// use atomig::{Atomic, Ordering}; 788 | /// 789 | /// let foo = Atomic::new(23); 790 | /// assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); 791 | /// assert_eq!(foo.load(Ordering::Relaxed), 23); 792 | /// assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); 793 | /// assert_eq!(foo.load(Ordering::Relaxed), 22); 794 | /// ``` 795 | /// 796 | /// If you want to obtain the minimum value in one step, you can use the 797 | /// following: 798 | /// 799 | /// ``` 800 | /// use atomig::{Atomic, Ordering}; 801 | /// 802 | /// let foo = Atomic::new(23); 803 | /// let bar = 12; 804 | /// let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); 805 | /// assert!(min_foo == 12); 806 | /// ``` 807 | pub fn fetch_min(&self, val: T, order: Ordering) -> T { 808 | T::unpack(T::Repr::fetch_min(&self.0, val.pack(), order)) 809 | } 810 | } 811 | 812 | impl fmt::Debug for Atomic { 813 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 814 | self.load(Ordering::SeqCst).fmt(f) 815 | } 816 | } 817 | 818 | impl Default for Atomic { 819 | fn default() -> Self { 820 | Self::new(T::default()) 821 | } 822 | } 823 | 824 | impl From for Atomic { 825 | fn from(v: T) -> Self { 826 | Self::new(v) 827 | } 828 | } 829 | 830 | #[cfg(feature = "serde")] 831 | impl serde::Serialize for Atomic { 832 | fn serialize(&self, serializer: S) -> Result 833 | where 834 | S: serde::Serializer, 835 | { 836 | self.load(Ordering::SeqCst).serialize(serializer) 837 | } 838 | } 839 | 840 | 841 | #[cfg(feature = "serde")] 842 | impl<'de, T: Atom + serde::Deserialize<'de>> serde::Deserialize<'de> for Atomic { 843 | fn deserialize(deserializer: D) -> Result 844 | where 845 | D: serde::Deserializer<'de>, 846 | { 847 | serde::Deserialize::deserialize(deserializer).map(Self::new) 848 | } 849 | } 850 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{Atom, Atomic, Ordering}; 2 | 3 | // =============================================================================================== 4 | // ===== Single test snippets 5 | // =============================================================================================== 6 | 7 | macro_rules! generic_tests { 8 | ($ty:ty, $val0:expr, $val1:expr) => { 9 | #[test] 10 | fn new() { 11 | let _: Atomic<$ty> = Atomic::new($val0); 12 | } 13 | 14 | #[test] 15 | fn load_store() { 16 | let a = Atomic::new($val0); 17 | assert_eq!(a.load(Ordering::SeqCst), $val0); 18 | 19 | a.store($val1, Ordering::SeqCst); 20 | assert_eq!(a.load(Ordering::SeqCst), $val1); 21 | } 22 | 23 | #[test] 24 | fn into_inner() { 25 | let a = Atomic::new($val0); 26 | assert_eq!(a.into_inner(), $val0); 27 | } 28 | 29 | #[test] 30 | fn swap() { 31 | let a = Atomic::new($val0); 32 | assert_eq!(a.swap($val1, Ordering::SeqCst), $val0); 33 | assert_eq!(a.load(Ordering::SeqCst), $val1); 34 | } 35 | 36 | #[test] 37 | fn fmt_debug() { 38 | let a = Atomic::new($val0); 39 | assert_eq!(format!("{:?}", a), format!("{:?}", $val0)); 40 | } 41 | 42 | #[test] 43 | fn from() { 44 | let a = Atomic::new($val0); 45 | let b: Atomic<$ty> = $val0.into(); 46 | assert_eq!(a.load(Ordering::SeqCst), b.load(Ordering::SeqCst)); 47 | } 48 | 49 | // TODO: compare_and_* methods 50 | }; 51 | } 52 | 53 | macro_rules! default_tests { 54 | ($ty:ty) => { 55 | #[test] 56 | fn default() { 57 | let a: Atomic<$ty> = Default::default(); 58 | assert_eq!(a.load(Ordering::SeqCst), <$ty>::default()); 59 | } 60 | }; 61 | } 62 | 63 | macro_rules! serde_tests { 64 | ($ty:ty, $val0:expr) => { 65 | #[cfg(feature = "serde")] 66 | use bincode; 67 | 68 | #[cfg(feature = "serde")] 69 | #[test] 70 | fn test_serde_round_trip() { 71 | let src = Atomic::new($val0); 72 | let serialized = bincode::serialize(&src).unwrap(); 73 | let deserialized: Atomic<$ty> = bincode::deserialize(&serialized).unwrap(); 74 | assert_eq!(src.load(Ordering::SeqCst), deserialized.load(Ordering::SeqCst)); 75 | } 76 | }; 77 | } 78 | 79 | macro_rules! logic_tests { 80 | ($val0:expr, $val1:expr) => { 81 | #[test] 82 | fn logic() { 83 | let a = Atomic::new($val0); 84 | assert_eq!(a.fetch_and($val1, Ordering::SeqCst), $val0); 85 | assert_eq!(a.load(Ordering::SeqCst), $val0 & $val1); 86 | 87 | let a = Atomic::new($val0); 88 | assert_eq!(a.fetch_nand($val1, Ordering::SeqCst), $val0); 89 | assert_eq!(a.load(Ordering::SeqCst), !($val0 & $val1)); 90 | 91 | let a = Atomic::new($val0); 92 | assert_eq!(a.fetch_or($val1, Ordering::SeqCst), $val0); 93 | assert_eq!(a.load(Ordering::SeqCst), $val0 | $val1); 94 | 95 | let a = Atomic::new($val0); 96 | assert_eq!(a.fetch_xor($val1, Ordering::SeqCst), $val0); 97 | assert_eq!(a.load(Ordering::SeqCst), $val0 ^ $val1); 98 | } 99 | }; 100 | } 101 | 102 | macro_rules! int_tests { 103 | ($val0:expr, $val1:expr) => { 104 | #[test] 105 | fn integer() { 106 | let a = Atomic::new($val0); 107 | assert_eq!(a.fetch_add($val1, Ordering::SeqCst), $val0); 108 | assert_eq!(a.load(Ordering::SeqCst), $val0.wrapping_add($val1)); 109 | 110 | let a = Atomic::new($val0); 111 | assert_eq!(a.fetch_sub($val1, Ordering::SeqCst), $val0); 112 | assert_eq!(a.load(Ordering::SeqCst), $val0.wrapping_sub($val1)); 113 | } 114 | }; 115 | } 116 | 117 | /// If the first token is `y`, emit the following tokens (inside a brace), 118 | /// otherwise emit nothing. 119 | macro_rules! emit_if { 120 | (n, { $($t:tt)* }) => {}; 121 | (y, { $($t:tt)* }) => { $($t)* }; 122 | } 123 | 124 | 125 | // =============================================================================================== 126 | // ===== Actual tests of different types 127 | // =============================================================================================== 128 | 129 | macro_rules! gen_tests_for_primitives { 130 | ( 131 | $mod_name:ident, $ty:ty, $val0:expr, $val1:expr, 132 | [$with_logic:ident $with_int:ident] 133 | ) => { 134 | mod $mod_name { 135 | use super::*; 136 | 137 | generic_tests!($ty, $val0, $val1); 138 | default_tests!($ty); 139 | serde_tests!($ty, $val0); 140 | 141 | emit_if!($with_logic, { logic_tests!($val0, $val1); }); 142 | emit_if!($with_int, { int_tests!($val0, $val1); }); 143 | } 144 | }; 145 | } 146 | 147 | // mod ty val0 val1 [logic int] 148 | gen_tests_for_primitives!(_bool, bool, true, false, [y n]); 149 | gen_tests_for_primitives!(_u8, u8, 7u8, 33u8, [y y]); 150 | gen_tests_for_primitives!(_i8, i8, 7i8, 33i8, [y y]); 151 | gen_tests_for_primitives!(_u16, u16, 7u16, 33u16, [y y]); 152 | gen_tests_for_primitives!(_i16, i16, 7i16, 33i16, [y y]); 153 | gen_tests_for_primitives!(_u32, u32, 7u32, 33u32, [y y]); 154 | gen_tests_for_primitives!(_i32, i32, 7i32, 33i32, [y y]); 155 | gen_tests_for_primitives!(_u64, u64, 7u64, 33u64, [y y]); 156 | gen_tests_for_primitives!(_i64, i64, 7i64, 33i64, [y y]); 157 | gen_tests_for_primitives!(_usize, usize, 7usize, 33usize, [y y]); 158 | gen_tests_for_primitives!(_isize, isize, 7isize, 33isize, [y y]); 159 | gen_tests_for_primitives!(_f32, f32, 7.0f32, 33.0f32, [n n]); 160 | gen_tests_for_primitives!(_f64, f64, 7.0f64, 33.0f64, [n n]); 161 | gen_tests_for_primitives!(_char, char, 'x', '♥', [n n]); 162 | 163 | // Arrays. They do implement `AtomLogic` but the logic tests don't work with them. 164 | // mod ty val0 val1 [logic int] 165 | gen_tests_for_primitives!(_u8array2, [u8; 2], [3u8, 79], [17u8, 240], [n n]); 166 | gen_tests_for_primitives!(_u8array4, [u8; 4], [3u8, 79, 13, 230], [17u8, 240, 59, 184], [n n]); 167 | gen_tests_for_primitives!(_u8array8, [u8; 8], [3u8, 79, 13, 230, 4, 80, 14, 231], 168 | [17u8, 240, 59, 184, 18, 241, 60, 185], [n n]); 169 | gen_tests_for_primitives!(_u16array2, [u16; 2], [3u16, 257], [17u16, 9999], [n n]); 170 | gen_tests_for_primitives!(_u16array4, [u16; 4], [3u16, 257, 13, 230], 171 | [17u16, 9999, 59, 17003], [n n]); 172 | gen_tests_for_primitives!(_u32array2, [u32; 2], [3u32, 77977], [17u32, 190247], [n n]); 173 | gen_tests_for_primitives!(_i8array2, [i8; 2], [3i8, -79], [-17i8, 113], [n n]); 174 | gen_tests_for_primitives!(_i8array4, [i8; 4], [3i8, -79, 13, -120], 175 | [-17i8, 113, -59, -98], [n n]); 176 | gen_tests_for_primitives!(_i8array8, [i8; 8], [3i8, -79, 13, -120, 4, 80, -14, 111], 177 | [-17i8, 113, -59, -98, -18, 114, 60, -128], [n n]); 178 | gen_tests_for_primitives!(_i16array2, [i16; 2], [3i16, -257], [17i16, -9999], [n n]); 179 | gen_tests_for_primitives!(_i16array4, [i16; 4], [3i16, -257, 13, 230], 180 | [17i16, -9999, -59, 17003], [n n]); 181 | gen_tests_for_primitives!(_i32array2, [i32; 2], [3i32, -77977], [-17i32, 190247], [n n]); 182 | 183 | mod _ptr { 184 | use super::*; 185 | generic_tests!(Foo, Foo::Nothing, Foo::Set(0b101)); 186 | } 187 | 188 | mod custom { 189 | use super::*; 190 | generic_tests!(Foo, Foo::Nothing, Foo::Set(0b101)); 191 | default_tests!(Foo); 192 | } 193 | 194 | 195 | #[derive(Debug, PartialEq, Eq)] 196 | enum Foo { 197 | Nothing, 198 | Set(u8), 199 | } 200 | 201 | impl Default for Foo { 202 | fn default() -> Self { 203 | Foo::Set(0b10101010) 204 | } 205 | } 206 | 207 | impl Atom for Foo { 208 | type Repr = u16; 209 | fn pack(self) -> Self::Repr { 210 | match self { 211 | Foo::Nothing => 0x01FF, 212 | Foo::Set(s) => s as u16, 213 | } 214 | } 215 | fn unpack(src: Self::Repr) -> Self { 216 | if src & 0x0100 != 0 { 217 | Foo::Nothing 218 | } else { 219 | Foo::Set((src & 0xFF) as u8) 220 | } 221 | } 222 | } 223 | 224 | macro_rules! gen_tests_for_opt_non_zeroes { 225 | ($mod_name:ident, $ty:ident) => { 226 | mod $mod_name { 227 | use super::*; 228 | use std::num::$ty; 229 | 230 | generic_tests!(Option<$ty>, $ty::new(7), $ty::new(33)); 231 | default_tests!(Option<$ty>); 232 | serde_tests!(Option<$ty>, $ty::new(7)); 233 | 234 | #[test] 235 | fn integer() { 236 | let a = Atomic::new($ty::new(7)); 237 | assert_eq!(a.fetch_add($ty::new(33), Ordering::SeqCst), $ty::new(7)); 238 | assert_eq!(a.load(Ordering::SeqCst), $ty::new(40)); 239 | 240 | let a = Atomic::new($ty::new(33)); 241 | assert_eq!(a.fetch_sub($ty::new(7), Ordering::SeqCst), $ty::new(33)); 242 | assert_eq!(a.load(Ordering::SeqCst), $ty::new(26)); 243 | 244 | let a = Atomic::new($ty::new(33)); 245 | assert_eq!(a.fetch_sub(None, Ordering::SeqCst), $ty::new(33)); 246 | assert_eq!(a.load(Ordering::SeqCst), $ty::new(33)); 247 | 248 | let a = Atomic::new($ty::new(27)); 249 | assert_eq!(a.fetch_sub($ty::new(27), Ordering::SeqCst), $ty::new(27)); 250 | assert_eq!(a.load(Ordering::SeqCst), None); 251 | } 252 | } 253 | }; 254 | } 255 | 256 | gen_tests_for_opt_non_zeroes!(nz_u8, NonZeroU8); 257 | gen_tests_for_opt_non_zeroes!(nz_i8, NonZeroI8); 258 | gen_tests_for_opt_non_zeroes!(nz_u16, NonZeroU16); 259 | gen_tests_for_opt_non_zeroes!(nz_i16, NonZeroI16); 260 | gen_tests_for_opt_non_zeroes!(nz_u32, NonZeroU32); 261 | gen_tests_for_opt_non_zeroes!(nz_i32, NonZeroI32); 262 | gen_tests_for_opt_non_zeroes!(nz_u64, NonZeroU64); 263 | gen_tests_for_opt_non_zeroes!(nz_i64, NonZeroI64); 264 | gen_tests_for_opt_non_zeroes!(nz_usize, NonZeroUsize); 265 | gen_tests_for_opt_non_zeroes!(nz_isize, NonZeroIsize); 266 | 267 | fn _atomic_is_always_send_sync(a: Atomic) { 268 | fn requires_send_sync(_: X) {} 269 | 270 | requires_send_sync(a); 271 | } 272 | --------------------------------------------------------------------------------