diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/scopeguard | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/scopeguard')
-rw-r--r-- | third_party/rust/scopeguard/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/scopeguard/Cargo.lock | 5 | ||||
-rw-r--r-- | third_party/rust/scopeguard/Cargo.toml | 28 | ||||
-rw-r--r-- | third_party/rust/scopeguard/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/scopeguard/LICENSE-MIT | 25 | ||||
-rw-r--r-- | third_party/rust/scopeguard/README.rst | 103 | ||||
-rw-r--r-- | third_party/rust/scopeguard/examples/readme.rs | 27 | ||||
-rw-r--r-- | third_party/rust/scopeguard/src/lib.rs | 578 |
8 files changed, 968 insertions, 0 deletions
diff --git a/third_party/rust/scopeguard/.cargo-checksum.json b/third_party/rust/scopeguard/.cargo-checksum.json new file mode 100644 index 0000000000..cf78ae1218 --- /dev/null +++ b/third_party/rust/scopeguard/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"6fdc13a4ac088ef8f084acdd4b60829c154e4064516d0bb9729e05fa521a756b","Cargo.toml":"9e3bdbf6b410b9d82c84d60249a4e3ee7dc9a95e2ee41221da2fbd627214b030","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7","README.rst":"67d8fa211a9c7d4b856ebcf822318ae5ae0192f3bd141d8b608677010ea7842f","examples/readme.rs":"d00fe19aecd7ca1632bcf176306f7a13ed8fdefa890761aa2c532f8c97532a33","src/lib.rs":"29a60ae468881edbdebc30e8f4c87101ede6e7e669482419b1c52a35e4c7f5ed"},"package":"d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"}
\ No newline at end of file diff --git a/third_party/rust/scopeguard/Cargo.lock b/third_party/rust/scopeguard/Cargo.lock new file mode 100644 index 0000000000..c8614f08f5 --- /dev/null +++ b/third_party/rust/scopeguard/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "scopeguard" +version = "1.1.0" diff --git a/third_party/rust/scopeguard/Cargo.toml b/third_party/rust/scopeguard/Cargo.toml new file mode 100644 index 0000000000..9a5b836cfe --- /dev/null +++ b/third_party/rust/scopeguard/Cargo.toml @@ -0,0 +1,28 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "scopeguard" +version = "1.1.0" +authors = ["bluss"] +description = "A RAII scope guard that will run a given closure when it goes out of scope,\neven if the code between panics (assuming unwinding panic).\n\nDefines the macros `defer!`, `defer_on_unwind!`, `defer_on_success!` as\nshorthands for guards with one of the implemented strategies.\n" +documentation = "https://docs.rs/scopeguard/" +keywords = ["scope-guard", "defer", "panic", "unwind"] +categories = ["rust-patterns", "no-std"] +license = "MIT/Apache-2.0" +repository = "https://github.com/bluss/scopeguard" +[package.metadata.release] +no-dev-version = true + +[features] +default = ["use_std"] +use_std = [] diff --git a/third_party/rust/scopeguard/LICENSE-APACHE b/third_party/rust/scopeguard/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/scopeguard/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/scopeguard/LICENSE-MIT b/third_party/rust/scopeguard/LICENSE-MIT new file mode 100644 index 0000000000..8514471338 --- /dev/null +++ b/third_party/rust/scopeguard/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016-2019 Ulrik Sverdrup "bluss" and scopeguard developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/scopeguard/README.rst b/third_party/rust/scopeguard/README.rst new file mode 100644 index 0000000000..019ffe7a44 --- /dev/null +++ b/third_party/rust/scopeguard/README.rst @@ -0,0 +1,103 @@ + +scopeguard +========== + +Rust crate for a convenient RAII scope guard that will run a given closure when +it goes out of scope, even if the code between panics (assuming unwinding panic). + +The `defer!` macro and `guard` are `no_std` compatible (require only core), +but the on unwinding / not on uwinding strategies requires linking to `std`. + +Requires Rust 1.20. + + +Please read the `API documentation here`__ + +__ https://docs.rs/scopeguard/ + +|build_status|_ |crates|_ + +.. |build_status| image:: https://travis-ci.org/bluss/scopeguard.svg +.. _build_status: https://travis-ci.org/bluss/scopeguard + +.. |crates| image:: http://meritbadge.herokuapp.com/scopeguard +.. _crates: https://crates.io/crates/scopeguard + +How to use +---------- + +.. code:: rust + + #[macro_use(defer)] extern crate scopeguard; + + use scopeguard::guard; + + fn f() { + defer!(println!("Called at return or panic")); + panic!(); + } + + use std::fs::File; + use std::io::Write; + + fn g() { + let f = File::create("newfile.txt").unwrap(); + let mut file = guard(f, |f| { + // write file at return or panic + let _ = f.sync_all(); + }); + // Access the file through the scope guard itself + file.write_all(b"test me\n").unwrap(); + } + +Recent Changes +-------------- + +- 1.1.0 + + - Change macros (``defer!``, ``defer_on_success!`` and ``defer_on_unwind!``) + to accept statements. (by @konsumlamm) + +- 1.0.0 + + - Change the closure type from ``FnMut(&mut T)`` to ``FnOnce(T)``: + Passing the inner value by value instead of a mutable reference is a + breaking change, but allows the guard closure to consume it. (by @tormol) + + - Add ``defer_on_success!{}``, ``guard_on_success()`` and ``OnSuccess`` + strategy, which triggers when scope is exited *without* panic. It's the + opposite to ``OnUnwind`` / ``guard_on_unwind()`` / ``defer_on_unwind!{}``. + + - Add ``ScopeGuard::into_inner()``, which "defuses" the guard and returns the + guarded value. (by @tormol) + + - Implement ``Sync`` for guards with non-``Sync`` closures. + + - Require Rust 1.20 + +- 0.3.3 + + - Use ``#[inline]`` on a few more functions by @stjepang (#14) + - Add examples to crate documentation + +- 0.3.2 + + - Add crate categories + +- 0.3.1 + + - Add ``defer_on_unwind!``, ``Strategy`` trait + - Rename ``Guard`` → ``ScopeGuard`` + - Add ``ScopeGuard::with_strategy``. + - ``ScopeGuard`` now implements ``Debug``. + - Require Rust 1.11 + +- 0.2.0 + + - Require Rust 1.6 + - Use `no_std` unconditionally + - No other changes + +- 0.1.2 + + - Add macro ``defer!()`` diff --git a/third_party/rust/scopeguard/examples/readme.rs b/third_party/rust/scopeguard/examples/readme.rs new file mode 100644 index 0000000000..0710f9f5a1 --- /dev/null +++ b/third_party/rust/scopeguard/examples/readme.rs @@ -0,0 +1,27 @@ + +#[macro_use(defer)] extern crate scopeguard; + +use scopeguard::guard; + +fn f() { + defer!(println!("Called at return or panic")); + panic!(); +} + +use std::fs::File; +use std::io::Write; + +fn g() { + let f = File::create("newfile.txt").unwrap(); + let mut file = guard(f, |f| { + // write file at return or panic + let _ = f.sync_all(); + }); + // Access the file through the scope guard itself + file.write_all(b"test me\n").unwrap(); +} + +fn main() { + f(); + g(); +} diff --git a/third_party/rust/scopeguard/src/lib.rs b/third_party/rust/scopeguard/src/lib.rs new file mode 100644 index 0000000000..d33c2b69c8 --- /dev/null +++ b/third_party/rust/scopeguard/src/lib.rs @@ -0,0 +1,578 @@ +#![cfg_attr(not(any(test, feature = "use_std")), no_std)] +#![doc(html_root_url = "https://docs.rs/scopeguard/1/")] + +//! A scope guard will run a given closure when it goes out of scope, +//! even if the code between panics. +//! (as long as panic doesn't abort) +//! +//! # Examples +//! +//! ## Hello World +//! +//! This example creates a scope guard with an example function: +//! +//! ``` +//! extern crate scopeguard; +//! +//! fn f() { +//! let _guard = scopeguard::guard((), |_| { +//! println!("Hello Scope Exit!"); +//! }); +//! +//! // rest of the code here. +//! +//! // Here, at the end of `_guard`'s scope, the guard's closure is called. +//! // It is also called if we exit this scope through unwinding instead. +//! } +//! # fn main() { +//! # f(); +//! # } +//! ``` +//! +//! ## `defer!` +//! +//! Use the `defer` macro to run an operation at scope exit, +//! either regular scope exit or during unwinding from a panic. +//! +//! ``` +//! #[macro_use(defer)] extern crate scopeguard; +//! +//! use std::cell::Cell; +//! +//! fn main() { +//! // use a cell to observe drops during and after the scope guard is active +//! let drop_counter = Cell::new(0); +//! { +//! // Create a scope guard using `defer!` for the current scope +//! defer! { +//! drop_counter.set(1 + drop_counter.get()); +//! } +//! +//! // Do regular operations here in the meantime. +//! +//! // Just before scope exit: it hasn't run yet. +//! assert_eq!(drop_counter.get(), 0); +//! +//! // The following scope end is where the defer closure is called +//! } +//! assert_eq!(drop_counter.get(), 1); +//! } +//! ``` +//! +//! ## Scope Guard with Value +//! +//! If the scope guard closure needs to access an outer value that is also +//! mutated outside of the scope guard, then you may want to use the scope guard +//! with a value. The guard works like a smart pointer, so the inner value can +//! be accessed by reference or by mutable reference. +//! +//! ### 1. The guard owns a file +//! +//! In this example, the scope guard owns a file and ensures pending writes are +//! synced at scope exit. +//! +//! ``` +//! extern crate scopeguard; +//! +//! use std::fs::*; +//! use std::io::{self, Write}; +//! # // Mock file so that we don't actually write a file +//! # struct MockFile; +//! # impl MockFile { +//! # fn create(_s: &str) -> io::Result<Self> { Ok(MockFile) } +//! # fn write_all(&self, _b: &[u8]) -> io::Result<()> { Ok(()) } +//! # fn sync_all(&self) -> io::Result<()> { Ok(()) } +//! # } +//! # use self::MockFile as File; +//! +//! fn try_main() -> io::Result<()> { +//! let f = File::create("newfile.txt")?; +//! let mut file = scopeguard::guard(f, |f| { +//! // ensure we flush file at return or panic +//! let _ = f.sync_all(); +//! }); +//! // Access the file through the scope guard itself +//! file.write_all(b"test me\n").map(|_| ()) +//! } +//! +//! fn main() { +//! try_main().unwrap(); +//! } +//! +//! ``` +//! +//! ### 2. The guard restores an invariant on scope exit +//! +//! ``` +//! extern crate scopeguard; +//! +//! use std::mem::ManuallyDrop; +//! use std::ptr; +//! +//! // This function, just for this example, takes the first element +//! // and inserts it into the assumed sorted tail of the vector. +//! // +//! // For optimization purposes we temporarily violate an invariant of the +//! // Vec, that it owns all of its elements. +//! // +//! // The safe approach is to use swap, which means two writes to memory, +//! // the optimization is to use a “hole” which uses only one write of memory +//! // for each position it moves. +//! // +//! // We *must* use a scope guard to run this code safely. We +//! // are running arbitrary user code (comparison operators) that may panic. +//! // The scope guard ensures we restore the invariant after successful +//! // exit or during unwinding from panic. +//! fn insertion_sort_first<T>(v: &mut Vec<T>) +//! where T: PartialOrd +//! { +//! struct Hole<'a, T: 'a> { +//! v: &'a mut Vec<T>, +//! index: usize, +//! value: ManuallyDrop<T>, +//! } +//! +//! unsafe { +//! // Create a moved-from location in the vector, a “hole”. +//! let value = ptr::read(&v[0]); +//! let mut hole = Hole { v: v, index: 0, value: ManuallyDrop::new(value) }; +//! +//! // Use a scope guard with a value. +//! // At scope exit, plug the hole so that the vector is fully +//! // initialized again. +//! // The scope guard owns the hole, but we can access it through the guard. +//! let mut hole_guard = scopeguard::guard(hole, |hole| { +//! // plug the hole in the vector with the value that was // taken out +//! let index = hole.index; +//! ptr::copy_nonoverlapping(&*hole.value, &mut hole.v[index], 1); +//! }); +//! +//! // run algorithm that moves the hole in the vector here +//! // move the hole until it's in a sorted position +//! for i in 1..hole_guard.v.len() { +//! if *hole_guard.value >= hole_guard.v[i] { +//! // move the element back and the hole forward +//! let index = hole_guard.index; +//! ptr::copy_nonoverlapping(&hole_guard.v[index + 1], &mut hole_guard.v[index], 1); +//! hole_guard.index += 1; +//! } else { +//! break; +//! } +//! } +//! +//! // When the scope exits here, the Vec becomes whole again! +//! } +//! } +//! +//! fn main() { +//! let string = String::from; +//! let mut data = vec![string("c"), string("a"), string("b"), string("d")]; +//! insertion_sort_first(&mut data); +//! assert_eq!(data, vec!["a", "b", "c", "d"]); +//! } +//! +//! ``` +//! +//! +//! # Crate Features +//! +//! - `use_std` +//! + Enabled by default. Enables the `OnUnwind` and `OnSuccess` strategies. +//! + Disable to use `no_std`. +//! +//! # Rust Version +//! +//! This version of the crate requires Rust 1.20 or later. +//! +//! The scopeguard 1.x release series will use a carefully considered version +//! upgrade policy, where in a later 1.x version, we will raise the minimum +//! required Rust version. + +#[cfg(not(any(test, feature = "use_std")))] +extern crate core as std; + +use std::fmt; +use std::marker::PhantomData; +use std::mem::{self, ManuallyDrop}; +use std::ops::{Deref, DerefMut}; +use std::ptr; + +/// Controls in which cases the associated code should be run +pub trait Strategy { + /// Return `true` if the guard’s associated code should run + /// (in the context where this method is called). + fn should_run() -> bool; +} + +/// Always run on scope exit. +/// +/// “Always” run: on regular exit from a scope or on unwinding from a panic. +/// Can not run on abort, process exit, and other catastrophic events where +/// destructors don’t run. +#[derive(Debug)] +pub enum Always {} + +/// Run on scope exit through unwinding. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[derive(Debug)] +pub enum OnUnwind {} + +/// Run on regular scope exit, when not unwinding. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[derive(Debug)] +pub enum OnSuccess {} + +impl Strategy for Always { + #[inline(always)] + fn should_run() -> bool { true } +} + +#[cfg(feature = "use_std")] +impl Strategy for OnUnwind { + #[inline] + fn should_run() -> bool { std::thread::panicking() } +} + +#[cfg(feature = "use_std")] +impl Strategy for OnSuccess { + #[inline] + fn should_run() -> bool { !std::thread::panicking() } +} + +/// Macro to create a `ScopeGuard` (always run). +/// +/// The macro takes statements, which are the body of a closure +/// that will run when the scope is exited. +#[macro_export] +macro_rules! defer { + ($($t:tt)*) => { + let _guard = $crate::guard((), |()| { $($t)* }); + }; +} + +/// Macro to create a `ScopeGuard` (run on successful scope exit). +/// +/// The macro takes statements, which are the body of a closure +/// that will run when the scope is exited. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[macro_export] +macro_rules! defer_on_success { + ($($t:tt)*) => { + let _guard = $crate::guard_on_success((), |()| { $($t)* }); + }; +} + +/// Macro to create a `ScopeGuard` (run on unwinding from panic). +/// +/// The macro takes statements, which are the body of a closure +/// that will run when the scope is exited. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[macro_export] +macro_rules! defer_on_unwind { + ($($t:tt)*) => { + let _guard = $crate::guard_on_unwind((), |()| { $($t)* }); + }; +} + +/// `ScopeGuard` is a scope guard that may own a protected value. +/// +/// If you place a guard in a local variable, the closure can +/// run regardless how you leave the scope — through regular return or panic +/// (except if panic or other code aborts; so as long as destructors run). +/// It is run only once. +/// +/// The `S` parameter for [`Strategy`](trait.Strategy.html) determines if +/// the closure actually runs. +/// +/// The guard's closure will be called with the held value in the destructor. +/// +/// The `ScopeGuard` implements `Deref` so that you can access the inner value. +pub struct ScopeGuard<T, F, S = Always> + where F: FnOnce(T), + S: Strategy, +{ + value: ManuallyDrop<T>, + dropfn: ManuallyDrop<F>, + // fn(S) -> S is used, so that the S is not taken into account for auto traits. + strategy: PhantomData<fn(S) -> S>, +} + +impl<T, F, S> ScopeGuard<T, F, S> + where F: FnOnce(T), + S: Strategy, +{ + /// Create a `ScopeGuard` that owns `v` (accessible through deref) and calls + /// `dropfn` when its destructor runs. + /// + /// The `Strategy` decides whether the scope guard's closure should run. + #[inline] + pub fn with_strategy(v: T, dropfn: F) -> ScopeGuard<T, F, S> { + ScopeGuard { + value: ManuallyDrop::new(v), + dropfn: ManuallyDrop::new(dropfn), + strategy: PhantomData, + } + } + + /// “Defuse” the guard and extract the value without calling the closure. + /// + /// ``` + /// extern crate scopeguard; + /// + /// use scopeguard::{guard, ScopeGuard}; + /// + /// fn conditional() -> bool { true } + /// + /// fn main() { + /// let mut guard = guard(Vec::new(), |mut v| v.clear()); + /// guard.push(1); + /// + /// if conditional() { + /// // a condition maybe makes us decide to + /// // “defuse” the guard and get back its inner parts + /// let value = ScopeGuard::into_inner(guard); + /// } else { + /// // guard still exists in this branch + /// } + /// } + /// ``` + #[inline] + pub fn into_inner(guard: Self) -> T { + // Cannot move out of Drop-implementing types, so + // ptr::read the value and forget the guard. + unsafe { + let value = ptr::read(&*guard.value); + // read the closure so that it is dropped, and assign it to a local + // variable to ensure that it is only dropped after the guard has + // been forgotten. (In case the Drop impl of the closure, or that + // of any consumed captured variable, panics). + let _dropfn = ptr::read(&*guard.dropfn); + mem::forget(guard); + value + } + } +} + + +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +#[inline] +pub fn guard<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, Always> + where F: FnOnce(T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[inline] +pub fn guard_on_success<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, OnSuccess> + where F: FnOnce(T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +/// +/// Requires crate feature `use_std`. +/// +/// ## Examples +/// +/// For performance reasons, or to emulate “only run guard on unwind” in +/// no-std environments, we can also use the default guard and simply manually +/// defuse it at the end of scope like the following example. (The performance +/// reason would be if the [`OnUnwind`]'s call to [std::thread::panicking()] is +/// an issue.) +/// +/// ``` +/// extern crate scopeguard; +/// +/// use scopeguard::ScopeGuard; +/// # fn main() { +/// { +/// let guard = scopeguard::guard((), |_| {}); +/// +/// // rest of the code here +/// +/// // we reached the end of scope without unwinding - defuse it +/// ScopeGuard::into_inner(guard); +/// } +/// # } +/// ``` +#[cfg(feature = "use_std")] +#[inline] +pub fn guard_on_unwind<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, OnUnwind> + where F: FnOnce(T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +// ScopeGuard can be Sync even if F isn't because the closure is +// not accessible from references. +// The guard does not store any instance of S, so it is also irrelevant. +unsafe impl<T, F, S> Sync for ScopeGuard<T, F, S> + where T: Sync, + F: FnOnce(T), + S: Strategy +{} + +impl<T, F, S> Deref for ScopeGuard<T, F, S> + where F: FnOnce(T), + S: Strategy +{ + type Target = T; + + fn deref(&self) -> &T { + &*self.value + } +} + +impl<T, F, S> DerefMut for ScopeGuard<T, F, S> + where F: FnOnce(T), + S: Strategy +{ + fn deref_mut(&mut self) -> &mut T { + &mut *self.value + } +} + +impl<T, F, S> Drop for ScopeGuard<T, F, S> + where F: FnOnce(T), + S: Strategy +{ + fn drop(&mut self) { + // This is OK because the fields are `ManuallyDrop`s + // which will not be dropped by the compiler. + let (value, dropfn) = unsafe { + (ptr::read(&*self.value), ptr::read(&*self.dropfn)) + }; + if S::should_run() { + dropfn(value); + } + } +} + +impl<T, F, S> fmt::Debug for ScopeGuard<T, F, S> + where T: fmt::Debug, + F: FnOnce(T), + S: Strategy +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct(stringify!(ScopeGuard)) + .field("value", &*self.value) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cell::Cell; + use std::panic::catch_unwind; + use std::panic::AssertUnwindSafe; + + #[test] + fn test_defer() { + let drops = Cell::new(0); + defer!(drops.set(1000)); + assert_eq!(drops.get(), 0); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_defer_success_1() { + let drops = Cell::new(0); + { + defer_on_success!(drops.set(1)); + assert_eq!(drops.get(), 0); + } + assert_eq!(drops.get(), 1); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_defer_success_2() { + let drops = Cell::new(0); + let _ = catch_unwind(AssertUnwindSafe(|| { + defer_on_success!(drops.set(1)); + panic!("failure") + })); + assert_eq!(drops.get(), 0); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_defer_unwind_1() { + let drops = Cell::new(0); + let _ = catch_unwind(AssertUnwindSafe(|| { + defer_on_unwind!(drops.set(1)); + assert_eq!(drops.get(), 0); + panic!("failure") + })); + assert_eq!(drops.get(), 1); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_defer_unwind_2() { + let drops = Cell::new(0); + { + defer_on_unwind!(drops.set(1)); + } + assert_eq!(drops.get(), 0); + } + + #[test] + fn test_only_dropped_by_closure_when_run() { + let value_drops = Cell::new(0); + let value = guard((), |()| value_drops.set(1 + value_drops.get())); + let closure_drops = Cell::new(0); + let guard = guard(value, |_| closure_drops.set(1 + closure_drops.get())); + assert_eq!(value_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(closure_drops.get(), 1); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_dropped_once_when_not_run() { + let value_drops = Cell::new(0); + let value = guard((), |()| value_drops.set(1 + value_drops.get())); + let captured_drops = Cell::new(0); + let captured = guard((), |()| captured_drops.set(1 + captured_drops.get())); + let closure_drops = Cell::new(0); + let guard = guard_on_unwind(value, |value| { + drop(value); + drop(captured); + closure_drops.set(1 + closure_drops.get()) + }); + assert_eq!(value_drops.get(), 0); + assert_eq!(captured_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(captured_drops.get(), 1); + assert_eq!(closure_drops.get(), 0); + } + + #[test] + fn test_into_inner() { + let dropped = Cell::new(false); + let value = guard(42, |_| dropped.set(true)); + let guard = guard(value, |_| dropped.set(true)); + let inner = ScopeGuard::into_inner(guard); + assert_eq!(dropped.get(), false); + assert_eq!(*inner, 42); + } +} |