├── .gitignore ├── Cargo.toml ├── LICENSE.APACHE ├── LICENSE.MIT ├── README.md ├── SECURITY.md ├── benches └── basic.rs ├── src └── lib.rs └── tests ├── async.rs └── basic.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atomic_refcell" 3 | version = "0.1.12" 4 | authors = ["Bobby Holley "] 5 | description = "Threadsafe RefCell" 6 | license = "Apache-2.0/MIT" 7 | repository = "https://github.com/bholley/atomic_refcell" 8 | documentation = "https://docs.rs/atomic_refcell/" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | serde = { version = "1.0", optional = true } 13 | 14 | [dev-dependencies] 15 | serde_json = "1.0" 16 | 17 | [features] 18 | default = [] 19 | serde = ["dep:serde"] 20 | -------------------------------------------------------------------------------- /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 | MIT License 2 | 3 | Copyright (c) 2022 Bobby Holley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # atomic_refcell 2 | Threadsafe RefCell for Rust. 3 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Security updates are applied only to the latest release. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. 10 | 11 | Please disclose it at [security advisory](https://github.com/bholley/atomic_refcell/security/advisories/new). 12 | 13 | This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. 14 | -------------------------------------------------------------------------------- /benches/basic.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate atomic_refcell; 4 | extern crate test; 5 | 6 | use atomic_refcell::AtomicRefCell; 7 | use test::Bencher; 8 | 9 | #[derive(Default)] 10 | struct Bar(u32); 11 | 12 | #[bench] 13 | fn immutable_borrow(b: &mut Bencher) { 14 | let a = AtomicRefCell::new(Bar::default()); 15 | b.iter(|| a.borrow()); 16 | } 17 | 18 | #[bench] 19 | fn immutable_second_borrow(b: &mut Bencher) { 20 | let a = AtomicRefCell::new(Bar::default()); 21 | let _first = a.borrow(); 22 | b.iter(|| a.borrow()); 23 | } 24 | 25 | #[bench] 26 | fn immutable_third_borrow(b: &mut Bencher) { 27 | let a = AtomicRefCell::new(Bar::default()); 28 | let _first = a.borrow(); 29 | let _second = a.borrow(); 30 | b.iter(|| a.borrow()); 31 | } 32 | 33 | #[bench] 34 | fn mutable_borrow(b: &mut Bencher) { 35 | let a = AtomicRefCell::new(Bar::default()); 36 | b.iter(|| a.borrow_mut()); 37 | } 38 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Implements a container type providing RefCell-like semantics for objects 2 | //! shared across threads. 3 | //! 4 | //! RwLock is traditionally considered to be the |Sync| analogue of RefCell. 5 | //! However, for consumers that can guarantee that they will never mutably 6 | //! borrow the contents concurrently with immutable borrows, an RwLock is 7 | //! overkill, and has key disadvantages: 8 | //! * Performance: Even the fastest existing implementation of RwLock (that of 9 | //! parking_lot) performs at least two atomic operations during immutable 10 | //! borrows. This makes mutable borrows significantly cheaper than immutable 11 | //! borrows, leading to weird incentives when writing performance-critical 12 | //! code. 13 | //! * Features: Implementing AtomicRefCell on top of RwLock makes it impossible 14 | //! to implement useful things like AtomicRef{,Mut}::map. 15 | //! 16 | //! As such, we re-implement RefCell semantics from scratch with a single atomic 17 | //! reference count. The primary complication of this scheme relates to keeping 18 | //! things in a consistent state when one thread performs an illegal borrow and 19 | //! panics. Since an AtomicRefCell can be accessed by multiple threads, and since 20 | //! panics are recoverable, we need to ensure that an illegal (panicking) access by 21 | //! one thread does not lead to undefined behavior on other, still-running threads. 22 | //! 23 | //! So we represent things as follows: 24 | //! * Any value with the high bit set (so half the total refcount space) indicates 25 | //! a mutable borrow. 26 | //! * Mutable borrows perform an atomic compare-and-swap, swapping in the high bit 27 | //! if the current value is zero. If the current value is non-zero, the thread 28 | //! panics and the value is left undisturbed. 29 | //! * Immutable borrows perform an atomic increment. If the new value has the high 30 | //! bit set, the thread panics. The incremented refcount is left as-is, since it 31 | //! still represents a valid mutable borrow. When the mutable borrow is released, 32 | //! the refcount is set unconditionally to zero, clearing any stray increments by 33 | //! panicked threads. 34 | //! 35 | //! There are a few additional purely-academic complications to handle overflow, 36 | //! which are documented in the implementation. 37 | //! 38 | //! The rest of this module is mostly derived by copy-pasting the implementation of 39 | //! RefCell and fixing things up as appropriate. Certain non-threadsafe methods 40 | //! have been removed. We segment the concurrency logic from the rest of the code to 41 | //! keep the tricky parts small and easy to audit. 42 | 43 | #![no_std] 44 | #![allow(unsafe_code)] 45 | #![deny(missing_docs)] 46 | 47 | use core::cell::UnsafeCell; 48 | use core::cmp; 49 | use core::fmt; 50 | use core::fmt::{Debug, Display}; 51 | use core::marker::PhantomData; 52 | use core::ops::{Deref, DerefMut}; 53 | use core::ptr::NonNull; 54 | use core::sync::atomic; 55 | use core::sync::atomic::AtomicUsize; 56 | 57 | #[cfg(feature = "serde")] 58 | extern crate serde; 59 | #[cfg(feature = "serde")] 60 | use serde::{Deserialize, Serialize}; 61 | 62 | /// A threadsafe analogue to RefCell. 63 | pub struct AtomicRefCell { 64 | borrow: AtomicUsize, 65 | value: UnsafeCell, 66 | } 67 | 68 | /// An error returned by [`AtomicRefCell::try_borrow`](struct.AtomicRefCell.html#method.try_borrow). 69 | pub struct BorrowError { 70 | _private: (), 71 | } 72 | 73 | impl Debug for BorrowError { 74 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 75 | f.debug_struct("BorrowError").finish() 76 | } 77 | } 78 | 79 | impl Display for BorrowError { 80 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 81 | Display::fmt("already mutably borrowed", f) 82 | } 83 | } 84 | 85 | /// An error returned by [`AtomicRefCell::try_borrow_mut`](struct.AtomicRefCell.html#method.try_borrow_mut). 86 | pub struct BorrowMutError { 87 | _private: (), 88 | } 89 | 90 | impl Debug for BorrowMutError { 91 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 92 | f.debug_struct("BorrowMutError").finish() 93 | } 94 | } 95 | 96 | impl Display for BorrowMutError { 97 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 98 | Display::fmt("already borrowed", f) 99 | } 100 | } 101 | 102 | impl AtomicRefCell { 103 | /// Creates a new `AtomicRefCell` containing `value`. 104 | #[inline] 105 | pub const fn new(value: T) -> AtomicRefCell { 106 | AtomicRefCell { 107 | borrow: AtomicUsize::new(0), 108 | value: UnsafeCell::new(value), 109 | } 110 | } 111 | 112 | /// Consumes the `AtomicRefCell`, returning the wrapped value. 113 | #[inline] 114 | pub fn into_inner(self) -> T { 115 | debug_assert!(self.borrow.load(atomic::Ordering::Acquire) == 0); 116 | self.value.into_inner() 117 | } 118 | } 119 | 120 | impl AtomicRefCell { 121 | /// Immutably borrows the wrapped value. 122 | #[inline] 123 | pub fn borrow(&self) -> AtomicRef { 124 | match AtomicBorrowRef::try_new(&self.borrow) { 125 | Ok(borrow) => AtomicRef { 126 | value: unsafe { NonNull::new_unchecked(self.value.get()) }, 127 | borrow, 128 | }, 129 | Err(s) => panic!("{}", s), 130 | } 131 | } 132 | 133 | /// Attempts to immutably borrow the wrapped value, but instead of panicking 134 | /// on a failed borrow, returns `Err`. 135 | #[inline] 136 | pub fn try_borrow(&self) -> Result, BorrowError> { 137 | match AtomicBorrowRef::try_new(&self.borrow) { 138 | Ok(borrow) => Ok(AtomicRef { 139 | value: unsafe { NonNull::new_unchecked(self.value.get()) }, 140 | borrow, 141 | }), 142 | Err(_) => Err(BorrowError { _private: () }), 143 | } 144 | } 145 | 146 | /// Mutably borrows the wrapped value. 147 | #[inline] 148 | pub fn borrow_mut(&self) -> AtomicRefMut { 149 | match AtomicBorrowRefMut::try_new(&self.borrow) { 150 | Ok(borrow) => AtomicRefMut { 151 | value: unsafe { NonNull::new_unchecked(self.value.get()) }, 152 | borrow, 153 | marker: PhantomData, 154 | }, 155 | Err(s) => panic!("{}", s), 156 | } 157 | } 158 | 159 | /// Attempts to mutably borrow the wrapped value, but instead of panicking 160 | /// on a failed borrow, returns `Err`. 161 | #[inline] 162 | pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { 163 | match AtomicBorrowRefMut::try_new(&self.borrow) { 164 | Ok(borrow) => Ok(AtomicRefMut { 165 | value: unsafe { NonNull::new_unchecked(self.value.get()) }, 166 | borrow, 167 | marker: PhantomData, 168 | }), 169 | Err(_) => Err(BorrowMutError { _private: () }), 170 | } 171 | } 172 | 173 | /// Returns a raw pointer to the underlying data in this cell. 174 | /// 175 | /// External synchronization is needed to avoid data races when dereferencing 176 | /// the pointer. 177 | #[inline] 178 | pub fn as_ptr(&self) -> *mut T { 179 | self.value.get() 180 | } 181 | 182 | /// Returns a mutable reference to the wrapped value. 183 | /// 184 | /// No runtime checks take place (unless debug assertions are enabled) 185 | /// because this call borrows `AtomicRefCell` mutably at compile-time. 186 | #[inline] 187 | pub fn get_mut(&mut self) -> &mut T { 188 | debug_assert!(self.borrow.load(atomic::Ordering::Acquire) == 0); 189 | unsafe { &mut *self.value.get() } 190 | } 191 | } 192 | 193 | // 194 | // Core synchronization logic. Keep this section small and easy to audit. 195 | // 196 | 197 | const HIGH_BIT: usize = !(::core::usize::MAX >> 1); 198 | const MAX_FAILED_BORROWS: usize = HIGH_BIT + (HIGH_BIT >> 1); 199 | 200 | struct AtomicBorrowRef<'b> { 201 | borrow: &'b AtomicUsize, 202 | } 203 | 204 | impl<'b> AtomicBorrowRef<'b> { 205 | #[inline] 206 | fn try_new(borrow: &'b AtomicUsize) -> Result { 207 | let new = borrow.fetch_add(1, atomic::Ordering::Acquire) + 1; 208 | if new & HIGH_BIT != 0 { 209 | // If the new count has the high bit set, that almost certainly 210 | // means there's an pre-existing mutable borrow. In that case, 211 | // we simply leave the increment as a benign side-effect and 212 | // return `Err`. Once the mutable borrow is released, the 213 | // count will be reset to zero unconditionally. 214 | // 215 | // The overflow check here ensures that an unbounded number of 216 | // immutable borrows during the scope of one mutable borrow 217 | // will soundly trigger a panic (or abort) rather than UB. 218 | Self::check_overflow(borrow, new); 219 | Err("already mutably borrowed") 220 | } else { 221 | Ok(AtomicBorrowRef { borrow: borrow }) 222 | } 223 | } 224 | 225 | #[cold] 226 | #[inline(never)] 227 | fn check_overflow(borrow: &'b AtomicUsize, new: usize) { 228 | if new == HIGH_BIT { 229 | // We overflowed into the reserved upper half of the refcount 230 | // space. Before panicking, decrement the refcount to leave things 231 | // in a consistent immutable-borrow state. 232 | // 233 | // This can basically only happen if somebody forget()s AtomicRefs 234 | // in a tight loop. 235 | borrow.fetch_sub(1, atomic::Ordering::Release); 236 | panic!("too many immutable borrows"); 237 | } else if new >= MAX_FAILED_BORROWS { 238 | // During the mutable borrow, an absurd number of threads have 239 | // attempted to increment the refcount with immutable borrows. 240 | // To avoid hypothetically wrapping the refcount, we abort the 241 | // process once a certain threshold is reached. 242 | // 243 | // This requires billions of borrows to fail during the scope of 244 | // one mutable borrow, and so is very unlikely to happen in a real 245 | // program. 246 | // 247 | // To avoid a potential unsound state after overflowing, we make 248 | // sure the entire process aborts. 249 | // 250 | // Right now, there's no stable way to do that without `std`: 251 | // https://github.com/rust-lang/rust/issues/67952 252 | // As a workaround, we cause an abort by making this thread panic 253 | // during the unwinding of another panic. 254 | // 255 | // On platforms where the panic strategy is already 'abort', the 256 | // ForceAbort object here has no effect, as the program already 257 | // panics before it is dropped. 258 | struct ForceAbort; 259 | impl Drop for ForceAbort { 260 | fn drop(&mut self) { 261 | panic!("Aborting to avoid unsound state of AtomicRefCell"); 262 | } 263 | } 264 | let _abort = ForceAbort; 265 | panic!("Too many failed borrows"); 266 | } 267 | } 268 | } 269 | 270 | impl<'b> Drop for AtomicBorrowRef<'b> { 271 | #[inline] 272 | fn drop(&mut self) { 273 | let old = self.borrow.fetch_sub(1, atomic::Ordering::Release); 274 | // This assertion is technically incorrect in the case where another 275 | // thread hits the hypothetical overflow case, since we might observe 276 | // the refcount before it fixes it up (and panics). But that never will 277 | // never happen in a real program, and this is a debug_assert! anyway. 278 | debug_assert!(old & HIGH_BIT == 0); 279 | } 280 | } 281 | 282 | struct AtomicBorrowRefMut<'b> { 283 | borrow: &'b AtomicUsize, 284 | } 285 | 286 | impl<'b> Drop for AtomicBorrowRefMut<'b> { 287 | #[inline] 288 | fn drop(&mut self) { 289 | self.borrow.store(0, atomic::Ordering::Release); 290 | } 291 | } 292 | 293 | impl<'b> AtomicBorrowRefMut<'b> { 294 | #[inline] 295 | fn try_new(borrow: &'b AtomicUsize) -> Result, &'static str> { 296 | // Use compare-and-swap to avoid corrupting the immutable borrow count 297 | // on illegal mutable borrows. 298 | let old = match borrow.compare_exchange( 299 | 0, 300 | HIGH_BIT, 301 | atomic::Ordering::Acquire, 302 | atomic::Ordering::Relaxed, 303 | ) { 304 | Ok(x) => x, 305 | Err(x) => x, 306 | }; 307 | 308 | if old == 0 { 309 | Ok(AtomicBorrowRefMut { borrow }) 310 | } else if old & HIGH_BIT == 0 { 311 | Err("already immutably borrowed") 312 | } else { 313 | Err("already mutably borrowed") 314 | } 315 | } 316 | } 317 | 318 | unsafe impl Send for AtomicRefCell {} 319 | unsafe impl Sync for AtomicRefCell {} 320 | 321 | // 322 | // End of core synchronization logic. No tricky thread stuff allowed below 323 | // this point. 324 | // 325 | 326 | impl Clone for AtomicRefCell { 327 | #[inline] 328 | fn clone(&self) -> AtomicRefCell { 329 | AtomicRefCell::new(self.borrow().clone()) 330 | } 331 | } 332 | 333 | impl Default for AtomicRefCell { 334 | #[inline] 335 | fn default() -> AtomicRefCell { 336 | AtomicRefCell::new(Default::default()) 337 | } 338 | } 339 | 340 | impl PartialEq for AtomicRefCell { 341 | #[inline] 342 | fn eq(&self, other: &AtomicRefCell) -> bool { 343 | *self.borrow() == *other.borrow() 344 | } 345 | } 346 | 347 | impl Eq for AtomicRefCell {} 348 | 349 | impl PartialOrd for AtomicRefCell { 350 | #[inline] 351 | fn partial_cmp(&self, other: &AtomicRefCell) -> Option { 352 | self.borrow().partial_cmp(&*other.borrow()) 353 | } 354 | } 355 | 356 | impl Ord for AtomicRefCell { 357 | #[inline] 358 | fn cmp(&self, other: &AtomicRefCell) -> cmp::Ordering { 359 | self.borrow().cmp(&*other.borrow()) 360 | } 361 | } 362 | 363 | impl From for AtomicRefCell { 364 | fn from(t: T) -> AtomicRefCell { 365 | AtomicRefCell::new(t) 366 | } 367 | } 368 | 369 | impl<'b> Clone for AtomicBorrowRef<'b> { 370 | #[inline] 371 | fn clone(&self) -> AtomicBorrowRef<'b> { 372 | AtomicBorrowRef::try_new(self.borrow).unwrap() 373 | } 374 | } 375 | 376 | /// A wrapper type for an immutably borrowed value from an `AtomicRefCell`. 377 | pub struct AtomicRef<'b, T: ?Sized + 'b> { 378 | value: NonNull, 379 | borrow: AtomicBorrowRef<'b>, 380 | } 381 | 382 | // SAFETY: `AtomicRef<'_, T> acts as a reference. `AtomicBorrowRef` is a 383 | // reference to an atomic. 384 | unsafe impl<'b, T: ?Sized> Sync for AtomicRef<'b, T> where for<'a> &'a T: Sync {} 385 | unsafe impl<'b, T: ?Sized> Send for AtomicRef<'b, T> where for<'a> &'a T: Send {} 386 | 387 | impl<'b, T: ?Sized> Deref for AtomicRef<'b, T> { 388 | type Target = T; 389 | 390 | #[inline] 391 | fn deref(&self) -> &T { 392 | // SAFETY: We hold shared borrow of the value. 393 | unsafe { self.value.as_ref() } 394 | } 395 | } 396 | 397 | impl<'b, T: ?Sized> AtomicRef<'b, T> { 398 | /// Copies an `AtomicRef`. 399 | /// 400 | /// Like its [std-counterpart](core::cell::Ref::clone), this type does not implement `Clone` 401 | /// to not interfere with cloning the contained type. 402 | #[inline] 403 | pub fn clone(orig: &AtomicRef<'b, T>) -> AtomicRef<'b, T> { 404 | AtomicRef { 405 | value: orig.value, 406 | borrow: orig.borrow.clone(), 407 | } 408 | } 409 | 410 | /// Make a new `AtomicRef` for a component of the borrowed data. 411 | #[inline] 412 | pub fn map(orig: AtomicRef<'b, T>, f: F) -> AtomicRef<'b, U> 413 | where 414 | F: FnOnce(&T) -> &U, 415 | { 416 | AtomicRef { 417 | value: NonNull::from(f(&*orig)), 418 | borrow: orig.borrow, 419 | } 420 | } 421 | 422 | /// Make a new `AtomicRef` for an optional component of the borrowed data. 423 | #[inline] 424 | pub fn filter_map(orig: AtomicRef<'b, T>, f: F) -> Option> 425 | where 426 | F: FnOnce(&T) -> Option<&U>, 427 | { 428 | Some(AtomicRef { 429 | value: NonNull::from(f(&*orig)?), 430 | borrow: orig.borrow, 431 | }) 432 | } 433 | } 434 | 435 | impl<'b, T: ?Sized> AtomicRefMut<'b, T> { 436 | /// Make a new `AtomicRefMut` for a component of the borrowed data, e.g. an enum 437 | /// variant. 438 | #[inline] 439 | pub fn map(mut orig: AtomicRefMut<'b, T>, f: F) -> AtomicRefMut<'b, U> 440 | where 441 | F: FnOnce(&mut T) -> &mut U, 442 | { 443 | AtomicRefMut { 444 | value: NonNull::from(f(&mut *orig)), 445 | borrow: orig.borrow, 446 | marker: PhantomData, 447 | } 448 | } 449 | 450 | /// Make a new `AtomicRefMut` for an optional component of the borrowed data. 451 | #[inline] 452 | pub fn filter_map( 453 | mut orig: AtomicRefMut<'b, T>, 454 | f: F, 455 | ) -> Option> 456 | where 457 | F: FnOnce(&mut T) -> Option<&mut U>, 458 | { 459 | Some(AtomicRefMut { 460 | value: NonNull::from(f(&mut *orig)?), 461 | borrow: orig.borrow, 462 | marker: PhantomData, 463 | }) 464 | } 465 | } 466 | 467 | /// A wrapper type for a mutably borrowed value from an `AtomicRefCell`. 468 | pub struct AtomicRefMut<'b, T: ?Sized + 'b> { 469 | value: NonNull, 470 | borrow: AtomicBorrowRefMut<'b>, 471 | // `NonNull` is covariant over `T`, but this is used in place of a mutable 472 | // reference so we need to be invariant over `T`. 473 | marker: PhantomData<&'b mut T>, 474 | } 475 | 476 | // SAFETY: `AtomicRefMut<'_, T> acts as a mutable reference. 477 | // `AtomicBorrowRefMut` is a reference to an atomic. 478 | unsafe impl<'b, T: ?Sized> Sync for AtomicRefMut<'b, T> where for<'a> &'a mut T: Sync {} 479 | unsafe impl<'b, T: ?Sized> Send for AtomicRefMut<'b, T> where for<'a> &'a mut T: Send {} 480 | 481 | impl<'b, T: ?Sized> Deref for AtomicRefMut<'b, T> { 482 | type Target = T; 483 | 484 | #[inline] 485 | fn deref(&self) -> &T { 486 | // SAFETY: We hold an exclusive borrow of the value. 487 | unsafe { self.value.as_ref() } 488 | } 489 | } 490 | 491 | impl<'b, T: ?Sized> DerefMut for AtomicRefMut<'b, T> { 492 | #[inline] 493 | fn deref_mut(&mut self) -> &mut T { 494 | // SAFETY: We hold an exclusive borrow of the value. 495 | unsafe { self.value.as_mut() } 496 | } 497 | } 498 | 499 | impl<'b, T: ?Sized + Debug + 'b> Debug for AtomicRef<'b, T> { 500 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 501 | ::fmt(self, f) 502 | } 503 | } 504 | 505 | impl<'b, T: ?Sized + Debug + 'b> Debug for AtomicRefMut<'b, T> { 506 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 507 | ::fmt(self, f) 508 | } 509 | } 510 | 511 | impl Debug for AtomicRefCell { 512 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 513 | match self.try_borrow() { 514 | Ok(borrow) => f.debug_struct("AtomicRefCell").field("value", &borrow).finish(), 515 | Err(_) => { 516 | // The RefCell is mutably borrowed so we can't look at its value 517 | // here. Show a placeholder instead. 518 | struct BorrowedPlaceholder; 519 | 520 | impl Debug for BorrowedPlaceholder { 521 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 522 | f.write_str("") 523 | } 524 | } 525 | 526 | f.debug_struct("AtomicRefCell").field("value", &BorrowedPlaceholder).finish() 527 | } 528 | } 529 | } 530 | } 531 | 532 | #[cfg(feature = "serde")] 533 | impl<'de, T: Deserialize<'de>> Deserialize<'de> for AtomicRefCell { 534 | fn deserialize(deserializer: D) -> Result 535 | where 536 | D: serde::Deserializer<'de>, 537 | { 538 | T::deserialize(deserializer).map(Self::from) 539 | } 540 | } 541 | 542 | #[cfg(feature = "serde")] 543 | impl Serialize for AtomicRefCell { 544 | fn serialize(&self, serializer: S) -> Result 545 | where 546 | S: serde::Serializer, 547 | { 548 | use serde::ser::Error; 549 | match self.try_borrow() { 550 | Ok(value) => value.serialize(serializer), 551 | Err(_err) => Err(S::Error::custom("already mutably borrowed")), 552 | } 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /tests/async.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | 3 | use atomic_refcell::AtomicRefCell; 4 | 5 | fn spawn(_future: Fut) 6 | where 7 | Fut: Future + Send + Sync, 8 | { 9 | } 10 | 11 | async fn something_async() {} 12 | 13 | // see https://github.com/bholley/atomic_refcell/issues/24 14 | #[test] 15 | fn test_atomic_ref_in_spawn() { 16 | let arc: Box usize + Send + Sync> = Box::new(|| 42); 17 | let a = AtomicRefCell::new(arc); 18 | spawn(async move { 19 | let x = a.borrow(); 20 | something_async().await; 21 | assert_eq!(x(), 42); 22 | }); 23 | } 24 | 25 | #[test] 26 | fn test_atomic_ref_mut_in_spawn() { 27 | let arc: Box usize + Send + Sync> = Box::new(|| 42); 28 | let a = AtomicRefCell::new(arc); 29 | spawn(async move { 30 | let x = a.borrow_mut(); 31 | something_async().await; 32 | assert_eq!(x(), 42); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /tests/basic.rs: -------------------------------------------------------------------------------- 1 | extern crate atomic_refcell; 2 | 3 | #[cfg(feature = "serde")] 4 | extern crate serde; 5 | 6 | use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; 7 | 8 | #[derive(Debug)] 9 | struct Foo { 10 | u: u32, 11 | } 12 | 13 | #[derive(Debug)] 14 | struct Bar { 15 | f: Foo, 16 | } 17 | 18 | impl Default for Bar { 19 | fn default() -> Self { 20 | Bar { f: Foo { u: 42 } } 21 | } 22 | } 23 | 24 | // FIXME(bholley): Add tests to exercise this in concurrent scenarios. 25 | 26 | #[test] 27 | fn immutable() { 28 | let a = AtomicRefCell::new(Bar::default()); 29 | let _first = a.borrow(); 30 | let _second = a.borrow(); 31 | } 32 | 33 | #[test] 34 | fn try_immutable() { 35 | let a = AtomicRefCell::new(Bar::default()); 36 | let _first = a.try_borrow().unwrap(); 37 | let _second = a.try_borrow().unwrap(); 38 | } 39 | 40 | #[test] 41 | fn mutable() { 42 | let a = AtomicRefCell::new(Bar::default()); 43 | let _ = a.borrow_mut(); 44 | } 45 | 46 | #[test] 47 | fn try_mutable() { 48 | let a = AtomicRefCell::new(Bar::default()); 49 | let _ = a.try_borrow_mut().unwrap(); 50 | } 51 | 52 | #[test] 53 | fn get_mut() { 54 | let mut a = AtomicRefCell::new(Bar::default()); 55 | let _ = a.get_mut(); 56 | } 57 | 58 | #[test] 59 | fn interleaved() { 60 | let a = AtomicRefCell::new(Bar::default()); 61 | { 62 | let _ = a.borrow_mut(); 63 | } 64 | { 65 | let _first = a.borrow(); 66 | let _second = a.borrow(); 67 | } 68 | { 69 | let _ = a.borrow_mut(); 70 | } 71 | } 72 | 73 | #[test] 74 | fn try_interleaved() { 75 | let a = AtomicRefCell::new(Bar::default()); 76 | { 77 | let _ = a.try_borrow_mut().unwrap(); 78 | } 79 | { 80 | let _first = a.try_borrow().unwrap(); 81 | let _second = a.try_borrow().unwrap(); 82 | let _ = a.try_borrow_mut().unwrap_err(); 83 | } 84 | { 85 | let _first = a.try_borrow_mut().unwrap(); 86 | let _ = a.try_borrow().unwrap_err(); 87 | } 88 | } 89 | 90 | // For Miri to catch issues when calling a function. 91 | // 92 | // See how this scenerio affects std::cell::RefCell implementation: 93 | // https://github.com/rust-lang/rust/issues/63787 94 | // 95 | // Also see relevant unsafe code guidelines issue: 96 | // https://github.com/rust-lang/unsafe-code-guidelines/issues/125 97 | #[test] 98 | fn drop_and_borrow_in_fn_call() { 99 | fn drop_and_borrow(cell: &AtomicRefCell, borrow: AtomicRef<'_, Bar>) { 100 | drop(borrow); 101 | *cell.borrow_mut() = Bar::default(); 102 | } 103 | 104 | let a = AtomicRefCell::new(Bar::default()); 105 | let borrow = a.borrow(); 106 | drop_and_borrow(&a, borrow); 107 | } 108 | 109 | #[test] 110 | #[should_panic(expected = "already immutably borrowed")] 111 | fn immutable_then_mutable() { 112 | let a = AtomicRefCell::new(Bar::default()); 113 | let _first = a.borrow(); 114 | let _second = a.borrow_mut(); 115 | } 116 | 117 | #[test] 118 | fn immutable_then_try_mutable() { 119 | let a = AtomicRefCell::new(Bar::default()); 120 | let _first = a.borrow(); 121 | let _second = a.try_borrow_mut().unwrap_err(); 122 | } 123 | 124 | #[test] 125 | #[should_panic(expected = "already mutably borrowed")] 126 | fn mutable_then_immutable() { 127 | let a = AtomicRefCell::new(Bar::default()); 128 | let _first = a.borrow_mut(); 129 | let _second = a.borrow(); 130 | } 131 | 132 | #[test] 133 | fn mutable_then_try_immutable() { 134 | let a = AtomicRefCell::new(Bar::default()); 135 | let _first = a.borrow_mut(); 136 | let _second = a.try_borrow().unwrap_err(); 137 | } 138 | 139 | #[test] 140 | #[should_panic(expected = "already mutably borrowed")] 141 | fn double_mutable() { 142 | let a = AtomicRefCell::new(Bar::default()); 143 | let _first = a.borrow_mut(); 144 | let _second = a.borrow_mut(); 145 | } 146 | 147 | #[test] 148 | fn mutable_then_try_mutable() { 149 | let a = AtomicRefCell::new(Bar::default()); 150 | let _first = a.borrow_mut(); 151 | let _second = a.try_borrow_mut().unwrap_err(); 152 | } 153 | 154 | #[test] 155 | fn map() { 156 | let a = AtomicRefCell::new(Bar::default()); 157 | let b = a.borrow(); 158 | assert_eq!(b.f.u, 42); 159 | let c = AtomicRef::map(b, |x| &x.f); 160 | assert_eq!(c.u, 42); 161 | let d = AtomicRef::map(c, |x| &x.u); 162 | assert_eq!(*d, 42); 163 | } 164 | 165 | #[test] 166 | fn map_mut() { 167 | let a = AtomicRefCell::new(Bar::default()); 168 | let mut b = a.borrow_mut(); 169 | assert_eq!(b.f.u, 42); 170 | b.f.u = 43; 171 | let mut c = AtomicRefMut::map(b, |x| &mut x.f); 172 | assert_eq!(c.u, 43); 173 | c.u = 44; 174 | let mut d = AtomicRefMut::map(c, |x| &mut x.u); 175 | assert_eq!(*d, 44); 176 | *d = 45; 177 | assert_eq!(*d, 45); 178 | } 179 | 180 | #[test] 181 | fn debug_fmt() { 182 | let a = AtomicRefCell::new(Foo { u: 42 }); 183 | assert_eq!(format!("{:?}", a), "AtomicRefCell { value: Foo { u: 42 } }"); 184 | } 185 | 186 | #[test] 187 | fn debug_fmt_borrowed() { 188 | let a = AtomicRefCell::new(Foo { u: 42 }); 189 | let _b = a.borrow(); 190 | assert_eq!(format!("{:?}", a), "AtomicRefCell { value: Foo { u: 42 } }"); 191 | } 192 | 193 | #[test] 194 | fn debug_fmt_borrowed_mut() { 195 | let a = AtomicRefCell::new(Foo { u: 42 }); 196 | let _b = a.borrow_mut(); 197 | assert_eq!(format!("{:?}", a), "AtomicRefCell { value: }"); 198 | } 199 | 200 | #[test] 201 | #[cfg(feature = "serde")] 202 | fn serde() { 203 | let value = 10; 204 | let cell = AtomicRefCell::new(value); 205 | 206 | let serialized = serde_json::to_string(&cell).unwrap(); 207 | let deserialized = serde_json::from_str::>(&serialized).unwrap(); 208 | 209 | assert_eq!(*deserialized.borrow(), value); 210 | } 211 | --------------------------------------------------------------------------------