MultipleAssignmentDisposable.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
  27. // to the outside world (see the Disposable property getter), so no-one can ever assign
  28. // this value to us manually.
  29. return Volatile.Read(ref _current) == BooleanDisposable.True;
  30. }
  31. }
  32. /// <summary>
  33. /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
  34. /// </summary>
  35. /// <remarks>If the MutableDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object.</remarks>
  36. public IDisposable Disposable
  37. {
  38. get
  39. {
  40. var a = Volatile.Read(ref _current);
  41. // Don't leak the DISPOSED sentinel
  42. if (a == BooleanDisposable.True)
  43. {
  44. a = DefaultDisposable.Instance;
  45. }
  46. return a;
  47. }
  48. set
  49. {
  50. // Let's read the current value atomically (also prevents reordering).
  51. var old = Volatile.Read(ref _current);
  52. for (;;)
  53. {
  54. // If it is the disposed instance, dispose the value.
  55. if (old == BooleanDisposable.True)
  56. {
  57. value?.Dispose();
  58. return;
  59. }
  60. // Atomically swap in the new value and get back the old.
  61. var b = Interlocked.CompareExchange(ref _current, value, old);
  62. // If the old and new are the same, the swap was successful and we can quit
  63. if (old == b)
  64. {
  65. return;
  66. }
  67. // Otherwise, make the old reference the current and retry.
  68. old = b;
  69. }
  70. }
  71. }
  72. /// <summary>
  73. /// Disposes the underlying disposable as well as all future replacements.
  74. /// </summary>
  75. public void Dispose()
  76. {
  77. // Read the current atomically.
  78. var a = Volatile.Read(ref _current);
  79. // If it is the disposed instance, don't bother further.
  80. if (a != BooleanDisposable.True)
  81. {
  82. // Atomically swap in the disposed instance.
  83. a = Interlocked.Exchange(ref _current, BooleanDisposable.True);
  84. // It is possible there was a concurrent Dispose call so don't need to call Dispose()
  85. // on DISPOSED
  86. if (a != BooleanDisposable.True)
  87. {
  88. a?.Dispose();
  89. }
  90. }
  91. }
  92. }
  93. }