├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── avr-specs └── avr-atmega328p.json ├── build.rs ├── ci.sh ├── rust-toolchain.toml └── src ├── lib.rs └── polyfill.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Build 19 | run: ./ci.sh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "rust-analyzer.checkOnSave.allTargets": false, 4 | "rust-analyzer.cargo.target": "thumbv6m-none-eabi", 5 | "rust-analyzer.procMacro.enable": true, 6 | "rust-analyzer.imports.granularity.group": "module", 7 | "rust-analyzer.imports.granularity.enforce": true, 8 | "rust-analyzer.cargo.buildScripts.enable": true, 9 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | No unreleased changes yet 11 | 12 | ## 1.0.3 - 2023-07-11 13 | 14 | - Crate is deprecated, use `portable-atomic` instead. 15 | 16 | ## 1.0.2 - 2023-03-12 17 | 18 | - Add MSP430 support. 19 | 20 | ## 1.0.1 - 2022-08-12 21 | 22 | - Fix `AtomicPtr` accidentally not being available when not polyfilled. 23 | 24 | ## 1.0.0 - 2022-08-12 25 | 26 | - Update to `critical-section` v1.0 27 | 28 | ## 0.1.11 - 2022-11-29 29 | 30 | - Bump `critical-section` dependency from `0.2` to `1.0`. 31 | 32 | This is a breaking change if you were relying on the default implementations available on `critical-section 0.2.7` and earlier. 33 | They have been removed in `critical-section 0.2.8` because they were **unsound**, since there's no way to guarantee they're correct for the 34 | target in use (for example for multi-core embedded targets). Since `critical-section 0.2.8` just forwards to `1.0` now, we decided to change `atomic-polyfill` to use `1.0` directly. 35 | 36 | If you're seeing a linker error like `undefined symbol: _critical_section_1_0_acquire`, you're affected. To fix it: 37 | 38 | - If your target supports `std`: Add the `critical-section` dependency to `Cargo.toml` enabling the `std` feature. 39 | 40 | ```toml 41 | [dependencies] 42 | critical-section = { version = "1.1", features = ["std"]} 43 | ``` 44 | 45 | - For single-core Cortex-M targets in privileged mode: 46 | ```toml 47 | [dependencies] 48 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} 49 | ``` 50 | 51 | - For single-hart RISC-V targets in privileged mode: 52 | ```toml 53 | [dependencies] 54 | riscv = { version = "0.10", features = ["critical-section-single-hart"]} 55 | ``` 56 | 57 | - For other targets: check if your HAL or architecture-support crate has a `critical-section 1.0` implementation available. Otherwise, [provide your own](https://github.com/rust-embedded/critical-section#providing-an-implementation). 58 | 59 | ## 0.1.10 - 2022-08-12 60 | 61 | - Fix `AtomicPtr` accidentally not being available when not polyfilled. 62 | 63 | ## 0.1.9 - 2022-08-12 64 | 65 | - Switch to only two polyfill levels. 66 | 67 | The "CAS" level which uses atomic load/store and critical-section based CAS was not 68 | sound, because `critical-section` guarantees only "no other critical section can run concurrently", 69 | not "no other code can run concurrently". Therefore a CS-based CAS can still race a native atomic store. 70 | 71 | ## 0.1.8 - 2022-04-12 72 | 73 | - Added AVR support. 74 | 75 | ## 0.1.7 - 2022-03-22 76 | 77 | - Added support for xtensa (ESP chips), with and without ESP-IDF. 78 | - Reexport `core::sync::atomic::*` as-is for unknown targets, to avoid build failures if they don't have full atomic support. 79 | 80 | ## 0.1.6 - 2022-02-08 81 | 82 | - Add polyfill support for `thumbv4t` targets. (Nintendo Game Boy Advance) 83 | - Added `get_mut()` to `AtomicBool`. 84 | - Added `into_inner()` to all atomics 85 | - Added `fmt::Debug` impl to `AtomicBool`, `AtomicPtr`. 86 | - Added `fmt::Pointer` impl to `AtomicPtr`. 87 | - Added `From<*mut T>` impl to `AtomicPtr`. 88 | - Added `RefUnwindSafe` impl to all atomics. 89 | 90 | ## 0.1.5 - 2021-11-02 91 | 92 | - Updated critical-section to v0.2.5. Fixes `std` implementation to allow reentrant (nested) critical sections. This would previously deadlock. 93 | 94 | ## 0.1.4 - 2021-09-20 95 | 96 | - Added support for RISC-V. 97 | - Added support for "full polyfill" level, where load/stores are polyfilled, not just CAS operations. 98 | - Added support for `AtomicU64`, `AtomicI64`. 99 | 100 | ## 0.1.3 - 2021-08-07 101 | 102 | - Only import `cortex-m` when needed (#4) 103 | - Fix panic on `fetch_update` due to incorrect ordering (#5) 104 | 105 | ## 0.1.2 - 2021-03-29 106 | 107 | - Added missing reexport of `fence` and `compiler_fence` in polyfilled mode. 108 | 109 | ## 0.1.1 - 2021-03-04 110 | 111 | - Added polyfills for AtomicU8, AtomicU16, AtomicUsize, AtomicI8, AtomicI16, AtomicI32, AtomicIsize 112 | 113 | ## 0.1.0 - 2021-03-04 114 | 115 | - First release 116 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "atomic-polyfill" 7 | version = "1.0.3" 8 | dependencies = [ 9 | "critical-section", 10 | ] 11 | 12 | [[package]] 13 | name = "critical-section" 14 | version = "1.0.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ce181dda5a65c00fcbce2b9d44ad9be001202a54fb321865ed273ac39aa8ccbe" 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Dario Nieuwenhuis "] 3 | description = "Atomic polyfills, for targets where they're not available." 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | name = "atomic-polyfill" 7 | readme = "README.md" 8 | repository = "https://github.com/embassy-rs/atomic-polyfill" 9 | version = "1.0.3" 10 | 11 | categories = [ 12 | "embedded", 13 | "no-std", 14 | "concurrency", 15 | ] 16 | 17 | [dependencies] 18 | critical-section = "1.0.0" 19 | -------------------------------------------------------------------------------- /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) 2020 Dario Nieuwenhuis 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 | # ⚠️ THIS CRATE IS DEPRECATED ⚠️ 2 | 3 | **Use [`portable-atomic`](https://crates.io/crates/portable-atomic) instead.** It supports many more architectures and more efficient ways of emulating atomics. 4 | 5 | `portable-atomic` with the `critical-section` feature is a drop-in replacement. It uses the `critical-section` crate to ensure locking, just like `atomic-polyfill`. 6 | 7 | However, if your chip is single-core, you might want to enable the `unsafe-assume-single-core` feature instead. It makes `portable-atomic` emulate atomics by disabling interrupts. 8 | It is faster than using a `critical-section` implementation that disables interrupts, because it allows disabling them only on CAS operations, not in load/store operations. 9 | 10 | **If you're writing a library**, add a dependency on `portable-atomic` but do NOT enable any feature on it. Let the end user of your library enable the right features for their target. 11 | If you enable features, you're taking their choice away. 12 | 13 | ----- 14 | 15 | # atomic-polyfill 16 | 17 | [![Documentation](https://docs.rs/atomic-polyfill/badge.svg)](https://docs.rs/atomic-polyfill) 18 | 19 | This crate polyfills atomics on targets where they're not available, using critical sections. It is intended to be a drop-in replacement for `core::sync::atomic`. 20 | 21 | There are two "levels" of polyfilling: 22 | - Native: No polyfilling is performed, the native `core::sync::atomic::AtomicXX` is reexported. 23 | - Full: Both load/store and compare-and-set operations are polyfilled. 24 | 25 | Polyfilling requires a [`critical-section`](https://github.com/rust-embedded/critical-section) implementation for the current target. Check the `critical-section` README for details. 26 | 27 | ## Target support 28 | 29 | The right polyfill level is automatically picked based on the target and the atomic width: 30 | 31 | | Target | Level | Level for u64/i64 | 32 | |--------------------|------------------|-------------------| 33 | | thumbv4t | Full | Full | 34 | | thumbv6m | Full | Full | 35 | | thumbv7*, thumbv8* | Native | Full | 36 | | riscv32imc | Full | Full | 37 | | riscv32imac | Native | Full | 38 | | xtensa-*-espidf | Native | Native | 39 | | xtensa-esp32-* | Native | Full | 40 | | xtensa-esp32s2-* | Full | Full | 41 | | xtensa-esp32s3-* | Native | Full | 42 | | xtensa-esp8266-* | Full | Full | 43 | | AVR | Full | Full | 44 | 45 | For targets not listed above, `atomic-polyfill` assumes nothing and reexports `core::sync::atomic::*`. No polyfilling is done. PRs for polyfilling more targets are welcome :) 46 | 47 | ## Minimum Supported Rust Version (MSRV) 48 | 49 | MSRV is currently **Rust 1.54**. MSRV may be upgraded at any new patch release as long 50 | as latest stable Rust is supported. 51 | 52 | ## License 53 | 54 | This work is licensed under either of 55 | 56 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 57 | http://www.apache.org/licenses/LICENSE-2.0) 58 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 59 | 60 | at your option. 61 | 62 | ## Contribution 63 | 64 | Unless you explicitly state otherwise, any contribution intentionally submitted 65 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 66 | dual licensed as above, without any additional terms or conditions. 67 | -------------------------------------------------------------------------------- /avr-specs/avr-atmega328p.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "avr", 3 | "atomic-cas": false, 4 | "cpu": "atmega328p", 5 | "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", 6 | "eh-frame-header": false, 7 | "exe-suffix": ".elf", 8 | "executables": true, 9 | "late-link-args": { 10 | "gcc": [ 11 | "-lgcc" 12 | ] 13 | }, 14 | "linker": "avr-gcc", 15 | "linker-is-gnu": true, 16 | "llvm-target": "avr-unknown-unknown", 17 | "max-atomic-width": 8, 18 | "no-default-libraries": false, 19 | "pre-link-args": { 20 | "gcc": [ 21 | "-mmcu=atmega328p", 22 | "-Wl,--as-needed" 23 | ] 24 | }, 25 | "target-c-int-width": "16", 26 | "target-pointer-width": "16" 27 | } 28 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fmt; 3 | 4 | #[derive(Clone, Copy, PartialEq, Eq)] 5 | enum PolyfillLevel { 6 | // Native, ie no polyfill. Just reexport from core::sync::atomic 7 | Native, 8 | // Full polyfill: polyfill both load/store and CAS with critical sections 9 | Polyfill, 10 | } 11 | 12 | impl fmt::Display for PolyfillLevel { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | let s = match *self { 15 | Self::Native => "native", 16 | Self::Polyfill => "polyfill", 17 | }; 18 | write!(f, "{}", s) 19 | } 20 | } 21 | 22 | fn main() { 23 | let target = env::var("TARGET").unwrap(); 24 | 25 | use PolyfillLevel::*; 26 | 27 | let patterns = [ 28 | ("avr-*", (Polyfill, Polyfill)), 29 | ("msp430-none-elf", (Polyfill, Polyfill)), 30 | ("riscv32imac-*", (Native, Polyfill)), 31 | ("riscv32gc-*", (Native, Polyfill)), 32 | ("riscv32imc-*-espidf", (Native, Native)), 33 | ("riscv32*", (Polyfill, Polyfill)), 34 | ("thumbv4t-*", (Polyfill, Polyfill)), 35 | ("thumbv6m-*", (Polyfill, Polyfill)), 36 | ("thumbv7m-*", (Native, Polyfill)), 37 | ("thumbv7em-*", (Native, Polyfill)), 38 | ("thumbv8m.base-*", (Native, Polyfill)), 39 | ("thumbv8m.main-*", (Native, Polyfill)), 40 | ("xtensa-*-espidf", (Native, Native)), 41 | ("xtensa-esp32-*", (Native, Polyfill)), 42 | ("xtensa-esp32s2-*", (Polyfill, Polyfill)), 43 | ("xtensa-esp32s3-*", (Native, Polyfill)), 44 | ("xtensa-esp8266-*", (Polyfill, Polyfill)), 45 | ]; 46 | 47 | if let Some((_, (level, level64))) = patterns 48 | .iter() 49 | .find(|(pattern, _)| matches(pattern, &target)) 50 | { 51 | if *level == PolyfillLevel::Polyfill { 52 | println!("cargo:rustc-cfg=polyfill_u8"); 53 | println!("cargo:rustc-cfg=polyfill_u16"); 54 | println!("cargo:rustc-cfg=polyfill_u32"); 55 | println!("cargo:rustc-cfg=polyfill_usize"); 56 | println!("cargo:rustc-cfg=polyfill_i8"); 57 | println!("cargo:rustc-cfg=polyfill_i16"); 58 | println!("cargo:rustc-cfg=polyfill_i32"); 59 | println!("cargo:rustc-cfg=polyfill_isize"); 60 | println!("cargo:rustc-cfg=polyfill_ptr"); 61 | println!("cargo:rustc-cfg=polyfill_bool"); 62 | } 63 | 64 | if *level64 == PolyfillLevel::Polyfill { 65 | println!("cargo:rustc-cfg=polyfill_u64"); 66 | println!("cargo:rustc-cfg=polyfill_i64"); 67 | } 68 | } else { 69 | // If we don't know about the target, just reexport the entire `core::atomic::*` 70 | // This doesn't polyfill anything, but it's guaranteed to never fail build. 71 | println!("cargo:rustc-cfg=reexport_core"); 72 | } 73 | } 74 | 75 | // tiny glob replacement to avoid pulling in more crates. 76 | // Supports 0 or 1 wildcards `*` 77 | fn matches(pattern: &str, val: &str) -> bool { 78 | if let Some(p) = pattern.find('*') { 79 | let prefix = &pattern[..p]; 80 | let suffix = &pattern[p + 1..]; 81 | val.len() >= prefix.len() + suffix.len() && val.starts_with(prefix) && val.ends_with(suffix) 82 | } else { 83 | val == pattern 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | cargo build 6 | cargo build --target thumbv6m-none-eabi 7 | cargo build --target thumbv7em-none-eabi 8 | cargo build --target riscv32imc-unknown-none-elf 9 | cargo build --target riscv32imac-unknown-none-elf 10 | cargo build --target i686-unknown-linux-gnu 11 | cargo build --target x86_64-unknown-linux-gnu 12 | cargo build --target riscv64gc-unknown-linux-gnu 13 | cargo build --target msp430-none-elf -Zbuild-std=core 14 | 15 | # without --release, it fails with "error: ran out of registers during register allocation" 16 | cargo build --release -Zbuild-std=core --target avr-specs/avr-atmega328p.json 17 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # Before upgrading check that everything is available on all tier1 targets here: 2 | # https://rust-lang.github.io/rustup-components-history 3 | [toolchain] 4 | channel = "nightly-2023-02-07" 5 | components = [ "rust-src", "rustfmt" ] 6 | targets = [ 7 | "thumbv6m-none-eabi", 8 | "thumbv7em-none-eabi", 9 | "riscv32imc-unknown-none-elf", 10 | "riscv32imac-unknown-none-elf", 11 | "i686-unknown-linux-gnu", 12 | "x86_64-unknown-linux-gnu", 13 | "riscv64gc-unknown-linux-gnu", 14 | ] 15 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![cfg_attr(reexport_core, forbid(unsafe_code))] 3 | #![doc = include_str!("../README.md")] 4 | 5 | #[cfg(reexport_core)] 6 | pub use core::sync::atomic::*; 7 | 8 | #[cfg(not(reexport_core))] 9 | mod polyfill; 10 | #[cfg(not(reexport_core))] 11 | pub use polyfill::*; 12 | -------------------------------------------------------------------------------- /src/polyfill.rs: -------------------------------------------------------------------------------- 1 | pub use core::sync::atomic::{compiler_fence, fence, Ordering}; 2 | 3 | macro_rules! atomic_int { 4 | ($int_type:ident,$atomic_type:ident, $cfg:ident) => { 5 | #[cfg(not($cfg))] 6 | pub use core::sync::atomic::$atomic_type; 7 | 8 | #[cfg($cfg)] 9 | #[repr(transparent)] 10 | pub struct $atomic_type { 11 | inner: core::cell::UnsafeCell<$int_type>, 12 | } 13 | 14 | #[cfg($cfg)] 15 | unsafe impl Send for $atomic_type {} 16 | #[cfg($cfg)] 17 | unsafe impl Sync for $atomic_type {} 18 | #[cfg($cfg)] 19 | impl core::panic::RefUnwindSafe for $atomic_type {} 20 | 21 | #[cfg($cfg)] 22 | impl Default for $atomic_type { 23 | #[inline] 24 | fn default() -> Self { 25 | Self::new(Default::default()) 26 | } 27 | } 28 | 29 | #[cfg($cfg)] 30 | impl From<$int_type> for $atomic_type { 31 | #[inline] 32 | fn from(v: $int_type) -> Self { 33 | Self::new(v) 34 | } 35 | } 36 | 37 | #[cfg($cfg)] 38 | impl core::fmt::Debug for $atomic_type { 39 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 40 | core::fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) 41 | } 42 | } 43 | 44 | #[cfg($cfg)] 45 | impl $atomic_type { 46 | pub const fn new(v: $int_type) -> Self { 47 | Self { 48 | inner: core::cell::UnsafeCell::new(v), 49 | } 50 | } 51 | 52 | pub fn into_inner(self) -> $int_type { 53 | self.inner.into_inner() 54 | } 55 | 56 | pub fn get_mut(&mut self) -> &mut $int_type { 57 | self.inner.get_mut() 58 | } 59 | 60 | pub fn load(&self, _order: Ordering) -> $int_type { 61 | return critical_section::with(|_| unsafe { *self.inner.get() }); 62 | } 63 | 64 | pub fn store(&self, val: $int_type, _order: Ordering) { 65 | return critical_section::with(|_| unsafe { *self.inner.get() = val }); 66 | } 67 | 68 | pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { 69 | self.op(order, |_| val) 70 | } 71 | 72 | pub fn compare_exchange( 73 | &self, 74 | current: $int_type, 75 | new: $int_type, 76 | success: Ordering, 77 | failure: Ordering, 78 | ) -> Result<$int_type, $int_type> { 79 | self.compare_exchange_weak(current, new, success, failure) 80 | } 81 | 82 | pub fn compare_exchange_weak( 83 | &self, 84 | current: $int_type, 85 | new: $int_type, 86 | _success: Ordering, 87 | _failure: Ordering, 88 | ) -> Result<$int_type, $int_type> { 89 | critical_section::with(|_| { 90 | let val = unsafe { &mut *self.inner.get() }; 91 | let old = *val; 92 | if old == current { 93 | *val = new; 94 | Ok(old) 95 | } else { 96 | Err(old) 97 | } 98 | }) 99 | } 100 | 101 | pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { 102 | self.op(order, |old| old.wrapping_add(val)) 103 | } 104 | 105 | pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { 106 | self.op(order, |old| old.wrapping_sub(val)) 107 | } 108 | 109 | pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { 110 | self.op(order, |old| old & val) 111 | } 112 | 113 | pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { 114 | self.op(order, |old| !(old & val)) 115 | } 116 | 117 | pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { 118 | self.op(order, |old| old | val) 119 | } 120 | 121 | pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { 122 | self.op(order, |old| old ^ val) 123 | } 124 | 125 | pub fn fetch_update( 126 | &self, 127 | _set_order: Ordering, 128 | _fetch_order: Ordering, 129 | mut f: F, 130 | ) -> Result<$int_type, $int_type> 131 | where 132 | F: FnMut($int_type) -> Option<$int_type>, 133 | { 134 | critical_section::with(|_| { 135 | let val = unsafe { &mut *self.inner.get() }; 136 | let old = *val; 137 | if let Some(new) = f(old) { 138 | *val = new; 139 | Ok(old) 140 | } else { 141 | Err(old) 142 | } 143 | }) 144 | } 145 | 146 | pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { 147 | self.op(order, |old| old.max(val)) 148 | } 149 | 150 | pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { 151 | self.op(order, |old| old.min(val)) 152 | } 153 | 154 | fn op(&self, _order: Ordering, f: impl FnOnce($int_type) -> $int_type) -> $int_type { 155 | critical_section::with(|_| { 156 | let val = unsafe { &mut *self.inner.get() }; 157 | let old = *val; 158 | *val = f(old); 159 | old 160 | }) 161 | } 162 | } 163 | }; 164 | } 165 | 166 | atomic_int!(u8, AtomicU8, polyfill_u8); 167 | atomic_int!(u16, AtomicU16, polyfill_u16); 168 | atomic_int!(u32, AtomicU32, polyfill_u32); 169 | atomic_int!(u64, AtomicU64, polyfill_u64); 170 | atomic_int!(usize, AtomicUsize, polyfill_usize); 171 | atomic_int!(i8, AtomicI8, polyfill_i8); 172 | atomic_int!(i16, AtomicI16, polyfill_i16); 173 | atomic_int!(i32, AtomicI32, polyfill_i32); 174 | atomic_int!(i64, AtomicI64, polyfill_i64); 175 | atomic_int!(isize, AtomicIsize, polyfill_isize); 176 | 177 | #[cfg(not(polyfill_bool))] 178 | pub use core::sync::atomic::AtomicBool; 179 | 180 | #[cfg(polyfill_bool)] 181 | #[repr(transparent)] 182 | pub struct AtomicBool { 183 | inner: core::cell::UnsafeCell, 184 | } 185 | 186 | #[cfg(polyfill_bool)] 187 | impl Default for AtomicBool { 188 | /// Creates an `AtomicBool` initialized to `false`. 189 | #[inline] 190 | fn default() -> Self { 191 | Self::new(false) 192 | } 193 | } 194 | 195 | #[cfg(polyfill_bool)] 196 | impl From for AtomicBool { 197 | #[inline] 198 | fn from(v: bool) -> Self { 199 | Self::new(v) 200 | } 201 | } 202 | 203 | #[cfg(polyfill_bool)] 204 | impl core::fmt::Debug for AtomicBool { 205 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 206 | core::fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) 207 | } 208 | } 209 | 210 | #[cfg(polyfill_bool)] 211 | unsafe impl Send for AtomicBool {} 212 | #[cfg(polyfill_bool)] 213 | unsafe impl Sync for AtomicBool {} 214 | #[cfg(polyfill_bool)] 215 | impl core::panic::RefUnwindSafe for AtomicBool {} 216 | 217 | #[cfg(polyfill_bool)] 218 | impl AtomicBool { 219 | pub const fn new(v: bool) -> AtomicBool { 220 | Self { 221 | inner: core::cell::UnsafeCell::new(v), 222 | } 223 | } 224 | 225 | pub fn into_inner(self) -> bool { 226 | self.inner.into_inner() 227 | } 228 | 229 | pub fn get_mut(&mut self) -> &mut bool { 230 | self.inner.get_mut() 231 | } 232 | 233 | pub fn load(&self, _order: Ordering) -> bool { 234 | critical_section::with(|_| unsafe { *self.inner.get() }) 235 | } 236 | 237 | pub fn store(&self, val: bool, _order: Ordering) { 238 | critical_section::with(|_| unsafe { *self.inner.get() = val }); 239 | } 240 | 241 | pub fn swap(&self, val: bool, order: Ordering) -> bool { 242 | self.op(order, |_| val) 243 | } 244 | 245 | pub fn compare_exchange( 246 | &self, 247 | current: bool, 248 | new: bool, 249 | success: Ordering, 250 | failure: Ordering, 251 | ) -> Result { 252 | self.compare_exchange_weak(current, new, success, failure) 253 | } 254 | 255 | pub fn compare_exchange_weak( 256 | &self, 257 | current: bool, 258 | new: bool, 259 | _success: Ordering, 260 | _failure: Ordering, 261 | ) -> Result { 262 | critical_section::with(|_| { 263 | let val = unsafe { &mut *self.inner.get() }; 264 | let old = *val; 265 | if old == current { 266 | *val = new; 267 | Ok(old) 268 | } else { 269 | Err(old) 270 | } 271 | }) 272 | } 273 | 274 | pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { 275 | self.op(order, |old| old & val) 276 | } 277 | 278 | pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { 279 | self.op(order, |old| !(old & val)) 280 | } 281 | 282 | pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { 283 | self.op(order, |old| old | val) 284 | } 285 | 286 | pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { 287 | self.op(order, |old| old ^ val) 288 | } 289 | 290 | pub fn fetch_update( 291 | &self, 292 | _set_order: Ordering, 293 | _fetch_order: Ordering, 294 | mut f: F, 295 | ) -> Result 296 | where 297 | F: FnMut(bool) -> Option, 298 | { 299 | critical_section::with(|_| { 300 | let val = unsafe { &mut *self.inner.get() }; 301 | let old = *val; 302 | if let Some(new) = f(old) { 303 | *val = new; 304 | Ok(old) 305 | } else { 306 | Err(old) 307 | } 308 | }) 309 | } 310 | 311 | pub fn fetch_max(&self, val: bool, order: Ordering) -> bool { 312 | self.op(order, |old| old.max(val)) 313 | } 314 | 315 | pub fn fetch_min(&self, val: bool, order: Ordering) -> bool { 316 | self.op(order, |old| old.min(val)) 317 | } 318 | 319 | fn op(&self, _order: Ordering, f: impl FnOnce(bool) -> bool) -> bool { 320 | critical_section::with(|_| { 321 | let val = unsafe { &mut *self.inner.get() }; 322 | let old = *val; 323 | *val = f(old); 324 | old 325 | }) 326 | } 327 | } 328 | 329 | #[cfg(not(polyfill_ptr))] 330 | pub use core::sync::atomic::AtomicPtr; 331 | 332 | #[cfg(polyfill_ptr)] 333 | #[repr(transparent)] 334 | pub struct AtomicPtr { 335 | inner: core::cell::UnsafeCell<*mut T>, 336 | } 337 | 338 | #[cfg(polyfill_ptr)] 339 | impl Default for AtomicPtr { 340 | /// Creates a null `AtomicPtr`. 341 | #[inline] 342 | fn default() -> Self { 343 | Self::new(core::ptr::null_mut()) 344 | } 345 | } 346 | 347 | #[cfg(polyfill_ptr)] 348 | impl From<*mut T> for AtomicPtr { 349 | #[inline] 350 | fn from(v: *mut T) -> Self { 351 | Self::new(v) 352 | } 353 | } 354 | 355 | #[cfg(polyfill_ptr)] 356 | impl core::fmt::Debug for AtomicPtr { 357 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 358 | core::fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) 359 | } 360 | } 361 | 362 | #[cfg(polyfill_ptr)] 363 | impl core::fmt::Pointer for AtomicPtr { 364 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 365 | core::fmt::Pointer::fmt(&self.load(Ordering::SeqCst), f) 366 | } 367 | } 368 | 369 | #[cfg(polyfill_ptr)] 370 | unsafe impl Sync for AtomicPtr {} 371 | #[cfg(polyfill_ptr)] 372 | unsafe impl Send for AtomicPtr {} 373 | #[cfg(polyfill_ptr)] 374 | impl core::panic::RefUnwindSafe for AtomicPtr {} 375 | 376 | #[cfg(polyfill_ptr)] 377 | impl AtomicPtr { 378 | pub const fn new(v: *mut T) -> AtomicPtr { 379 | Self { 380 | inner: core::cell::UnsafeCell::new(v), 381 | } 382 | } 383 | 384 | pub fn into_inner(self) -> *mut T { 385 | self.inner.into_inner() 386 | } 387 | 388 | pub fn get_mut(&mut self) -> &mut *mut T { 389 | self.inner.get_mut() 390 | } 391 | 392 | pub fn load(&self, _order: Ordering) -> *mut T { 393 | critical_section::with(|_| unsafe { *self.inner.get() }) 394 | } 395 | 396 | pub fn store(&self, val: *mut T, _order: Ordering) { 397 | critical_section::with(|_| unsafe { *self.inner.get() = val }); 398 | } 399 | 400 | pub fn swap(&self, val: *mut T, order: Ordering) -> *mut T { 401 | self.op(order, |_| val) 402 | } 403 | 404 | pub fn compare_exchange( 405 | &self, 406 | current: *mut T, 407 | new: *mut T, 408 | success: Ordering, 409 | failure: Ordering, 410 | ) -> Result<*mut T, *mut T> { 411 | self.compare_exchange_weak(current, new, success, failure) 412 | } 413 | 414 | pub fn compare_exchange_weak( 415 | &self, 416 | current: *mut T, 417 | new: *mut T, 418 | _success: Ordering, 419 | _failure: Ordering, 420 | ) -> Result<*mut T, *mut T> { 421 | critical_section::with(|_| { 422 | let val = unsafe { &mut *self.inner.get() }; 423 | let old = *val; 424 | if old == current { 425 | *val = new; 426 | Ok(old) 427 | } else { 428 | Err(old) 429 | } 430 | }) 431 | } 432 | 433 | pub fn fetch_update( 434 | &self, 435 | _set_order: Ordering, 436 | _fetch_order: Ordering, 437 | mut f: F, 438 | ) -> Result<*mut T, *mut T> 439 | where 440 | F: FnMut(*mut T) -> Option<*mut T>, 441 | { 442 | critical_section::with(|_| { 443 | let val = unsafe { &mut *self.inner.get() }; 444 | let old = *val; 445 | if let Some(new) = f(old) { 446 | *val = new; 447 | Ok(old) 448 | } else { 449 | Err(old) 450 | } 451 | }) 452 | } 453 | 454 | fn op(&self, _order: Ordering, f: impl FnOnce(*mut T) -> *mut T) -> *mut T { 455 | critical_section::with(|_| { 456 | let val = unsafe { &mut *self.inner.get() }; 457 | let old = *val; 458 | *val = f(old); 459 | old 460 | }) 461 | } 462 | } 463 | --------------------------------------------------------------------------------