Disposable.Utils.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. using System.Diagnostics.CodeAnalysis;
  5. using System.Threading;
  6. namespace System.Reactive.Disposables
  7. {
  8. internal enum TrySetSingleResult
  9. {
  10. Success,
  11. AlreadyAssigned,
  12. Disposed
  13. }
  14. public static partial class Disposable
  15. {
  16. /// <summary>
  17. /// Gets the value stored in <paramref name="fieldRef" /> or a null if
  18. /// <paramref name="fieldRef" /> was already disposed.
  19. /// </summary>
  20. internal static IDisposable? GetValue([NotNullIfNotNull(nameof(fieldRef))] /*in*/ ref IDisposable? fieldRef)
  21. {
  22. var current = Volatile.Read(ref fieldRef);
  23. return current == BooleanDisposable.True
  24. ? null
  25. : current;
  26. }
  27. /// <summary>
  28. /// Gets the value stored in <paramref name="fieldRef" /> or a no-op-Disposable if
  29. /// <paramref name="fieldRef" /> was already disposed.
  30. /// </summary>
  31. [return: NotNullIfNotNull(nameof(fieldRef))]
  32. internal static IDisposable? GetValueOrDefault([NotNullIfNotNull(nameof(fieldRef))] /*in*/ ref IDisposable? fieldRef)
  33. {
  34. var current = Volatile.Read(ref fieldRef);
  35. return current == BooleanDisposable.True
  36. ? EmptyDisposable.Instance
  37. : current;
  38. }
  39. /// <summary>
  40. /// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />.
  41. /// </summary>
  42. /// <returns>A <see cref="TrySetSingleResult"/> value indicating the outcome of the operation.</returns>
  43. internal static TrySetSingleResult TrySetSingle([NotNullIfNotNull(nameof(value))] ref IDisposable? fieldRef, IDisposable? value)
  44. {
  45. var old = Interlocked.CompareExchange(ref fieldRef, value, null);
  46. if (old == null)
  47. {
  48. return TrySetSingleResult.Success;
  49. }
  50. if (old != BooleanDisposable.True)
  51. {
  52. return TrySetSingleResult.AlreadyAssigned;
  53. }
  54. value?.Dispose();
  55. return TrySetSingleResult.Disposed;
  56. }
  57. /// <summary>
  58. /// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />. If <paramref name="fieldRef" />
  59. /// is not disposed and is assigned a different value, it will not be disposed.
  60. /// </summary>
  61. /// <returns>true if <paramref name="value" /> was successfully assigned to <paramref name="fieldRef" />.</returns>
  62. /// <returns>false <paramref name="fieldRef" /> has been disposed.</returns>
  63. internal static bool TrySetMultiple([NotNullIfNotNull(nameof(value))] ref IDisposable? fieldRef, IDisposable? value)
  64. {
  65. // Let's read the current value atomically (also prevents reordering).
  66. var old = Volatile.Read(ref fieldRef);
  67. for (; ; )
  68. {
  69. // If it is the disposed instance, dispose the value.
  70. if (old == BooleanDisposable.True)
  71. {
  72. value?.Dispose();
  73. return false;
  74. }
  75. // Atomically swap in the new value and get back the old.
  76. var b = Interlocked.CompareExchange(ref fieldRef, value, old);
  77. // If the old and new are the same, the swap was successful and we can quit
  78. if (old == b)
  79. {
  80. return true;
  81. }
  82. // Otherwise, make the old reference the current and retry.
  83. old = b;
  84. }
  85. }
  86. /// <summary>
  87. /// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />. If <paramref name="fieldRef" />
  88. /// is not disposed and is assigned a different value, it will be disposed.
  89. /// </summary>
  90. /// <returns>true if <paramref name="value" /> was successfully assigned to <paramref name="fieldRef" />.</returns>
  91. /// <returns>false <paramref name="fieldRef" /> has been disposed.</returns>
  92. internal static bool TrySetSerial([NotNullIfNotNull(nameof(value))] ref IDisposable? fieldRef, IDisposable? value)
  93. {
  94. var copy = Volatile.Read(ref fieldRef);
  95. for (; ; )
  96. {
  97. if (copy == BooleanDisposable.True)
  98. {
  99. value?.Dispose();
  100. return false;
  101. }
  102. var current = Interlocked.CompareExchange(ref fieldRef, value, copy);
  103. if (current == copy)
  104. {
  105. copy?.Dispose();
  106. return true;
  107. }
  108. copy = current;
  109. }
  110. }
  111. /// <summary>
  112. /// Disposes <paramref name="fieldRef" />.
  113. /// </summary>
  114. internal static void Dispose([NotNullIfNotNull(nameof(fieldRef))] ref IDisposable? fieldRef)
  115. {
  116. var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
  117. if (old != BooleanDisposable.True)
  118. {
  119. old?.Dispose();
  120. }
  121. }
  122. }
  123. }