about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-07-03 22:10:30 +0200
committerRalf Jung <post@ralfj.de>2018-07-03 22:33:17 +0200
commitf96c2468695911222ba7557ce04af0dd8fbb6df2 (patch)
treed7efa02d693f52dd1abb10cb2220555598d71805 /src/liballoc
parent860d169474acabdc53b9a698f8ce02eba7e0daeb (diff)
downloadrust-f96c2468695911222ba7557ce04af0dd8fbb6df2.tar.gz
rust-f96c2468695911222ba7557ce04af0dd8fbb6df2.zip
Strenghten synchronization in `Arc::is_unique`
Previously, `is_unique` would not synchronize at all with a `drop` that returned
early because it was not the last reference, leading to a data race.

Fixes #51780
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/sync.rs13
1 files changed, 7 insertions, 6 deletions
diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs
index 2abd9c85c57..4244b09b18f 100644
--- a/src/liballoc/sync.rs
+++ b/src/liballoc/sync.rs
@@ -886,13 +886,14 @@ impl<T: ?Sized> Arc<T> {
         // holder.
         //
         // The acquire label here ensures a happens-before relationship with any
-        // writes to `strong` prior to decrements of the `weak` count (via drop,
-        // which uses Release).
+        // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements
+        // of the `weak` count (via `Weak::drop`, which uses release).  If the upgraded
+        // weak ref was never dropped, the CAS here will fail so we do not care to synchronize.
         if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() {
-            // Due to the previous acquire read, this will observe any writes to
-            // `strong` that were due to upgrading weak pointers; only strong
-            // clones remain, which require that the strong count is > 1 anyway.
-            let unique = self.inner().strong.load(Relaxed) == 1;
+            // This needs to be an `Acquire` to synchronize with the decrement of the `strong`
+            // counter in `drop` -- the only access that happens when any but the last reference
+            // is being dropped.
+            let unique = self.inner().strong.load(Acquire) == 1;
 
             // The release write here synchronizes with a read in `downgrade`,
             // effectively preventing the above read of `strong` from happening