PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/source/library/Interlace/ReactorCore/ReactorQueue.cs

https://bitbucket.org/VahidN/interlace
C# | 174 lines | 85 code | 25 blank | 64 comment | 6 complexity | 979b956396eeb93660dc9d27760d0dd0 MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using System.Threading;
  31. #endregion
  32. namespace Interlace.ReactorCore
  33. {
  34. /// <summary>
  35. /// References the callback method to be called when an item has been automatically dequeued.
  36. /// </summary>
  37. /// <typeparam name="T">The type of item contained in the queue.</typeparam>
  38. /// <param name="queue">The queue that produced the item.</param>
  39. /// <param name="item">The item that was enqueued on the queue.</param>
  40. public delegate void ReactorQueueDequeueCallback<T>(ReactorQueue<T> queue, T item);
  41. /// <summary>
  42. /// A queue for sending a single consumer thread items from a single producer thread,
  43. /// with additional support for reactors.
  44. /// </summary>
  45. /// <typeparam name="T">The type of item the queue stores.</typeparam>
  46. public class ReactorQueue<T> : IDisposable
  47. {
  48. T[] _elements;
  49. int _nextEnqueueElement;
  50. int _nextDequeueElement;
  51. Semaphore _elementsSemaphore;
  52. Semaphore _spacesSemaphore;
  53. IReactor _reactor;
  54. ReactorQueueDequeueCallback<T> _callback;
  55. object _enqueueLock = new object();
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="T:ReactorQueue&lt;T&gt;"/> class.
  58. /// </summary>
  59. /// <param name="capacity">The capacity of the queue.</param>
  60. public ReactorQueue(int capacity)
  61. {
  62. _elements = new T[capacity];
  63. _nextDequeueElement = 0;
  64. _nextDequeueElement = 0;
  65. _elementsSemaphore = new Semaphore(0, capacity);
  66. _spacesSemaphore = new Semaphore(capacity, capacity);
  67. _reactor = null;
  68. _callback = null;
  69. }
  70. /// <summary>
  71. /// Enqueues the specified item.
  72. /// </summary>
  73. /// <remarks>The calling thread will block until space is available in the queue.
  74. /// This method must only be called by a single producing thread; it is not
  75. /// itself thread-safe.</remarks>
  76. /// <param name="item">The item.</param>
  77. public void Enqueue(T item)
  78. {
  79. lock (_enqueueLock)
  80. {
  81. _spacesSemaphore.WaitOne();
  82. _elements[_nextEnqueueElement] = item;
  83. _nextEnqueueElement = (_nextEnqueueElement + 1) % _elements.Length;
  84. _elementsSemaphore.Release();
  85. }
  86. }
  87. /// <summary>
  88. /// Dequeues a single item from the queue.
  89. /// </summary>
  90. /// <remarks>The calling thread will block until an item is available to dequeue. This
  91. /// method must only be called by a single consuming thread; it is not itself
  92. /// threadsafe.</remarks>
  93. /// <returns>The item.</returns>
  94. public T Dequeue()
  95. {
  96. _elementsSemaphore.WaitOne();
  97. T item = _elements[_nextDequeueElement];
  98. _nextDequeueElement = (_nextDequeueElement + 1) % _elements.Length;
  99. _spacesSemaphore.Release();
  100. return item;
  101. }
  102. /// <summary>
  103. /// Starts automatically dequeuing any items enqueued and calls a callback
  104. /// delegate for each item.
  105. /// </summary>
  106. /// <remarks>This method must only be called in the reactor thread.</remarks>
  107. /// <param name="reactor">The reactor. Callbacks will occur in the reactor thread.</param>
  108. /// <param name="callback">The method to call for each item.</param>
  109. public void StartDequeuingToCallback(IReactor reactor, ReactorQueueDequeueCallback<T> callback)
  110. {
  111. _reactor = reactor;
  112. _callback = callback;
  113. _reactor.AddHandle(_elementsSemaphore, DequeueCompleted);
  114. }
  115. void DequeueCompleted(IAsyncResult result, object state)
  116. {
  117. T item = _elements[_nextDequeueElement];
  118. _nextDequeueElement = (_nextDequeueElement + 1) % _elements.Length;
  119. _spacesSemaphore.Release();
  120. try
  121. {
  122. _callback(this, item);
  123. }
  124. finally
  125. {
  126. _reactor.AddHandle(_elementsSemaphore, DequeueCompleted);
  127. }
  128. }
  129. /// <summary>
  130. /// Stops automatic dequeuing.
  131. /// </summary>
  132. public void StopDequeuingToCallback()
  133. {
  134. _reactor.EnsureHandleRemoved(_elementsSemaphore);
  135. _reactor = null;
  136. _callback = null;
  137. }
  138. #region IDisposable Members
  139. public void Dispose()
  140. {
  141. if (_reactor != null) StopDequeuingToCallback();
  142. if (_elementsSemaphore != null) _elementsSemaphore.Close();
  143. if (_spacesSemaphore != null) _spacesSemaphore.Close();
  144. _elementsSemaphore = null;
  145. _spacesSemaphore = null;
  146. }
  147. #endregion
  148. }
  149. }