profile
viewpoint
Frank Steffahn steffahn Kiel, Germany

steffahn/haskell-scheme 0

Writing Scheme in Haskell

steffahn/reference 0

The Rust Reference

steffahn/replace_with 0

Temporarily take ownership of a value at a mutable location, and replace it with a new value based on the old one.

steffahn/rust 0

Empowering everyone to build reliable and efficient software.

steffahn/rust-feature-ideas 0

A place where I can take notes about ideas I have for improving the rust programming language.

steffahn/TabTastic 0

Striving to archieve fantastic user experience when editing code.

steffahn/tokio 0

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...

pull request commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

Thanks a lot for the PR. Sorry for my late review.

The reasoning for adding this method makes sense and I'm on board merging it unstably. However, I strongly agree that the name still has to change. As @danielhenrymantilla said here, it's confusing that "unwrap" and "drop" in the name don't refer to the same thing. I think into_inner is a pretty good name. Whether we rename/deprecate try_unwrap is another question.

Regarding the implementation: I checked it briefly and couldn't find any problems. However, this is code using relaxed atomics and I don't feel comfortable approving this kind of addition. I don't have experience with it and generally, it's a hard topic and the standard library was always very conservative with these kinds of changes. However, here, this is mostly just a copy of existing code and it seems to make sense to me. (And yes, I might or might not have spent the last 4 hours trying to understand Arc::drop.) So before merging this, I will probably ping the team to have a few more eyes on this.

Lastly, I left a few inline comments.

Thanks for the review so far. I’m thinking into_inner is pretty good myself, too. I’m not sure if renaming try_unwrap is even necessary; I think I was thinking about that these functions must be named similarly for reasons like discoverability and cleanness. But super clean names are hard/almost impossibly anyways and discoverability is good now that I added a link to unwrap_or_drop to try_unwrap’s documentation. So I guess we could just live with try_unwrap + into_inner for now. What I also like about into_inner is that it is a shorter and more familiar name, suggesting that this operation isn’t obscure but instead exactly what you canonically want to do when getting rid of an Arc whilst caring about getting the inner value (if possible). And also there is no useful operation of type Arc<T> -> T (the common type of an into_inner method) that I can think of.

<hr>

I would be curious to get your opinion on this, too:

Since this is an issue in multi-threaded settings only, a similar function on Rc is not strictly necessary but could be wanted nontheless for ergonomic and API-similarity reasons. (This PR currently only offers the function on Arc, but I could add one for Rc if wanted.)

[go to comment]

and

“In order to get thread safety, we have to use Arc. [...] All we need to do [...] is replace every reference to Rc with std::sync::Arc. That's it. We're thread safe. Done!”

[…] They do not seem to notice that by replacing Rc with Arc, they are introducing a race condition themselves that can cause a stack overflow.

This example can of course be solved by unwrap_or_drop. Since it comes from starting out with Rc and only migrating the code to Arc afterwards, it convinces me that Rc should get a version of unwrap_or_drop, too.

[go to comment]

steffahn

comment created time in 9 days

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer such a guarantee. See the last example below and the documentation+    /// of [`try_unwrap`][`Arc::try_unwrap`].+    ///+    /// # Examples+    ///+    /// Minimal example demonstrating the guarantee that `unwrap_or_drop` gives.+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = Arc::clone(&x);+    ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:+    /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));+    /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));+    ///+    /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();+    /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();+    ///+    /// // One of the threads is guaranteed to receive the inner value:+    /// assert!(matches!(+    ///     (x_unwrapped_value, y_unwrapped_value),+    ///     (None, Some(3)) | (Some(3), None)+    /// ));+    /// // The result could also be `(None, None)` if the threads called+    /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.+    /// ```+    ///+    /// A more practical example demonstrating the need for `unwrap_or_drop`:+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// // Definition of a simple singly linked list using `Arc`:+    /// #[derive(Clone)]+    /// struct LinkedList<T>(Option<Arc<Node<T>>>);+    /// struct Node<T>(T, Option<Arc<Node<T>>>);+    ///+    /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`+    /// // can cause a stack overflow. To prevent this, we can provide a+    /// // manual `Drop` implementation that does the destruction in a loop:+    /// impl<T> Drop for LinkedList<T> {+    ///     fn drop(&mut self) {+    ///         let mut x = self.0.take();+    ///         while let Some(arc) = x.take() {+    ///             Arc::unwrap_or_drop(arc).map(|node| x = node.1);+    ///         }+    ///     }+    /// }+    ///+    /// // implementation of `new` and `push` omitted+    /// impl<T> LinkedList<T> {+    ///     /* ... */+    /// #   fn new() -> Self {+    /// #       LinkedList(None)+    /// #   }+    /// #   fn push(&mut self, x: T) {+    /// #       self.0 = Some(Arc::new(Node(x, self.0.take())));+    /// #   }+    /// }+    ///+    /// // The following code could still cause a stack overflow+    /// // despite the manual `Drop` impl if that `Drop` impl used+    /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::unwrap_or_drop(arc)`.+    /// {+    ///     // create a long list and clone it+    ///     let mut x = LinkedList::new();+    ///     for i in 0..100000 {+    ///         x.push(i); // adds i to the front of x+    ///     }+    ///     let y = x.clone();+    ///+    ///     // drop the clones in parallel+    ///     let t1 = std::thread::spawn(|| drop(x));+    ///     let t2 = std::thread::spawn(|| drop(y));+    ///     t1.join().unwrap();+    ///     t2.join().unwrap();+    /// }+    /// ```+    #[inline]+    #[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue+    // FIXME: should this copy all/some of the comments from drop and drop_slow?+    pub fn unwrap_or_drop(this: Self) -> Option<T> {+        // following the implementation of `drop` (and `drop_slow`)

I’ll still have to rework the comments a bit anyways, as suggested above. But thanks for the info anyways.

steffahn

