Next.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. #if !NO_PERF
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reactive.Threading;
  6. using System.Threading;
  7. namespace System.Reactive.Linq.Observαble
  8. {
  9. class Next<TSource> : PushToPullAdapter<TSource, TSource>
  10. {
  11. public Next(IObservable<TSource> source)
  12. : base(source)
  13. {
  14. }
  15. protected override PushToPullSink<TSource, TSource> Run(IDisposable subscription)
  16. {
  17. return new _(subscription);
  18. }
  19. class _ : PushToPullSink<TSource, TSource>
  20. {
  21. private readonly object _gate;
  22. #if !NO_CDS
  23. private readonly SemaphoreSlim _semaphore;
  24. #else
  25. private readonly Semaphore _semaphore;
  26. #endif
  27. public _(IDisposable subscription)
  28. : base(subscription)
  29. {
  30. _gate = new object();
  31. #if !NO_CDS
  32. _semaphore = new SemaphoreSlim(0, 1);
  33. #else
  34. _semaphore = new Semaphore(0, 1);
  35. #endif
  36. }
  37. private bool _waiting;
  38. private NotificationKind _kind;
  39. private TSource _value;
  40. private Exception _error;
  41. public override void OnNext(TSource value)
  42. {
  43. lock (_gate)
  44. {
  45. if (_waiting)
  46. {
  47. _value = value;
  48. _kind = NotificationKind.OnNext;
  49. _semaphore.Release();
  50. }
  51. _waiting = false;
  52. }
  53. }
  54. public override void OnError(Exception error)
  55. {
  56. base.Dispose();
  57. lock (_gate)
  58. {
  59. //
  60. // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end.
  61. //
  62. _error = error;
  63. _kind = NotificationKind.OnError;
  64. if (_waiting)
  65. _semaphore.Release();
  66. _waiting = false;
  67. }
  68. }
  69. public override void OnCompleted()
  70. {
  71. base.Dispose();
  72. lock (_gate)
  73. {
  74. //
  75. // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end.
  76. //
  77. _kind = NotificationKind.OnCompleted;
  78. if (_waiting)
  79. _semaphore.Release();
  80. _waiting = false;
  81. }
  82. }
  83. public override bool TryMoveNext(out TSource current)
  84. {
  85. var done = false;
  86. lock (_gate)
  87. {
  88. _waiting = true;
  89. //
  90. // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end.
  91. //
  92. done = _kind != NotificationKind.OnNext;
  93. }
  94. if (!done)
  95. {
  96. #if !NO_CDS
  97. _semaphore.Wait();
  98. #else
  99. _semaphore.WaitOne();
  100. #endif
  101. }
  102. //
  103. // When we reach this point, we released the lock and got the next notification
  104. // from the observer. We assume no concurrent calls to the TryMoveNext method
  105. // are made (per general guidance on usage of IEnumerable<T>). If the observer
  106. // enters the lock again, it should have quit it first, causing _waiting to be
  107. // set to false, hence future accesses of the lock won't set the _kind, _value,
  108. // and _error fields, until TryMoveNext is entered again and _waiting is reset
  109. // to true. In conclusion, the fields are stable for read below.
  110. //
  111. // Notice we rely on memory barrier acquire/release behavior due to the use of
  112. // the semaphore, not the lock (we're still under the lock when we release the
  113. // semaphore in the On* methods!).
  114. //
  115. switch (_kind)
  116. {
  117. case NotificationKind.OnNext:
  118. current = _value;
  119. return true;
  120. case NotificationKind.OnError:
  121. _error.Throw();
  122. break;
  123. case NotificationKind.OnCompleted:
  124. break;
  125. }
  126. current = default(TSource);
  127. return false;
  128. }
  129. }
  130. }
  131. }
  132. #endif