فهرست منبع

Enable AsyncGateRelease to work with either another IDisposable or an IAsyncGate.

Add XML doc comments.
Ian Griffiths 10 ماه پیش
والد
کامیت
c9ac48c902
1فایلهای تغییر یافته به همراه43 افزوده شده و 4 حذف شده
  1. 43 4
      AsyncRx.NET/System.Reactive.Async/Threading/AsyncGateReleaser.cs

+ 43 - 4
AsyncRx.NET/System.Reactive.Async/Threading/AsyncGateReleaser.cs

@@ -4,12 +4,51 @@
 
 namespace System.Reactive.Threading
 {
-    public readonly struct AsyncGateReleaser : IDisposable
+    /// <summary>
+    /// Returned by <see cref="IAsyncGate.LockAsync"/>, enabling the caller to release the lock.
+    /// </summary>
+    public struct AsyncGateReleaser : IDisposable
     {
-        private readonly IAsyncGate _parent;
+        // Holds either an IAsyncGate or an IDisposable.
+        // In the case where this is an IAsyncGate, it's important that we try to avoid
+        // calling Release more than once, because this releaser is associated with just one
+        // call to LockAsync. IDisposable implementations are expected to be idempotent,
+        // so we need to remember when we've already made our one call to Release. (This
+        // can't be perfect because this is a struct, so callers might end up copying
+        // this value and then disposing each copy. But for normal using usage that won't
+        // be a problem, and this provides a reasonable best-effort approach. It's why
+        // this can't be a readonly struct though.)
+        private object _parentOrDisposable;
 
-        public AsyncGateReleaser(IAsyncGate parent) => _parent = parent;
+        /// <summary>
+        /// Creates an <see cref="AsyncGateReleaser"/> that calls <see cref="IAsyncGate.Release"/>
+        /// on its parent when disposed.
+        /// </summary>
+        /// <param name="parent"></param>
+        public AsyncGateReleaser(IAsyncGate parent) => _parentOrDisposable = parent;
 
-        public void Dispose() => _parent.Release();
+        /// <summary>
+        /// Creates an <see cref="AsyncGateReleaser"/> that calls another disposable when disposed.
+        /// </summary>
+        /// <param name="disposable">
+        /// The <see cref="IDisposable"/> implementation to which to defer.
+        /// </param>
+        /// <remarks>
+        /// This can be convenient for custom <see cref="IAsyncGate"/> implementations in that wrap
+        /// some underlying lock implementation that returns an <see cref="IDisposable"/> as the means
+        /// by which the lock is released.
+        /// </remarks>
+        public AsyncGateReleaser(IDisposable disposable) => _parentOrDisposable = disposable;
+
+        public void Dispose()
+        {
+            switch (_parentOrDisposable)
+            {
+                case IDisposable d: d.Dispose(); break;
+                case IAsyncGate g: g.Release(); break;
+            }
+
+            _parentOrDisposable = null;
+        }
     }
 }