comment created time in 10 days

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer such a guarantee. See the last example below and the documentation+    /// of [`try_unwrap`][`Arc::try_unwrap`].+    ///+    /// # Examples+    ///+    /// Minimal example demonstrating the guarantee that `unwrap_or_drop` gives.+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = Arc::clone(&x);+    ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:+    /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));+    /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));+    ///+    /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();+    /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();+    ///+    /// // One of the threads is guaranteed to receive the inner value:+    /// assert!(matches!(+    ///     (x_unwrapped_value, y_unwrapped_value),+    ///     (None, Some(3)) | (Some(3), None)+    /// ));+    /// // The result could also be `(None, None)` if the threads called+    /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.+    /// ```+    ///+    /// A more practical example demonstrating the need for `unwrap_or_drop`:+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// // Definition of a simple singly linked list using `Arc`:+    /// #[derive(Clone)]+    /// struct LinkedList<T>(Option<Arc<Node<T>>>);+    /// struct Node<T>(T, Option<Arc<Node<T>>>);+    ///+    /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`+    /// // can cause a stack overflow. To prevent this, we can provide a+    /// // manual `Drop` implementation that does the destruction in a loop:+    /// impl<T> Drop for LinkedList<T> {+    ///     fn drop(&mut self) {+    ///         let mut x = self.0.take();+    ///         while let Some(arc) = x.take() {+    ///             Arc::unwrap_or_drop(arc).map(|node| x = node.1);+    ///         }+    ///     }+    /// }+    ///+    /// // implementation of `new` and `push` omitted+    /// impl<T> LinkedList<T> {+    ///     /* ... */+    /// #   fn new() -> Self {+    /// #       LinkedList(None)+    /// #   }+    /// #   fn push(&mut self, x: T) {+    /// #       self.0 = Some(Arc::new(Node(x, self.0.take())));+    /// #   }+    /// }+    ///+    /// // The following code could still cause a stack overflow+    /// // despite the manual `Drop` impl if that `Drop` impl used+    /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::unwrap_or_drop(arc)`.+    /// {+    ///     // create a long list and clone it+    ///     let mut x = LinkedList::new();+    ///     for i in 0..100000 {+    ///         x.push(i); // adds i to the front of x+    ///     }+    ///     let y = x.clone();+    ///+    ///     // drop the clones in parallel+    ///     let t1 = std::thread::spawn(|| drop(x));+    ///     let t2 = std::thread::spawn(|| drop(y));+    ///     t1.join().unwrap();+    ///     t2.join().unwrap();+    /// }+    /// ```+    #[inline]+    #[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue+    // FIXME: should this copy all/some of the comments from drop and drop_slow?+    pub fn unwrap_or_drop(this: Self) -> Option<T> {+        // following the implementation of `drop` (and `drop_slow`)+        let mut this = core::mem::ManuallyDrop::new(this);++        if this.inner().strong.fetch_sub(1, Release) != 1 {+            return None;+        }++        acquire!(this.inner().strong);++        // FIXME: should the part below this be moved into a seperate #[inline(never)]+        // function, like it's done with drop_slow in drop?

I was mostly just curious what the reasoning for the separation in the drop implementation is good for. Possibly improved performance by reducing code size in the common case; but someone ought to have none some benchmarking determining that this is worth it somewhere, right? Perhaps I should look at the git blame and track down the original author and PR to find out... I was just thinking that since unwrap_or_drop is pretty much also just a destructor for Arc, all the same performance considerations should apply there, too. On the other hand, try_unwrap does not do anything like this.

steffahn

comment created time in 10 days

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer such a guarantee. See the last example below and the documentation+    /// of [`try_unwrap`][`Arc::try_unwrap`].+    ///+    /// # Examples+    ///+    /// Minimal example demonstrating the guarantee that `unwrap_or_drop` gives.+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = Arc::clone(&x);+    ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:+    /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));+    /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));+    ///+    /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();+    /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();+    ///+    /// // One of the threads is guaranteed to receive the inner value:+    /// assert!(matches!(+    ///     (x_unwrapped_value, y_unwrapped_value),+    ///     (None, Some(3)) | (Some(3), None)+    /// ));+    /// // The result could also be `(None, None)` if the threads called+    /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.+    /// ```+    ///+    /// A more practical example demonstrating the need for `unwrap_or_drop`:+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// // Definition of a simple singly linked list using `Arc`:+    /// #[derive(Clone)]+    /// struct LinkedList<T>(Option<Arc<Node<T>>>);+    /// struct Node<T>(T, Option<Arc<Node<T>>>);+    ///+    /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`+    /// // can cause a stack overflow. To prevent this, we can provide a+    /// // manual `Drop` implementation that does the destruction in a loop:+    /// impl<T> Drop for LinkedList<T> {+    ///     fn drop(&mut self) {+    ///         let mut x = self.0.take();+    ///         while let Some(arc) = x.take() {+    ///             Arc::unwrap_or_drop(arc).map(|node| x = node.1);+    ///         }+    ///     }+    /// }+    ///+    /// // implementation of `new` and `push` omitted+    /// impl<T> LinkedList<T> {+    ///     /* ... */+    /// #   fn new() -> Self {+    /// #       LinkedList(None)+    /// #   }+    /// #   fn push(&mut self, x: T) {+    /// #       self.0 = Some(Arc::new(Node(x, self.0.take())));+    /// #   }+    /// }+    ///+    /// // The following code could still cause a stack overflow+    /// // despite the manual `Drop` impl if that `Drop` impl used+    /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::unwrap_or_drop(arc)`.+    /// {+    ///     // create a long list and clone it+    ///     let mut x = LinkedList::new();+    ///     for i in 0..100000 {+    ///         x.push(i); // adds i to the front of x+    ///     }+    ///     let y = x.clone();+    ///+    ///     // drop the clones in parallel+    ///     let t1 = std::thread::spawn(|| drop(x));+    ///     let t2 = std::thread::spawn(|| drop(y));+    ///     t1.join().unwrap();+    ///     t2.join().unwrap();+    /// }+    /// ```

Yeah I know, I called it overkill myself. Nontheless, more examples aren’t going to hurt, probably.

steffahn

comment created time in 10 days

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha 08455a631b16b5c4588afcdfd99401d2f83e47fc

fix oversight in test where `try_unwrap` was not changed back to `unwrap_or_drop`

view details

push time in 10 days

push eventsteffahn/rust

Frank Steffahn

commit sha c38f4b763791d1b1edb06a20b92724f8dda948b2

fix oversight in test where was not changed back to

view details

push time in 10 days

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn try_unwrap() {     assert_eq!(Arc::try_unwrap(x), Ok(5)); } +#[test]+fn unwrap_or_drop() {+    // FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok()`+    // and it makes this kind of assertion fail in roughly every second run somewhere+    // between 1000 and 5000 iterations; I feel like doing a single iteration is too+    // unlikely to catch anything interesting but doing too many is way too slow+    // for a test that wouldn't ever fail for any reasonable implementation++    for _ in 0..100+    // ^ increase chances of hitting uncommon race conditions+    {+        use std::sync::Arc;+        let x = Arc::new(3);+        let y = Arc::clone(&x);+        let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok());+        let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok());

I tested and the test does indeed fail more often than one would think (on my machine) if try_unwrap(x).ok() is used (at least on my machine). Which makes me notice... I totally forgot replacing it with unwrap_or_drop again after testing that these failures can happen!! Damn... this means also means that it didn’t fail once on the CI. But the hope was basically only that if unwrap_or_drop changed in the future and became broken (i.e. lost its guarantees of being atomic) then the test would fail for a lot of people (even if not for everyone) and the error would be noticed eventually.

steffahn

comment created time in 10 days

PullRequestReviewEvent

delete branch steffahn/tokio

delete branch : const-notify-rwlock-semaphore

delete time in 17 days

push eventsteffahn/tokio

Frank Steffahn

commit sha 8d2e3bc575f51815ae7319f1e43fe6c7d664e6e4

sync: add const constructors to RwLock, Notify, and Semaphore (#2833) * Add const constructors to `RwLock`, `Notify`, and `Semaphore`. Referring to the types in `tokio::sync`. Also add `const` to `new` for the remaining atomic integers in `src/loom` and `UnsafeCell`. Builds upon previous work in #2790 Closes #2756

view details

push time in 17 days

Pull request review commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

 impl<T: ?Sized> RwLock<T> {         }     } +    /// Creates a new instance of an `RwLock<T>` which is unlocked.+    ///+    /// # Examples+    ///+    /// ```+    /// use tokio::sync::RwLock;+    ///+    /// static LOCK: RwLock<i32> = RwLock::const_new(5);+    /// ```+    #[cfg(all(feature = "parking_lot", not(all(loom, test))))]+    pub const fn const_new(value: T) -> RwLock<T>

