PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/source/library/Interlace/ReactorCore/Reactor.cs

https://bitbucket.org/VahidN/interlace
C# | 328 lines | 229 code | 66 blank | 33 comment | 16 complexity | b974573358371fbb4eeda70db1b803d0 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.IO;
  30. using System.Net;
  31. using System.Net.Sockets;
  32. using System.Text;
  33. using System.Threading;
  34. #endregion
  35. namespace Interlace.ReactorCore
  36. {
  37. public class Reactor : IReactor
  38. {
  39. TimerQueue _queue = new TimerQueue();
  40. List<ReactorSlot> _slots = new List<ReactorSlot>();
  41. bool _slotsInUse = false;
  42. public event EventHandler<ServiceExceptionEventArgs> ReactorException;
  43. public void ConnectStream(IProtocolFactory factory, string addressString, int port)
  44. {
  45. StreamSocketClientConnector connector = new StreamSocketClientConnector(this);
  46. connector.Connect(factory, addressString, port);
  47. }
  48. public void ConnectStream(IProtocolFactory factory, IPAddress address, int port)
  49. {
  50. StreamSocketClientConnector connector = new StreamSocketClientConnector(this);
  51. connector.Connect(factory, address, port);
  52. }
  53. public void ConnectStream(IProtocolFactory factory, IPEndPoint peer)
  54. {
  55. StreamSocketClientConnector connector = new StreamSocketClientConnector(this);
  56. connector.Connect(factory, peer);
  57. }
  58. public void BindDatagram(IProtocolFactory factory)
  59. {
  60. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  61. connector.Bind(factory);
  62. }
  63. public void BindDatagram(IProtocolFactory factory, int localPort)
  64. {
  65. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  66. connector.Bind(factory, localPort);
  67. }
  68. public void BindDatagram(IProtocolFactory factory, IPAddress localAddress, int localPort)
  69. {
  70. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  71. connector.Bind(factory, localAddress, localPort);
  72. }
  73. public void BindToPeerDatagram(IProtocolFactory factory, string addressString, int port)
  74. {
  75. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  76. connector.BindToPeer(factory, addressString, port);
  77. }
  78. public void BindToPeerDatagram(IProtocolFactory factory, IPAddress address, int port)
  79. {
  80. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  81. connector.BindToPeer(factory, address, port);
  82. }
  83. public void BindToPeerDatagram(IProtocolFactory factory, IPEndPoint peer)
  84. {
  85. DatagramSocketConnector connector = new DatagramSocketConnector(this);
  86. connector.BindToPeer(factory, peer);
  87. }
  88. public ConnectorHandle ListenStream(IProtocolFactory factory, int port, IPAddress address)
  89. {
  90. StreamSocketServerConnector connector = new StreamSocketServerConnector(this);
  91. connector.Listen(factory, port, address);
  92. return new ConnectorHandle(connector);
  93. }
  94. public ConnectorHandle ListenStream(IProtocolFactory factory, int port)
  95. {
  96. StreamSocketServerConnector connector = new StreamSocketServerConnector(this);
  97. connector.Listen(factory, port);
  98. return new ConnectorHandle(connector);
  99. }
  100. public ConnectorHandle ListenStream(IProtocolFactory factory)
  101. {
  102. StreamSocketServerConnector connector = new StreamSocketServerConnector(this);
  103. connector.Listen(factory);
  104. return new ConnectorHandle(connector);
  105. }
  106. public void AttachStream(IProtocolFactory factory, Stream stream)
  107. {
  108. AttachStream(factory, stream, stream);
  109. }
  110. public void AttachStream(IProtocolFactory factory, Stream readStream, Stream writeStream)
  111. {
  112. factory.StartedConnecting();
  113. Protocol protocol = factory.BuildProtocol();
  114. StreamConnection connection = new StreamConnection(this, protocol);
  115. connection.StartConnection(readStream, writeStream, protocol.ReceiveBufferSize);
  116. protocol.MakeConnection(connection);
  117. }
  118. public void AddResult(IAsyncResult result, ResultCallback callback, int integerState, object state)
  119. {
  120. ReactorSlot slot = new ReactorSlot();
  121. slot.Result = result;
  122. slot.Callback = callback;
  123. slot.State = state;
  124. slot.IsPermanent = false;
  125. slot.Handle = slot.Result.AsyncWaitHandle;
  126. if (_slotsInUse) throw new InvalidOperationException("Cross-thread use of a reactor detected.");
  127. _slots.Add(slot);
  128. }
  129. public void AddResult(IAsyncResult result, ResultCallback callback, int integerState)
  130. {
  131. AddResult(result, callback, integerState, null);
  132. }
  133. public void AddResult(IAsyncResult result, ResultCallback callback)
  134. {
  135. AddResult(result, callback, 0, null);
  136. }
  137. public void AddHandle(WaitHandle handle, ResultCallback callback, int integerState, object state)
  138. {
  139. ReactorSlot slot = new ReactorSlot();
  140. slot.Result = null;
  141. slot.Callback = callback;
  142. slot.State = state;
  143. slot.IsPermanent = false;
  144. slot.Handle = handle;
  145. if (_slotsInUse) throw new InvalidOperationException("Cross-thread use of a reactor detected.");
  146. _slots.Add(slot);
  147. }
  148. public void AddHandle(WaitHandle handle, ResultCallback callback, int integerState)
  149. {
  150. AddHandle(handle, callback, integerState, null);
  151. }
  152. public void AddHandle(WaitHandle handle, ResultCallback callback)
  153. {
  154. AddHandle(handle, callback, 0, null);
  155. }
  156. public void AddPermanentHandle(WaitHandle handle, ResultCallback callback, int integerState, object state)
  157. {
  158. ReactorSlot slot = new ReactorSlot();
  159. slot.Result = null;
  160. slot.Callback = callback;
  161. slot.State = state;
  162. slot.IsPermanent = true;
  163. slot.Handle = handle;
  164. if (_slotsInUse) throw new InvalidOperationException("Cross-thread use of a reactor detected.");
  165. _slots.Add(slot);
  166. }
  167. public void AddPermanentHandle(WaitHandle handle, ResultCallback callback, int integerState)
  168. {
  169. AddPermanentHandle(handle, callback, integerState, null);
  170. }
  171. public void AddPermanentHandle(WaitHandle handle, ResultCallback callback)
  172. {
  173. AddPermanentHandle(handle, callback, 0, null);
  174. }
  175. public void EnsureHandleRemoved(WaitHandle handle)
  176. {
  177. int i = 0;
  178. // This loop modifies the slots list in the loop body:
  179. while (i < _slots.Count)
  180. {
  181. // Check the native handle for equality:
  182. if (_slots[i].Handle.Handle.Equals(handle.Handle))
  183. {
  184. if (_slotsInUse) throw new InvalidOperationException("Cross-thread use of a reactor detected.");
  185. _slots.RemoveAt(i);
  186. }
  187. else
  188. {
  189. i++;
  190. }
  191. }
  192. }
  193. public TimerHandle AddTimer(DateTime fireAt, TimerCallback callback, object state)
  194. {
  195. return _queue.Add(fireAt, callback, state);
  196. }
  197. public TimerHandle AddTimer(TimeSpan fireAfter, TimerCallback callback, object state)
  198. {
  199. return _queue.Add(DateTime.Now + fireAfter, callback, state);
  200. }
  201. public RepeatingTimerHandle AddRepeatingTimer(TimeSpan interval, TimerCallback callback, object state)
  202. {
  203. RepeatingTimerHandle repeatingTimer =
  204. new RepeatingTimerHandle(_queue, interval, callback, state, true);
  205. return repeatingTimer;
  206. }
  207. readonly TimeSpan _forever = new TimeSpan(-1);
  208. public void RunLoopIteration()
  209. {
  210. _slotsInUse = true;
  211. try
  212. {
  213. // Build a list of handles in slot order; assume that the slots won't change index
  214. // during the run loop:
  215. WaitHandle[] handles = new WaitHandle[_slots.Count];
  216. for (int i = 0; i < _slots.Count; i++)
  217. {
  218. handles[i] = _slots[i].Handle;
  219. }
  220. // Wait on all of the slots until the next timer is ready to be fired:
  221. TimeSpan timeout = _queue.IsEmpty ?
  222. _forever : _queue.GetTimeUntilNextFireable(DateTime.Now);
  223. int signalledSlotIndex = WaitHandle.WaitAny(handles, timeout, false);
  224. if (signalledSlotIndex != WaitHandle.WaitTimeout)
  225. {
  226. // Fire off the signalled slot:
  227. ReactorSlot signalledSlot = _slots[signalledSlotIndex];
  228. if (!signalledSlot.IsPermanent) _slots.RemoveAt(signalledSlotIndex);
  229. // (The "handles" array no longer matches the slots list.)
  230. try
  231. {
  232. _slotsInUse = false;
  233. signalledSlot.Callback(signalledSlot.Result, signalledSlot.State);
  234. }
  235. catch (Exception ex)
  236. {
  237. ServiceExceptionEventArgs args =
  238. new ServiceExceptionEventArgs(ServiceExceptionKind.DuringHandler, ex);
  239. if (ReactorException != null) ReactorException(this, args);
  240. if (!args.Handled) throw;
  241. }
  242. }
  243. }
  244. finally
  245. {
  246. _slotsInUse = false;
  247. }
  248. // Fire any timers:
  249. try
  250. {
  251. _queue.FireAllFireable(DateTime.Now);
  252. }
  253. catch (Exception ex)
  254. {
  255. ServiceExceptionEventArgs args =
  256. new ServiceExceptionEventArgs(ServiceExceptionKind.DuringTimer, ex);
  257. if (ReactorException != null) ReactorException(this, args);
  258. if (!args.Handled) throw;
  259. }
  260. }
  261. }
  262. }