AsyncGateReleaser.cs 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT License.
  3. // See the LICENSE file in the project root for more information.
  4. namespace System.Reactive.Threading
  5. {
  6. /// <summary>
  7. /// Returned by <see cref="IAsyncGate.LockAsync"/>, enabling the caller to release the lock.
  8. /// </summary>
  9. public struct AsyncGateReleaser : IDisposable
  10. {
  11. // Holds either an IAsyncGate or an IDisposable.
  12. // In the case where this is an IAsyncGate, it's important that we try to avoid
  13. // calling Release more than once, because this releaser is associated with just one
  14. // call to LockAsync. IDisposable implementations are expected to be idempotent,
  15. // so we need to remember when we've already made our one call to Release. (This
  16. // can't be perfect because this is a struct, so callers might end up copying
  17. // this value and then disposing each copy. But for normal using usage that won't
  18. // be a problem, and this provides a reasonable best-effort approach. It's why
  19. // this can't be a readonly struct though.)
  20. private object _parentOrDisposable;
  21. /// <summary>
  22. /// Creates an <see cref="AsyncGateReleaser"/> that calls <see cref="IAsyncGate.Release"/>
  23. /// on its parent when disposed.
  24. /// </summary>
  25. /// <param name="parent"></param>
  26. public AsyncGateReleaser(IAsyncGate parent) => _parentOrDisposable = parent;
  27. /// <summary>
  28. /// Creates an <see cref="AsyncGateReleaser"/> that calls another disposable when disposed.
  29. /// </summary>
  30. /// <param name="disposable">
  31. /// The <see cref="IDisposable"/> implementation to which to defer.
  32. /// </param>
  33. /// <remarks>
  34. /// This can be convenient for custom <see cref="IAsyncGate"/> implementations in that wrap
  35. /// some underlying lock implementation that returns an <see cref="IDisposable"/> as the means
  36. /// by which the lock is released.
  37. /// </remarks>
  38. public AsyncGateReleaser(IDisposable disposable) => _parentOrDisposable = disposable;
  39. public void Dispose()
  40. {
  41. switch (_parentOrDisposable)
  42. {
  43. case IDisposable d: d.Dispose(); break;
  44. case IAsyncGate g: g.Release(); break;
  45. }
  46. _parentOrDisposable = null;
  47. }
  48. }
  49. }