version is 99111606f 2020-09-11

the error is mentioning unresolved links to crate::fs::read_to_string::read_to_string in io/util/async_read_ext.rs, and to std::io::error::ErrorKind::WouldBlock in net/udp/socket.rs

steffahn

comment created time in 17 days

PullRequestReviewEvent

Pull request review commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

 impl<T: ?Sized> RwLock<T> {         }     } +    /// Creates a new instance of an `RwLock<T>` which is unlocked.+    ///+    /// # Examples+    ///+    /// ```+    /// use tokio::sync::RwLock;+    ///+    /// static LOCK: RwLock<i32> = RwLock::const_new(5);+    /// ```+    #[cfg(all(feature = "parking_lot", not(all(loom, test))))]+    pub const fn const_new(value: T) -> RwLock<T>

Unfortunately the approach as described in CONTRIBUTING.md fails with the same kind of broken_intro_doc_links warning/error I had to work around.

steffahn

comment created time in 18 days

PullRequestReviewEvent

Pull request review commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

 impl<T: ?Sized> RwLock<T> {         }     } +    /// Creates a new instance of an `RwLock<T>` which is unlocked.+    ///+    /// # Examples+    ///+    /// ```+    /// use tokio::sync::RwLock;+    ///+    /// static LOCK: RwLock<i32> = RwLock::const_new(5);+    /// ```+    #[cfg(all(feature = "parking_lot", not(all(loom, test))))]+    pub const fn const_new(value: T) -> RwLock<T>

Oh, I see. I wasn’t too far off then.

steffahn

comment created time in 18 days

PullRequestReviewEvent

push eventsteffahn/tokio

Frank Steffahn

commit sha b1e249e5c4ca59d378324ce6a94c3b1c863ab5c3

Fix feature flag documentation of `Notify`, `RwLock`, and `Semaphore`.

view details

push time in 18 days

Pull request review commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

 impl<T: ?Sized> RwLock<T> {         }     } +    /// Creates a new instance of an `RwLock<T>` which is unlocked.+    ///+    /// # Examples+    ///+    /// ```+    /// use tokio::sync::RwLock;+    ///+    /// static LOCK: RwLock<i32> = RwLock::const_new(5);+    /// ```+    #[cfg(all(feature = "parking_lot", not(all(loom, test))))]+    pub const fn const_new(value: T) -> RwLock<T>

Okay, got it now with cargo +nightly rustdoc --all-features -- --cfg docsrs after changing the deny(broken_intra_doc_links) in lib.rs.

steffahn

comment created time in 18 days

PullRequestReviewEvent

pull request commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

Please try to avoid force-pushing, as it makes reading the change-history difficult for me to read.

I was going to stop force-pushing as soon as a review started or more than a typo-sized mistake was being fixed.

steffahn

comment created time in 18 days

Pull request review commenttokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

 impl<T: ?Sized> RwLock<T> {         }     } +    /// Creates a new instance of an `RwLock<T>` which is unlocked.+    ///+    /// # Examples+    ///+    /// ```+    /// use tokio::sync::RwLock;+    ///+    /// static LOCK: RwLock<i32> = RwLock::const_new(5);+    /// ```+    #[cfg(all(feature = "parking_lot", not(all(loom, test))))]+    pub const fn const_new(value: T) -> RwLock<T>

Probably also in the other two. How do I build docsrs-style documentation locally to see the result?

steffahn

comment created time in 18 days

PullRequestReviewEvent

push eventsteffahn/tokio

Frank Steffahn

commit sha e487fa61ba64473d145faed0a271cfbad6946a2d

Add const constructors to `RwLock`, `Notify`, and `Semaphore`. Referring to the types in `tokio::sync`. Also add `const` to `new` for the remaining atomic integers in `src/loom` and `UnsafeCell`. Builds upon previous work in #2790. Closes #2756.

view details

push time in 18 days

push eventsteffahn/tokio

Frank Steffahn

commit sha 805b89d639639d287d467bf16cf39da41c17f990

Add const constructors to `RwLock`, `Notify`, and `Semaphore`. Referring to the types in `tokio::sync`. Also add `const` to `new` for the remaining atomic integers in `src/loom` and `UnsafeCell`. Builds upon previous work in #2790. Closes #2756.

view details

push time in 18 days

issue commenttokio-rs/tokio

Const constructors for Semaphore, Mutex, etc. with parking_lot.

Yikes I forgot to add it to RwLock, I can throw in a second PR covering the missed cases but I'll won't be able to get around to it for a few hours unfortunately.

Ah, thanks for the offer, but now I already created a PR myself ^^

@Darksonn feel free to take a look

steffahn

comment created time in 18 days

PR opened tokio-rs/tokio

Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

Referring to the types in tokio::sync. Also add const to new for the remaining atomic integers in src/loom and UnsafeCell.

<!-- Thank you for your Pull Request. Please provide a description above and review the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the documentation, which requires special commands beyond cargo fmt and cargo doc. -->

Motivation and Solution

Builds upon previous work in #2790. Closes #2756.

This PR adds const_new to sync::Notify, sync::RwLock, and sync::Semaphore, in addition to the previously added one for sync::Mutex as they can all be reasonably be used in static variables and this saves the need for (and overhead of) something like lazy_static in the case that parking_lot already is used internally anyways.

Also, upon encountering the problem of missing const for AtomicU8 in src/loom (used in Notify), I decided (also for consistency) to add const to all the constructors for the remaining types in that directory, whereas #2790 only added const to new for AtomicUSize. <!-- Summarize the solution and provide any necessary context needed to understand the code change. -->

+50 -5

0 comment

8 changed files

pr created time in 18 days

create barnchsteffahn/tokio

branch : const-notify-rwlock-semaphore

created branch time in 18 days

fork steffahn/tokio

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...

https://tokio.rs

fork in 18 days

issue commenttokio-rs/tokio

Const constructors for Semaphore, Mutex, etc. with parking_lot.

Looking a bit more into tokio::sync, const constructors for the types Semaphore and Notify seem reasonable too.

steffahn

comment created time in 18 days

issue commenttokio-rs/tokio

Const constructors for Semaphore, Mutex, etc. with parking_lot.

Dammit, I'm only noticing this as everything is merged already...

@mental32 @Darksonn What was the rationale to not include RwLock?

steffahn

