// 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 { /// /// Provides callbacks to objects whose lifetime is managed by . /// internal interface IDeferredDisposable { /// /// Called when the object's refcount reaches zero. /// /// /// Indicates whether the object has been disposed. /// /// /// If the refount reaches zero before the object is disposed, this method will be called with /// set to false. If the object is then disposed, this method will be /// called again, with set to true. If the refcount reaches zero /// after the object has already been disposed, this will be called a single time, with /// set to true. /// void OnFinalRelease(bool disposed); } /// /// 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. /// /// The type of object whose lifetime will be managed. /// /// This type maintains a reference count, and tracks whether the object has been disposed. When /// Callbacks are made to 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 when /// 'disposed' is true. /// internal struct DeferredDisposableLifetime 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; } } } } }