MultipleAssignmentDisposable.cs 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Threading;
  5. namespace System.Reactive.Disposables
  6. {
  7. /// <summary>
  8. /// Represents a disposable resource whose underlying disposable resource can be swapped for another disposable resource.
  9. /// </summary>
  10. public sealed class MultipleAssignmentDisposable : ICancelable
  11. {
  12. private IDisposable _current;
  13. /// <summary>
  14. /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.MultipleAssignmentDisposable"/> class with no current underlying disposable.
  15. /// </summary>
  16. public MultipleAssignmentDisposable()
  17. {
  18. }
  19. /// <summary>
  20. /// Gets a value that indicates whether the object is disposed.
  21. /// </summary>
  22. public bool IsDisposed
  23. {
  24. get
  25. {
  26. return Volatile.Read(ref _current) == BooleanDisposable.True;
  27. }
  28. }
  29. /// <summary>
  30. /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
  31. /// </summary>
  32. /// <remarks>If the MutableDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object.</remarks>
  33. public IDisposable Disposable
  34. {
  35. get
  36. {
  37. var a = Volatile.Read(ref _current);
  38. // Don't leak the DISPOSED sentinel
  39. if (a == BooleanDisposable.True)
  40. {
  41. a = DefaultDisposable.Instance;
  42. }
  43. return a;
  44. }
  45. set
  46. {
  47. // Let's read the current value atomically (also prevents reordering).
  48. var old = Volatile.Read(ref _current);
  49. for (;;)
  50. {
  51. // If it is the disposed instance, dispose the value.
  52. if (old == BooleanDisposable.True)
  53. {
  54. value?.Dispose();
  55. return;
  56. }
  57. // Atomically swap in the new value and get back the old.
  58. var b = Interlocked.CompareExchange(ref _current, value, old);
  59. // If the old and new are the same, the swap was successful and we can quit
  60. if (old == b)
  61. {
  62. return;
  63. }
  64. // Otherwise, make the old reference the current and retry.
  65. old = b;
  66. }
  67. }
  68. }
  69. /// <summary>
  70. /// Disposes the underlying disposable as well as all future replacements.
  71. /// </summary>
  72. public void Dispose()
  73. {
  74. // Read the current atomically.
  75. var a = Volatile.Read(ref _current);
  76. // If it is the disposed instance, don't bother further.
  77. if (a != BooleanDisposable.True)
  78. {
  79. // Atomically swap in the disposed instance.
  80. a = Interlocked.Exchange(ref _current, BooleanDisposable.True);
  81. // It is possible there was a concurrent Dispose call so don't need to call Dispose()
  82. // on DISPOSED
  83. if (a != BooleanDisposable.True)
  84. {
  85. a?.Dispose();
  86. }
  87. }
  88. }
  89. }
  90. }