comment created time in 18 days

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer such a guarantee. See the last example below and the documentation+    /// of [`try_unwrap`][`Arc::try_unwrap`].+    ///+    /// # Examples+    ///+    /// Minimal example demonstrating the guarantee that `unwrap_or_drop` gives.+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = Arc::clone(&x);+    ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:+    /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));+    /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));+    ///+    /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();+    /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();+    ///+    /// // One of the threads is guaranteed to receive the inner value:+    /// assert!(matches!(+    ///     (x_unwrapped_value, y_unwrapped_value),+    ///     (None, Some(3)) | (Some(3), None)+    /// ));+    /// // The result could also be `(None, None)` if the threads called+    /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.+    /// ```+    ///+    /// A more practical example demonstrating the need for `unwrap_or_drop`:+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// // Definition of a simple singly linked list using `Arc`:+    /// #[derive(Clone)]+    /// struct LinkedList<T>(Option<Arc<Node<T>>>);+    /// struct Node<T>(T, Option<Arc<Node<T>>>);+    ///+    /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`+    /// // can cause a stack overflow. To prevent this, we can provide a+    /// // manual `Drop` implementation that does the destruction in a loop:+    /// impl<T> Drop for LinkedList<T> {+    ///     fn drop(&mut self) {+    ///         let mut x = self.0.take();+    ///         while let Some(arc) = x.take() {+    ///             Arc::unwrap_or_drop(arc).map(|node| x = node.1);+    ///         }+    ///     }+    /// }+    ///+    /// // implementation of `new` and `push` omitted+    /// impl<T> LinkedList<T> {+    ///     /* ... */+    /// #   fn new() -> Self {+    /// #       LinkedList(None)+    /// #   }+    /// #   fn push(&mut self, x: T) {+    /// #       self.0 = Some(Arc::new(Node(x, self.0.take())));+    /// #   }+    /// }+    ///+    /// // The following code could still cause a stack overflow+    /// // despite the manual `Drop` impl if that `Drop` impl used+    /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::unwrap_or_drop(arc)`.+    /// {

It's not necessary, I know. I felt like it looked better this way, especially making clear that the comment is referencing the entire following code. I also wrote this when the two examples where not split yet. I have not reevaluated if it looks a bit less confusing to have this block on top level now that the examples are split.

steffahn

comment created time in a month

PullRequestReviewEvent

pull request commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

@poliorcetics also see the updated screenshot in my comment further up for a rendered version of that documentation

steffahn

comment created time in a month

pull request commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

I already modified the docs of try_unwrap in one of my newer commits.

Poliorcetics notifications@github.com schrieb am Mi., 2. Sept. 2020, 22:50:

The method is defined as taking this: Self so it will always be called as Arc::unwrap_or_drop(my_value). IMO this is enough to show what's going on. The fact it feats the current naming style is a nice bonus.

Now that I think about it: @steffahn https://github.com/steffahn, what do you think about modifying the doc for Arc::try_unwrap to give a pointer to Arc::unwrap_or_drop about the Arc::try_unwrap(x).ok() case ? Or maybe only adding a FIXME in the new method for later, when it is eventually stabilised ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/pull/75911#issuecomment-685990109, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA6NGJUWRTPXZC227UT4MQDSD2VX5ANCNFSM4QK2SWNQ .

steffahn

