Revert "Cleanup netfx-specific code from projects that no longer build in netfx confi...
authorJan Kotas <jkotas@microsoft.com>
Sun, 26 Aug 2018 14:34:04 +0000 (07:34 -0700)
committerJan Kotas <jkotas@microsoft.com>
Sun, 26 Aug 2018 21:13:12 +0000 (14:13 -0700)
This reverts commit 21eb9aa89fae41b73f87c073abd95041ecf1b0e0.

src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs [new file with mode: 0644]

diff --git a/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs b/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs
new file mode 100644 (file)
index 0000000..e2b1eb9
--- /dev/null
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Threading
+{
+    /// <summary>
+    /// Provides callbacks to objects whose lifetime is managed by <see cref="DeferredDisposableLifetime{T}"/>.
+    /// </summary>
+    internal interface IDeferredDisposable
+    {
+        /// <summary>
+        /// Called when the object's refcount reaches zero.
+        /// </summary>
+        /// <param name="disposed">
+        /// Indicates whether the object has been disposed.
+        /// </param>
+        /// <remarks>
+        /// If the refcount reaches zero before the object is disposed, this method will be called with
+        /// <paramref name="disposed"/> set to false.  If the object is then disposed, this method will be
+        /// called again, with <paramref name="disposed"/> set to true.  If the refcount reaches zero
+        /// after the object has already been disposed, this will be called a single time, with 
+        /// <paramref name="disposed"/> set to true.
+        /// </remarks>
+        void OnFinalRelease(bool disposed);
+    }
+
+    /// <summary>
+    /// Manages the lifetime of an object which implements IDisposable, but which must defer the actual
+    /// cleanup of state until all existing uses of the object are complete.
+    /// </summary>
+    /// <typeparam name="T">The type of object whose lifetime will be managed.</typeparam>
+    /// <remarks>
+    /// This type maintains a reference count, and tracks whether the object has been disposed.  When
+    /// Callbacks are made to <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when the refcount
+    /// reaches zero.  Objects that need to defer cleanup until they have been disposed *and* they have
+    /// no more references can do so in <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when
+    /// 'disposed' is true.
+    /// </remarks>
+    internal struct DeferredDisposableLifetime<T> where T : class, IDeferredDisposable
+    {
+        //
+        // _count is positive until Dispose is called, after which it's (-1 - refcount).
+        //
+        private int _count;
+
+        public bool AddRef(T obj)
+        {
+            while (true)
+            {
+                int oldCount = Volatile.Read(ref _count);
+
+                // Have we been disposed?
+                if (oldCount < 0)
+                    throw new ObjectDisposedException(typeof(T).ToString());
+
+                int newCount = checked(oldCount + 1);
+
+                if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+                    return true;
+            }
+        }
+
+        public void Release(T obj)
+        {
+            while (true)
+            {
+                int oldCount = Volatile.Read(ref _count);
+                if (oldCount > 0)
+                {
+                    // We haven't been disposed.  Decrement _count.
+                    int newCount = oldCount - 1;
+                    if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+                    {
+                        if (newCount == 0)
+                            obj.OnFinalRelease(disposed: false);
+                        return;
+                    }
+                }
+                else
+                {
+                    Debug.Assert(oldCount != 0 && oldCount != -1);
+
+                    // We've been disposed.  Increment _count.
+                    int newCount = oldCount + 1;
+                    if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+                    {
+                        if (newCount == -1)
+                            obj.OnFinalRelease(disposed: true);
+                        return;
+                    }
+                }
+            }
+        }
+
+        public void Dispose(T obj)
+        {
+            while (true)
+            {
+                int oldCount = Volatile.Read(ref _count);
+                if (oldCount < 0)
+                    return; // already disposed
+
+                int newCount = -1 - oldCount;
+                if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+                {
+                    if (newCount == -1)
+                        obj.OnFinalRelease(disposed: true);
+                    return;
+                }
+            }
+        }
+    }
+}