comment created time in a month

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {     /// let x = Arc::new(3);     /// let y = Arc::clone(&x);     ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:     /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));     /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));     ///     /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();     /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();     ///+    /// // One of the threads is guaranteed to receive the inner value:     /// assert!(matches!(     ///     (x_unwrapped_value, y_unwrapped_value),     ///     (None, Some(3)) | (Some(3), None)     /// ));+    ///+    ///

Thanks, I split them up now ^^

steffahn

comment created time in a month

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha 838e5edba7b1cc3a1fc36a4448a501c3333f7705

split examples

view details

push time in a month

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {     ///     /// If `unwrap_or_drop` is called on every clone of this `Arc`,     /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///     /// The similar expression `Arc::try_unwrap(this).ok()` does not-    /// offer this guarantee.+    /// offer such a guarantee. See the last example below and the documentation+    /// of `try_unwrap`[`Arc::try_unwrap`].

Yeah you can also put [`try_unwrap`][`Arc::try_unwrap`], its a function that's specific enough for it not to be confusing.

And also, it’s fully spelled out in the previous sentence. That was the main reason I wanted it as short as possible.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn unwrap_or_drop() {     assert_eq!(Arc::unwrap_or_drop(x), Some(5)); } +#[test]+fn unwrap_or_drop_linked_list() {

Note that code written in docs is compiled and run by default so you can count that as tests.

I know, but a single iteration is wayyy unlikely to hit the race condition.

steffahn

comment created time in a month

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha 1ceee61665a85533c63cf32a901a180fd674fc99

fix typo, remove superflous test

view details

push time in a month

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn unwrap_or_drop() {     assert_eq!(Arc::unwrap_or_drop(x), Some(5)); } +#[test]+fn unwrap_or_drop_linked_list() {+    #[derive(Clone)]+    struct LinkedList<T>(Option<Arc<Node<T>>>);+    struct Node<T>(T, Option<Arc<Node<T>>>);++    impl<T> Drop for LinkedList<T> {+        fn drop(&mut self) {+            let mut x = self.0.take();+            while let Some(arc) = x.take() {+                Arc::unwrap_or_drop(arc).map(|node| x = node.1);+            }+        }+    }++    impl<T> LinkedList<T> {+        fn push(&mut self, x: T) {+            self.0 = Some(Arc::new(Node(x, self.0.take())));+        }+    }++    use std::thread;+    for _ in 0..25 {

That’s a number that makes it likely enough to hit the race condition while not slowing the test down too much. As I said above, I think this test could be removed entirely.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn unwrap_or_drop() {     assert_eq!(Arc::unwrap_or_drop(x), Some(5)); } +#[test]+fn unwrap_or_drop_linked_list() {

I’m not even sure if this test is useful at all. It doesn’t really show much more than the unwrap_or_drop test aready does, just applied to a practical example. I think I should just delete it again. I mainly pushed for the docs anyways, and didn’t think about the fact that I also had written this test.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {     /// let x = Arc::new(3);     /// let y = Arc::clone(&x);     ///+    /// // Two threads calling `unwrap_or_drop` on both clones of an `Arc`:     /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));     /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));     ///     /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();     /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();     ///+    /// // One of the threads is guaranteed to receive the inner value:     /// assert!(matches!(     ///     (x_unwrapped_value, y_unwrapped_value),     ///     (None, Some(3)) | (Some(3), None)     /// ));+    ///+    ///

I wasn’t sure of the correct way to format this since I haven’t found any precedence of multiple example code blocks yet. Perhaps there are some examples like this somewhere in the standard library.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {     ///     /// If `unwrap_or_drop` is called on every clone of this `Arc`,     /// it is guaranteed that exactly one of the calls returns the inner value.+    /// This means in particular that the inner value is not dropped.+    ///     /// The similar expression `Arc::try_unwrap(this).ok()` does not-    /// offer this guarantee.+    /// offer such a guarantee. See the last example below and the documentation+    /// of `try_unwrap`[`Arc::try_unwrap`].

A type, I actually wanted to write [`try_unwrap`][`Arc::try_unwrap`] which renders as try_unwrap but the link works. I thought there are enough mentions of the word Arc in this comment already.

steffahn

comment created time in a month

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha 8af2a40048e21c6e33d563de0bd484e4dbaeda8d

more comments and test, maybe won't want to keep all of them since it's a lot squash me later

view details

push time in a month

pull request commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

The current documentation is adapted from and compares this function to try_unwrap so there’s no mention of the motivation (dropping Arc without calling a destructor). I don’t know if this should be added.

This would be nice to have; just by reading the type signature it wasn't clear why you'd want this over arc.try_unwrap().ok().

I’ve been working on the documentation a bit. I also added a paragraph to try_unwrap and a second example to unwrap_or_drop. This might be overkill though:

<hr>

Screenshot_20200829_210858 Screenshot_20200829_210948

<hr>

I mean, in case you like it, I can commit and push it so that people can correct any typos, etc.

steffahn

comment created time in a month

pull request commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

I’ve found another use-case while reading: this book about linked lists in rust.

The author presents code like this

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut head = self.head.take();
        while let Some(node) = head {
            if let Ok(mut node) = Rc::try_unwrap(node) {
                head = node.next.take();
            } else {
                break;
            }
        }
    }
}

to avoid stack overflows on drop. This code does use try_unwrap and drop the Rc in the Err case. On the next page they state “In order to get thread safety, we have to use Arc. [...] All we need to do [...] is replace every reference to Rc with std::sync::Arc. That's it. We're thread safe. Done!”

At least they acknowledge that in Rust, “[...] it's impossible to cause data races, (not to be mistaken with the more general issue of race conditions).” They do not seem to notice that by replacing Rc with Arc, they are introducing a race condition that can cause a stack overflow.

This example can of course be solved by unwrap_or_drop. Since it comes from starting out with Rc and only migrating the code to Arc afterwards, it convinces me that Rc should get a version of unwrap_or_drop, too.

steffahn

comment created time in a month

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer this guarantee.+    ///+    /// # Examples+    ///+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = Arc::clone(&x);+    ///+    /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x));+    /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y));+    ///+    /// let x_unwrapped_value = x_unwrap_thread.join().unwrap();+    /// let y_unwrapped_value = y_unwrap_thread.join().unwrap();+    ///+    /// assert!(matches!(+    ///     (x_unwrapped_value, y_unwrapped_value),+    ///     (None, Some(3)) | (Some(3), None)+    /// ));+    /// ```+    #[inline]+    #[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue+    // FIXME: should this copy all/some of the comments from drop and drop_slow?+    pub fn unwrap_or_drop(this: Self) -> Option<T> {+        // following the implementation of `drop` (and `drop_slow`)+        let mut this = core::mem::ManuallyDrop::new(this);++        if this.inner().strong.fetch_sub(1, Release) != 1 {+            return None;+        }++        acquire!(this.inner().strong);++        // FIXME: should the part below this be moved into a seperate #[inline(never)]+        // function, like it's done with drop_slow in drop?++        // using `ptr::read` where `drop_slow` was using `ptr::drop_in_place`+        let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) };

The situation is a bit more complex. It is safe because it is doing the same things that the drop implementation does, with the difference of doing ptr::read instead of ptr::drop_in_place, but those two operations are applicable in pretty much exactly the same situations. The reason why every step of drop is safe includes some really really really long comments, hence my general question, as stated in the FIXME in line 538: “should I copy [...] the comments from drop?”

steffahn

comment created time in a month

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha a534492145409879e51dad61d7a4a0733cb084f5

Implement `Arc::unwrap_or_drop`. Also add documentation and tests for it. This commit has some minor unresolved questions and is intended to be amended.

view details

push time in a month

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn try_unwrap() {     assert_eq!(Arc::try_unwrap(x), Ok(5)); } +#[test]+fn unwrap_or_drop() {+    // FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok`+    // and it makes this kind of assersion fail in roughly every second run somewhere+    // between 1000 and 5000 iterations; I feel like doing a sinlge iteration is too+    // unlikely to catch anything interesting but doing too many is way too slow+    // for a test that wouldn't ever fail for any reasonable implementation++    for _ in 0..100+    // ^ increase chances of hitting uncommon race conditions+    {+        use std::sync::Arc;+        let x = Arc::new(3);+        let y = x.clone();+        let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok());+        let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok());+        let r = r_thread.join().expect("r_thread panicked");+        let s = s_thread.join().expect("s_thread panicked");+        assert!(+            matches!((r, s), (None, Some(3)) | (Some(3), None)),+            "assertion failed: unexpected result `{:?}`\+            \n  expected `(None, Some(3))` or `(Some(3), None)`",+            (r, s),+        );+    }++    let x = Arc::new(3);+    assert_eq!(Arc::unwrap_or_drop(x), Some(3));++    let x = Arc::new(4);+    let y = x.clone();

Well, one distinction to be made is between the doc-test where the source code will be visible in the documentation vs. the ordinary test for which mostly the functionality matters. Anyways, I’ll apply your suggestions to keep consistency within my test function and with the doctest.

Also, are there really 3 copies of your answer?

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer this guarantee.+    ///+    /// # Examples+    ///+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = x.clone();

It would need to be Arc::clone(&x). Do you still prefer this?

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn try_unwrap() {     assert_eq!(Arc::try_unwrap(x), Ok(5)); } +#[test]+fn unwrap_or_drop() {+    // FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok`+    // and it makes this kind of assersion fail in roughly every second run somewhere+    // between 1000 and 5000 iterations; I feel like doing a sinlge iteration is too+    // unlikely to catch anything interesting but doing too many is way too slow+    // for a test that wouldn't ever fail for any reasonable implementation++    for _ in 0..100+    // ^ increase chances of hitting uncommon race conditions+    {+        use std::sync::Arc;+        let x = Arc::new(3);+        let y = x.clone();+        let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok());+        let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok());+        let r = r_thread.join().expect("r_thread panicked");+        let s = s_thread.join().expect("s_thread panicked");+        assert!(+            matches!((r, s), (None, Some(3)) | (Some(3), None)),+            "assertion failed: unexpected result `{:?}`\+            \n  expected `(None, Some(3))` or `(Some(3), None)`",+            (r, s),+        );+    }++    let x = Arc::new(3);+    assert_eq!(Arc::unwrap_or_drop(x), Some(3));++    let x = Arc::new(4);+    let y = x.clone();

This one is copied from the try_unwrap test above.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 impl<T> Arc<T> {             Ok(elem)         }     }++    /// Returns the inner value, if the `Arc` has exactly one strong reference.+    ///+    /// Otherwise, [`None`] is returned and the `Arc` is dropped.+    ///+    /// This will succeed even if there are outstanding weak references.+    ///+    /// If `unwrap_or_drop` is called on every clone of this `Arc`,+    /// it is guaranteed that exactly one of the calls returns the inner value.+    /// The similar expression `Arc::try_unwrap(this).ok()` does not+    /// offer this guarantee.+    ///+    /// # Examples+    ///+    /// ```+    /// #![feature(unwrap_or_drop)]+    ///+    /// use std::sync::Arc;+    ///+    /// let x = Arc::new(3);+    /// let y = x.clone();

Great idea! Do you have an opinion on those identifiers I chose in the following lines? I’m not totally happy with them since they’re very long, but short ones seemed potentially confusing.

steffahn

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

 fn try_unwrap() {     assert_eq!(Arc::try_unwrap(x), Ok(5)); } +#[test]+fn unwrap_or_drop() {+    // FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok`+    // and it makes this kind of assersion fail in roughly every second run somewhere+    // between 1000 and 5000 iterations; I feel like doing a sinlge iteration is too

Thanks for those corrections, wrote that paragraph rather quickly, I was planning on removing this comment before this is merged anways, once I got an answer to that question.

steffahn

comment created time in a month

PullRequestReviewEvent

push eventsteffahn/rust

Frank Steffahn

commit sha 8155a9a2c57f71f109d57fd4b5a68d7870a91e91

Implement `Arc::unwrap_or_drop`. Also add documentation and tests for it. This commit has some minor unresolved questions and is intended to be amended.

view details

push time in a month

push eventsteffahn/rust

Frank Steffahn

commit sha a6f80e67930768f894763e4005701d2b77ef3309

Implement `Arc::unwrap_or_drop`. Also add documentation and tests for it. This commit has some minor unresolved questions and is intended to be amended.

view details

push time in a month

PR opened rust-lang/rust

Add `Arc::unwrap_or_drop` for safely discarding `Arc`s without calling the destructor on the inner type.

This is my first contribution. The commit includes tests and documentation. I have marked a few stylistic or technical questions with TODO. In particular, I don’t know how the tracking issues for new unstable standard library functions work.

There was previously some discussion on IRLO.

Motivation

The functionality provided by this new “method” on Arc was previously not archievable with the Arc API. The function unwrap_or_drop is related to (and hence similarly named similar to) try_unwrap. The expression Arc::unwrap_or_drop(x) is almost the same as Arc::try_unwrap(x).ok(), however the latter includes two steps, the try_unwrap call and dropping the Arc, whereas unwrap_or_drop accesses the Arc atomically. Since this is an issue in multi-threaded settings only, a similar function on Rc is not strictly necessary but could be wanted nontheless for ergonomic and API-similarity reasons. (This PR currently only offers the function on Arc, but I could add one for Rc if wanted.) In the IRLO discussion, I also mentioned two more functions that could possibly extend this API.

The function Arc::unwrap_or_drop(this: Arc<T>) -> Option<T> offers a way to “drop” an Arc without calling the destructor on the contained type. When the Arc provided was the last strong pointer to its target, the target value is returned. Being able to do this is valueable around linear(-ish) types that should not or cannot just be dropped ordinarity, but require extra arguments, or operations that can fail or are async to properly get rid of.

Further Remarks

The current documentation is adapted from and compares this function to try_unwrap so there’s no mention of the motivation (dropping Arc without calling a destructor). I don’t know if this should be added.

The names of try_unwrap and unwrap_or_drop since these operations seem quite different from the unwrap methods on Option or Result. This functionality could be renamed around into_inner, for example as try_into_inner (instead of try_unwrap, which would be deprecated) and into_inner (instead of unwrap_or_drop). Some people favored this kind of naming scheme in the IRLO discussion. On the other hand, into_inner is usually more straightforward and deterministic than what unwrap_or_drop offers.

+89 -0

0 comment

2 changed files

pr created time in a month

create barnchsteffahn/rust

branch : drop_linear_arc

created branch time in a month

fork steffahn/rust

Empowering everyone to build reliable and efficient software.

https://www.rust-lang.org

fork in a month

issue commentrust-lang/book

Cons-List with References instead of Box<T>s compile- and usable although the book says they aren't

Sorry I wasn't clear enough when suggesting to open an issue. The information in the book is not deprecated regarding the main point that Lists with ordinary references are impractical - that's still true. Just the example given there to demonstrate the impracticality is outdated / bad / wrong since it claims Cons(10, &Nil) doesn't compile even though this code does compile due to static promotion.

Arninius

comment created time in a month

delete branch steffahn/replace_with

delete branch : fix-potential-ub

delete time in 2 months

pull request commentgeohot/haskell-scheme

Add float support.

You could add something like

numValToFloat :: NumVal -> Float
numValToFloat (Float f) = f
numValToFloat (Integer i) = fromInteger i

to make all the other functions a lot shorter, e.g.

(+) (Integer a) (Integer b) = Integer $ a+b
(+) a b = Float $ (numValToFloat a)+(numValToFloat b)

also note that you can define infix operators without writing them prefix, like so:

Integer a + Integer b = Integer $ a + b
a + b = Float $ numValToFloat a + numValToFloat b

finally, adding tests would probably be a good idea ^^

whagsv

comment created time in 2 months

issue commentrust-lang/miri

False positive around mem::forget and ManuallyDrop (or the `replace_with` crate has UB)

For the record, the replace_with code also had problems (i.e. was unsound) around panic: https://github.com/alecmocatta/replace_with/pull/14#issuecomment-673469800

steffahn

comment created time in 2 months

pull request commentalecmocatta/replace_with

Replace mem::forget with ManuallyDrop, fixing UB

Another reason, and this PR fixes something that was definitely unsound:

use replace_with::replace_with;


fn main() {
	struct PanicOnDrop;
	impl Drop for PanicOnDrop {
		fn drop(&mut self) {
			panic!()
		}
	}
	let a = (vec![0], PanicOnDrop);
	let mut x = 0;
	replace_with(&mut x, move || a.0[0], |x| x);
}

causes a use-after-free:

C:\Users\Frank\Documents\playground\rust\replace_with_miri_test>cargo +nightly-2020-08-12 miri run
    Checking replace_with_miri_test v0.1.0 (C:\Users\Frank\Documents\playground\rust\replace_with_miri_test)
thread 'main' panicked at 'explicit panic', src\main.rs:8:13
error: Undefined Behavior: pointer to alloc11273 was dereferenced after this allocation got freed
    --> C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\slice\mod.rs:6244:14
     |
6244 |     unsafe { &*ptr::slice_from_raw_parts(data, len) }
     |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer to alloc11273 was dereferenced after this allocation got freed
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

     = note: inside `std::slice::from_raw_parts::<i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\slice\mod.rs:6244:14
     = note: inside `<std::vec::Vec<i32> as std::ops::Deref>::deref` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\vec.rs:1962:18
     = note: inside `<std::vec::Vec<i32> as std::ops::Index<usize>>::index` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\vec.rs:2012:23
note: inside closure at src\main.rs:13:31
    --> src\main.rs:13:31
     |
13   |     replace_with(&mut x, move || a.0[0], |x| x);
     |                                  ^^^^^^
     = note: inside closure at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:156:59
     = note: inside `<replace_with::OnDrop<[closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:13:23: 13:37 a:(std::vec::Vec<i32>, main::PanicOnDrop)]]> as std::ops::Drop>::drop` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:97:3
     = note: inside `std::intrinsics::drop_in_place::<replace_with::OnDrop<[closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:13:23: 13:37 a:(std::vec::Vec<i32>, main::PanicOnDrop)]]>> - shim(Some(replace_with::OnDrop<[closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:13:23: 13:37 a:(std::vec::Vec<i32>, main::PanicOnDrop)]]>))` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib
\src\rust\library\core\src\ptr\mod.rs:184:1
     = note: inside `replace_with::on_unwind::<[closure@replace_with::replace_with::{{closure}}#0 0:[closure@src\main.rs:13:39: 13:44], 1:i32], i32, [closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:13:23: 13:37 a:(std::vec::Vec<i32>, main::PanicOnDrop)]]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:109:1
     = note: inside `replace_with::replace_with::<i32, [closure@src\main.rs:13:23: 13:37 a:(std::vec::Vec<i32>, main::PanicOnDrop)], [closure@src\main.rs:13:39: 13:44]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:156:13
note: inside `main` at src\main.rs:13:2
    --> src\main.rs:13:2
     |
13   |     replace_with(&mut x, move || a.0[0], |x| x);
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:233:5
     = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys_common\backtrace.rs:137:18
     = note: inside closure at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:66:18
     = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:265:13
     = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:373:40
     = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:337:19
     = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:394:14
     = note: inside `std::rt::lang_start_internal` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:51:25
     = note: inside `std::rt::lang_start::<()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:65:5

error: aborting due to previous error

Basically what is happening is explained by a comment in scopeguard::ScopeGuard::into_inner.

pub fn on_unwind<F: FnOnce() -> T, T, P: FnOnce()>(f: F, p: P) -> T {
    let x = OnDrop(mem::ManuallyDrop::new(p));
    let t = f();
    let _ = unsafe { ptr::read(&*x.0) }; // if `p` panics on drop, `x`'s destructor will access `p` even though it was partially dropped
    mem::forget(x);
    t
}
steffahn

comment created time in 2 months

push eventsteffahn/replace_with

Frank Steffahn

commit sha 15e588a9d671f2441f49077247b61169c69d021c

Replace mem::forget with ManuallyDrop, fixing potential UB As shown in https://github.com/rust-lang/miri/issues/1508, it was possible to "find UB" in `replace_with` with Miri.

view details

push time in 2 months

pull request commentalecmocatta/replace_with

Replace mem::forget with ManuallyDrop, fixing potential UB

Dang it, ManuallyDrop::take is too new.

steffahn

comment created time in 2 months

PR opened alecmocatta/replace_with

Replace mem::forget with ManuallyDrop, fixing potential UB

As shown in https://github.com/rust-lang/miri/issues/1508, it was possible to "find UB" in replace_with with Miri.

I also changed the ptr::read from the existing ManuallyDrop into a more straight-forward ManuallyDrop::take.

+3 -3

0 comment

1 changed file

pr created time in 2 months

create barnchsteffahn/replace_with

branch : fix-potential-ub

created branch time in 2 months

issue closedrust-lang/miri

False positive around mem::forget and ManuallyDrop (or the `replace_with` crate has UB)

I’m posting this issue here rather than in the replace_with crate because I’m personally not seeing any UB in their code. And because I’m guessing the people around here might be more knowledgeable on the topic of inconspicuous undefined behavior.

I’m not too accustomed with miri, in particular I don’t even know if false positives (positive in the sense that UB has been found) are supposed to happen or not.

<hr>

Here’s my code

use replace_with::replace_with;
fn main() {
    let bx = Box::new(0);
    let mut x = 0;
    replace_with(&mut x, move || *bx, |x| x);
}

using replace_with v0.1.6.

Their (relevant) code is:

use std::{mem, ptr};

pub fn replace_with<T, D: FnOnce() -> T, F: FnOnce(T) -> T>(dest: &mut T, default: D, f: F) {
	unsafe {
		let old = ptr::read(dest);
		let new = on_unwind(move || f(old), || ptr::write(dest, default()));
		ptr::write(dest, new);
	}
}

pub fn on_unwind<F: FnOnce() -> T, T, P: FnOnce()>(f: F, p: P) -> T {
	let x = OnDrop(mem::ManuallyDrop::new(p));
	let t = f();
	let _ = unsafe { ptr::read(&*x.0) };
	mem::forget(x);
	t
}

struct OnDrop<F: FnOnce()>(mem::ManuallyDrop<F>);
impl<F: FnOnce()> Drop for OnDrop<F> {
	fn drop(&mut self) {
		(unsafe { ptr::read(&*self.0) })();
	}
}

<hr>

Miri output:

C:\Users\Frank\Documents\playground\rust\replace_with_miri_test>cargo +nightly-2020-08-12 miri run
    Checking replace_with v0.1.6
    Checking replace_with_miri_test v0.1.0 (C:\Users\Frank\Documents\playground\rust\replace_with_miri_test)
error: Undefined Behavior: type validation failed: encountered a dangling box (use-after-free) at .0.value.<captured-var(1)>.<captured-var(bx)>
   --> C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:107:14
    |
107 |     mem::forget(x);
    |                 ^ type validation failed: encountered a dangling box (use-after-free) at .0.value.<captured-var(1)>.<captured-var(bx)>
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

    = note: inside `replace_with::on_unwind::<[closure@replace_with::replace_with::{{closure}}#0 0:[closure@src\main.rs:5:39: 5:44], 1:i32], i32, [closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:5:26: 5:37 bx:std::boxed::Box<i32>]]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:107:14
    = note: inside `replace_with::replace_with::<i32, [closure@src\main.rs:5:26: 5:37 bx:std::boxed::Box<i32>], [closure@src\main.rs:5:39: 5:44]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:156:13
note: inside `main` at src\main.rs:5:5
   --> src\main.rs:5:5
    |
5   |     replace_with(&mut x, move || *bx, |x| x);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:233:5
    = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys_common\backtrace.rs:137:18
    = note: inside closure at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:66:18
    = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:265:13
    = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:373:40
    = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:337:19
    = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:394:14
    = note: inside `std::rt::lang_start_internal` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:51:25
    = note: inside `std::rt::lang_start::<()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:65:5

error: aborting due to previous error

closed time in 2 months

steffahn

issue commentrust-lang/miri

False positive around mem::forget and ManuallyDrop (or the `replace_with` crate has UB)

I’ll close this for now as it might indeed be UB and it seems to be unidiomatic use of mem::forget according to the docs, and also ManuallyDrop is a better alternative anyways.

steffahn

comment created time in 2 months

fork steffahn/replace_with

Temporarily take ownership of a value at a mutable location, and replace it with a new value based on the old one.

fork in 2 months

issue commentrust-lang/miri

False positive around mem::forget and ManuallyDrop (or the `replace_with` crate has UB)

Okay, interesting. I’m confused why miri mentions the Box in particular though since that Box is behind a ManuallyDrop and you ought to be able to pass around ManuallyDrops after they’ve been dropped, right?

Anyways, it probably doesn’t hurt to propose a change to

pub fn on_unwind<F: FnOnce() -> T, T, P: FnOnce()>(f: F, p: P) -> T {
    let x = OnDrop(mem::ManuallyDrop::new(p));
    let t = f();
    let _closure = unsafe { ptr::read(&*x.0) };
    mem::forget(x);
    t
}
steffahn

comment created time in 2 months

issue openedrust-lang/miri

False positive around mem::forget and ManuallyDrop (or the `replace_with` crate has UB)

I’m posting this issue here rather than in the replace_with crate because I’m personally not seeing any UB in their code. And because I’m guessing the people around here might be more knowledgeable on the topic of inconspicuous undefined behavior.

I’m not too accustomed with miri, in particular I don’t even know if false positives (positive in the sense that UB has been found) are supposed to happen or not.

<hr>

Here’s my code

use replace_with::replace_with;
fn main() {
    let bx = Box::new(0);
    let mut x = 0;
    replace_with(&mut x, move || *bx, |x| x);
}

using replace_with v0.1.6.

Their (relevant) code is:

use std::{mem, ptr};

pub fn replace_with<T, D: FnOnce() -> T, F: FnOnce(T) -> T>(dest: &mut T, default: D, f: F) {
	unsafe {
		let old = ptr::read(dest);
		let new = on_unwind(move || f(old), || ptr::write(dest, default()));
		ptr::write(dest, new);
	}
}

pub fn on_unwind<F: FnOnce() -> T, T, P: FnOnce()>(f: F, p: P) -> T {
	let x = OnDrop(mem::ManuallyDrop::new(p));
	let t = f();
	let _ = unsafe { ptr::read(&*x.0) };
	mem::forget(x);
	t
}

struct OnDrop<F: FnOnce()>(mem::ManuallyDrop<F>);
impl<F: FnOnce()> Drop for OnDrop<F> {
	fn drop(&mut self) {
		(unsafe { ptr::read(&*self.0) })();
	}
}

<hr>

Miri output:

C:\Users\Frank\Documents\playground\rust\replace_with_miri_test>cargo +nightly-2020-08-12 miri run
    Checking replace_with v0.1.6
    Checking replace_with_miri_test v0.1.0 (C:\Users\Frank\Documents\playground\rust\replace_with_miri_test)
error: Undefined Behavior: type validation failed: encountered a dangling box (use-after-free) at .0.value.<captured-var(1)>.<captured-var(bx)>
   --> C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:107:14
    |
107 |     mem::forget(x);
    |                 ^ type validation failed: encountered a dangling box (use-after-free) at .0.value.<captured-var(1)>.<captured-var(bx)>
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

    = note: inside `replace_with::on_unwind::<[closure@replace_with::replace_with::{{closure}}#0 0:[closure@src\main.rs:5:39: 5:44], 1:i32], i32, [closure@replace_with::replace_with::{{closure}}#1 0:&mut &mut i32, 1:[closure@src\main.rs:5:26: 5:37 bx:std::boxed::Box<i32>]]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:107:14
    = note: inside `replace_with::replace_with::<i32, [closure@src\main.rs:5:26: 5:37 bx:std::boxed::Box<i32>], [closure@src\main.rs:5:39: 5:44]>` at C:\Users\Frank\.cargo\registry\src\github.com-1ecc6299db9ec823\replace_with-0.1.6\src\lib.rs:156:13
note: inside `main` at src\main.rs:5:5
   --> src\main.rs:5:5
    |
5   |     replace_with(&mut x, move || *bx, |x| x);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:233:5
    = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys_common\backtrace.rs:137:18
    = note: inside closure at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:66:18
    = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:265:13
    = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:373:40
    = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:337:19
    = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:394:14
    = note: inside `std::rt::lang_start_internal` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:51:25
    = note: inside `std::rt::lang_start::<()>` at C:\Users\Frank\.rustup\toolchains\nightly-2020-08-12-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:65:5

error: aborting due to previous error

created time in 2 months

issue openedtokio-rs/tokio

Const constructors for Semaphore, Mutex, etc. with parking_lot.

I’m noticing that with the parking_lot feature enabled, the type tokio::sync::Semaphore is made up of types that can all be constructed with const fns. Putting a Mutex into a static variable is a common pattern to simulate global variables in Rust; without something like const fn new available, one also needs to wrap the variable into a lazy_static or something similar. Thus the following seems like a valuable addition to me.

I propose that a few functions are made conditionally const, when the parking_lot feature is enabled. I’m not sure yet how nice this can work out in the documentation and how bad it would look in the source code though. If making e.g. Mutex::new conditionally const causes too much churn, an alternative is to add some additional functions, gated behind the parking_lot feature, for const construction of Semaphore, Mutex, etc.

I have no idea what this loom feature is all about and how it interacts with this feature request. <hr>

As far as I can tell, we would just need to add const to

  • loom::std::parking_lot::Mutex::new
  • util::LinkedList::new
  • sync::batch_semaphore::Semaphore::new (conditionally)
  • sync::Semaphore::new (conditionally)
  • sync::Mutex::new (conditionally)
  • sync::RwLock::new (conditionally)
  • possibly other users of Semaphore I haven’t found yet.

created time in 2 months

PR opened geohot/haskell-scheme

Improve README
  • add links to YT (for those non-subscribers)
  • mark cond support as DONE
+3 -3

0 comment

1 changed file

pr created time in 2 months

create barnchsteffahn/haskell-scheme

branch : improve-readme

created branch time in 2 months

push eventsteffahn/haskell-scheme

Frank Steffahn

commit sha 41b862d651bd7c9b7e114c3000b22e7968cce71e

Add a (failing) test for cond. `cond` should behave like `if` in treating everything except `#f` as true.

view details

levant

commit sha 28b76af9834af8560a9f1dc56505e6768aaba158

Fix a bug in the 'cond' implementation

view details

George Hotz

commit sha 179ddb6a7e83ffc55c20da75bf7bfe68e61f817c

Merge pull request #4 from steffahn/test-slightly-incorrect-cond-implementation Add a (failing) test for cond.

view details

George Hotz

commit sha 45403c9806d8da889e33a6d4c89b2cbfff668689

Merge pull request #5 from levant47/master Fix a bug in the 'cond' implementation

view details

push time in 2 months

delete branch steffahn/haskell-scheme

delete branch : test-slightly-incorrect-cond-implementation

delete time in 2 months

push eventsteffahn/haskell-scheme

Frank Steffahn

commit sha 41b862d651bd7c9b7e114c3000b22e7968cce71e

Add a (failing) test for cond. `cond` should behave like `if` in treating everything except `#f` as true.

view details

push time in 2 months

push eventsteffahn/haskell-scheme

Frank Steffahn

commit sha d71649742f1d787aba14d5895bbbd5c203302d0e

Add a (failing) test for cond. Cond is should behave like `if` in treating everything except `#f` as true.

view details

push time in 2 months

push eventsteffahn/haskell-scheme

Frank Steffahn

commit sha d10d991414e6fd20fbf4b0e055dcfdd0c66626df

Add a (failing) test for cond. Cond is should behave like `if` in treating everything except `#f` as true.

view details

push time in 2 months

PR opened geohot/haskell-scheme

Add a (failing) test for cond.

Cond is should behave like if in treating everything except #f as true.

+1 -0

0 comment

1 changed file

pr created time in 2 months

push eventsteffahn/haskell-scheme

Frank Steffahn

commit sha 6f4f70b9cceb89971c97e350501778e6eda7e679

A handful of new tests that currently do all fail. I’ll admit, some of the tests are more for chapter 1.1.8 stuff.

view details

Frank Steffahn

commit sha 1bd532db1b647f050c2d0819467a78e94b711f68

Fix two typos in new test.

view details

levant

commit sha 6868913cc1cf8ac07d2dcc274baf19780659a9b4

Implement 'cond' and tests for it

view details

levant

commit sha 70a012c75c0809d1e6e6752c2ebbe346d66790d6

Remove an unused import

view details

Timon Grassl

commit sha 10bcf59020ee64c43022208a6580a7de77b7abad

prettify readme

view details

Timon Grassl

commit sha 9f581250d50e58b459005eca7e6c3379b454db05

added links to related streams

view details

George Hotz

commit sha e5d8a1fddb9a695236b31c503d451446be841856

Merge pull request #1 from steffahn/patch-1 A handful of new tests that currently do all fail.

view details

George Hotz

commit sha 6fc02e297d7b76154885770080eb18d331a85b3b

Merge pull request #2 from levant47/master Implement 'cond' and tests for it

view details

George Hotz

commit sha 66f72100854c506611b8e3ab8e6a2a6534e3d10c

Merge pull request #3 from tgrassl/patch-1 Prettify the Readme

view details

push time in 2 months

delete branch steffahn/haskell-scheme

delete branch : patch-1

delete time in 2 months

more