From f562f0c059323a277df8e4493f3a50ff03bcb3ca Mon Sep 17 00:00:00 2001 From: JamJar00 Date: Mon, 16 May 2016 17:51:52 +0100 Subject: [PATCH] Added recycling --- Hazel/Connection.cs | 15 ++++++--- Hazel/ConnectionListener.cs | 6 +++- Hazel/DataEventArgs.cs | 39 +++++++++++++++++++--- Hazel/DisconnectedEventArgs.cs | 36 +++++++++++++++++++-- Hazel/Hazel.csproj | 2 ++ Hazel/IRecyclable.cs | 18 +++++++++++ Hazel/NewConnectionEventArgs.cs | 38 ++++++++++++++++++++-- Hazel/ObjectPool.cs | 57 +++++++++++++++++++++++++++++++++ Hazel/TcpConnection.cs | 4 +-- Hazel/TcpConnectionListener.cs | 5 ++- Hazel/UdpClientConnection.cs | 4 +-- Hazel/UdpConnection.Reliable.cs | 41 +++++++++++++++++++++--- Hazel/UdpConnectionListener.cs | 7 +++- Hazel/UdpServerConnection.cs | 4 +-- 14 files changed, 247 insertions(+), 29 deletions(-) create mode 100644 Hazel/IRecyclable.cs create mode 100644 Hazel/ObjectPool.cs diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs index 72cd245..3418f63 100644 --- a/Hazel/Connection.cs +++ b/Hazel/Connection.cs @@ -98,9 +98,13 @@ namespace Hazel /// /// Invokes the DataReceived event to alert subscribers we received data. /// - /// The arguments to supply. - protected void InvokeDataReceived(DataEventArgs args) + /// The bytes to supply. + /// The sendOption to supply. + protected void InvokeDataReceived(byte[] bytes, SendOption sendOption) { + DataEventArgs args = DataEventArgs.GetObject(); + args.Set(bytes, sendOption); + //Make a copy to avoid race condition between null check and invocation EventHandler handler = DataReceived; if (handler != null) @@ -110,9 +114,12 @@ namespace Hazel /// /// Invokes the Disconnected event to alert hooked up methods there was an error or the remote end point disconnected. /// - /// The arguments to supply. - protected void InvokeDisconnected(DisconnectedEventArgs args) + /// The exception, if any, that occured to cause this. + protected void InvokeDisconnected(Exception e) { + DisconnectedEventArgs args = DisconnectedEventArgs.GetObject(); + args.Set(e); + //Make a copy to avoid race condition between null check and invocation EventHandler handler = Disconnected; if (handler != null) diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs index 640bf4c..60452c4 100644 --- a/Hazel/ConnectionListener.cs +++ b/Hazel/ConnectionListener.cs @@ -26,8 +26,12 @@ namespace Hazel /// Invokes the NewConnection event with the supplied args. /// /// The arguments for the event. - protected void FireNewConnectionEvent(NewConnectionEventArgs args) + protected void InvokeNewConnection(Connection connection) { + //Get new args + NewConnectionEventArgs args = NewConnectionEventArgs.GetObject(); + args.Set(connection); + //Make a copy to avoid race condition between null check and invocation EventHandler handler = NewConnection; if (handler != null) diff --git a/Hazel/DataEventArgs.cs b/Hazel/DataEventArgs.cs index 9c87cf5..a1cb059 100644 --- a/Hazel/DataEventArgs.cs +++ b/Hazel/DataEventArgs.cs @@ -12,8 +12,22 @@ using System.Text; namespace Hazel { - public class DataEventArgs : EventArgs + public class DataEventArgs : EventArgs, IRecyclable { + /// + /// Object pool for this event. + /// + static readonly ObjectPool objectPool = new ObjectPool(() => new DataEventArgs()); + + /// + /// Returns an instance of this object from the pool. + /// + /// + internal static DataEventArgs GetObject() + { + return objectPool.GetObject(); + } + /// /// The bytes received. /// @@ -25,13 +39,30 @@ namespace Hazel public object SendOption { get; private set; } /// - /// Creates DataEventArgs from bytes received. + /// Private constructor for object pool. /// - /// - public DataEventArgs(byte[] bytes, SendOption sendOption) + DataEventArgs() + { + + } + + /// + /// Sets the members of the arguments. + /// + /// The bytes received. + /// The send option used to send the data. + internal void Set(byte[] bytes, SendOption sendOption) { this.Bytes = bytes; this.SendOption = sendOption; } + + /// + /// Returns this object back to the object pool. + /// + public void Recycle() + { + objectPool.PutObject(this); + } } } diff --git a/Hazel/DisconnectedEventArgs.cs b/Hazel/DisconnectedEventArgs.cs index c4b9a0b..4468eef 100644 --- a/Hazel/DisconnectedEventArgs.cs +++ b/Hazel/DisconnectedEventArgs.cs @@ -8,20 +8,50 @@ namespace Hazel /// /// Events args for disconnected events. /// - public class DisconnectedEventArgs + public class DisconnectedEventArgs : IRecyclable { + /// + /// Object pool for this event. + /// + static readonly ObjectPool objectPool = new ObjectPool(() => new DisconnectedEventArgs()); + + /// + /// Returns an instance of this object from the pool. + /// + /// + internal static DisconnectedEventArgs GetObject() + { + return objectPool.GetObject(); + } + /// /// The exception, if any, that caused the disconnect, otherwise null. /// public Exception Exception { get; private set; } /// - /// Creates a DisconnectedEventArgs from the given exception or null + /// Private constructor for object pool. + /// + DisconnectedEventArgs() + { + + } + + /// + /// Sets the given exception for the arguments. /// /// The exception if the cause. - internal DisconnectedEventArgs(Exception e) + internal void Set(Exception e) { this.Exception = e; } + + /// + /// Returns this object back to the object pool. + /// + public void Recycle() + { + objectPool.PutObject(this); + } } } diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index b77da7b..a4cc7dc 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -52,10 +52,12 @@ + + diff --git a/Hazel/IRecyclable.cs b/Hazel/IRecyclable.cs new file mode 100644 index 0000000..c044f3c --- /dev/null +++ b/Hazel/IRecyclable.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Hazel +{ + /// + /// Interface for all items that can be returned to an object pool. + /// + interface IRecyclable + { + /// + /// Returns this object back to the object pool. + /// + void Recycle(); + } +} diff --git a/Hazel/NewConnectionEventArgs.cs b/Hazel/NewConnectionEventArgs.cs index e4b6ab8..6cdf3aa 100644 --- a/Hazel/NewConnectionEventArgs.cs +++ b/Hazel/NewConnectionEventArgs.cs @@ -8,16 +8,50 @@ namespace Hazel /// /// Event args for new connection events. /// - public class NewConnectionEventArgs : EventArgs + public class NewConnectionEventArgs : EventArgs, IRecyclable { + /// + /// Object pool for this event. + /// + static readonly ObjectPool objectPool = new ObjectPool(() => new NewConnectionEventArgs()); + + /// + /// Returns an instance of this object from the pool. + /// + /// + internal static NewConnectionEventArgs GetObject() + { + return objectPool.GetObject(); + } + /// /// The new connection. /// public Connection Connection { get; private set; } - internal NewConnectionEventArgs(Connection Connection) + /// + /// Private constructor for thread pool. + /// + NewConnectionEventArgs() + { + + } + + /// + /// Sets the members of the arguments. + /// + /// + internal void Set(Connection Connection) { this.Connection = Connection; } + + /// + /// Returns this object back to the object pool. + /// + public void Recycle() + { + objectPool.PutObject(this); + } } } diff --git a/Hazel/ObjectPool.cs b/Hazel/ObjectPool.cs new file mode 100644 index 0000000..4a05e39 --- /dev/null +++ b/Hazel/ObjectPool.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hazel +{ + /// + /// A fairly simple object pool for items that will be created a lot. + /// + /// The type that is pooled. + class ObjectPool where T : IRecyclable + { + /// + /// Our pool of objects + /// + ConcurrentBag pool = new ConcurrentBag(); + + /// + /// The generator for creating new objects. + /// + /// + Func objectFactory; + + /// + /// Internal constructor for our ObjectPool. + /// + internal ObjectPool(Func objectFactory) + { + this.objectFactory = objectFactory; + } + + /// + /// Returns a pooled object of type T, if none are available another is created. + /// + /// An instance of T. + internal T GetObject() + { + T item; + if (pool.TryTake(out item)) + return item; + + return objectFactory.Invoke(); + } + + /// + /// Returns an object to the pool. + /// + /// The item to return. + internal void PutObject(T item) + { + pool.Add(item); + } + } +} diff --git a/Hazel/TcpConnection.cs b/Hazel/TcpConnection.cs index 71eec3a..f2462f1 100644 --- a/Hazel/TcpConnection.cs +++ b/Hazel/TcpConnection.cs @@ -188,7 +188,7 @@ namespace Hazel Statistics.LogReceive(bytes.Length, bytes.Length + 4); //Fire DataReceived event - InvokeDataReceived(new DataEventArgs(bytes, SendOption.OrderedFragmentedReliable)); + InvokeDataReceived(bytes, SendOption.OrderedFragmentedReliable); } /// @@ -296,7 +296,7 @@ namespace Hazel //Invoke event outide lock if need be if (invoke) { - InvokeDisconnected(new DisconnectedEventArgs(e)); + InvokeDisconnected(e); Dispose(); } diff --git a/Hazel/TcpConnectionListener.cs b/Hazel/TcpConnectionListener.cs index f651008..29e9d08 100644 --- a/Hazel/TcpConnectionListener.cs +++ b/Hazel/TcpConnectionListener.cs @@ -85,9 +85,8 @@ namespace Hazel //Sort the event out TcpConnection tcpConnection = new TcpConnection(tcpSocket); - NewConnectionEventArgs args = new NewConnectionEventArgs(tcpConnection); - - FireNewConnectionEvent(args); + //Invoke + InvokeNewConnection(tcpConnection); tcpConnection.StartListening(); } diff --git a/Hazel/UdpClientConnection.cs b/Hazel/UdpClientConnection.cs index dbf4820..08cee33 100644 --- a/Hazel/UdpClientConnection.cs +++ b/Hazel/UdpClientConnection.cs @@ -206,7 +206,7 @@ namespace Hazel } if (buffer != null) - InvokeDataReceived(new DataEventArgs(buffer, sendOption)); + InvokeDataReceived(buffer, sendOption); } /// @@ -230,7 +230,7 @@ namespace Hazel //Invoke event outide lock if need be if (invoke) { - InvokeDisconnected(new DisconnectedEventArgs(e)); + InvokeDisconnected(e); Dispose(); } diff --git a/Hazel/UdpConnection.Reliable.cs b/Hazel/UdpConnection.Reliable.cs index da02455..de46771 100644 --- a/Hazel/UdpConnection.Reliable.cs +++ b/Hazel/UdpConnection.Reliable.cs @@ -49,15 +49,34 @@ namespace Hazel /// /// Class to hold packet data /// - class Packet + class Packet : IRecyclable { + /// + /// Object pool for this event. + /// + static readonly ObjectPool objectPool = new ObjectPool(() => new Packet()); + + /// + /// Returns an instance of this object from the pool. + /// + /// + internal static Packet GetObject() + { + return objectPool.GetObject(); + } + public byte[] Data; public Timer Timer; public volatile int LastTimeout; public Action AckCallback; - public volatile bool Acknowledged = false; + public volatile bool Acknowledged; + + Packet() + { - public Packet(byte[] data, Action resendAction, int timeout, Action ackCallback) + } + + internal void Set(byte[] data, Action resendAction, int timeout, Action ackCallback) { Data = data; @@ -70,6 +89,15 @@ namespace Hazel LastTimeout = timeout; AckCallback = ackCallback; + Acknowledged = false; + } + + /// + /// Returns this object back to the object pool from whence it came. + /// + public void Recycle() + { + objectPool.PutObject(this); } } @@ -93,7 +121,8 @@ namespace Hazel bytes[2] = (byte)id; //Create packet object - Packet packet = new Packet( + Packet packet = Packet.GetObject(); + packet.Set( bytes, (Packet p) => { @@ -103,7 +132,7 @@ namespace Hazel lock (p.Timer) { if (!p.Acknowledged) - p.Timer.Change(p.LastTimeout *= 2, Timeout.Infinite); + p.Timer.Change(p.LastTimeout *= 2, Timeout.Infinite); //TODO disconnect after x tries } Trace.WriteLine("Resend."); @@ -188,6 +217,8 @@ namespace Hazel if (packet.AckCallback != null) packet.AckCallback.Invoke(); + packet.Recycle(); + reliableDataPacketsSent.Remove(id); } } diff --git a/Hazel/UdpConnectionListener.cs b/Hazel/UdpConnectionListener.cs index 69981a2..65d8484 100644 --- a/Hazel/UdpConnectionListener.cs +++ b/Hazel/UdpConnectionListener.cs @@ -146,7 +146,7 @@ namespace Hazel if (aware) connection.InvokeDataReceived(buffer); else - FireNewConnectionEvent(new NewConnectionEventArgs(connection)); + InvokeNewConnection(connection); } /// @@ -169,6 +169,11 @@ namespace Hazel { throw new HazelException("Could not send data as a SocketException occured.", e); } + catch (ObjectDisposedException) + { + //Keep alive timer probably ran, ignore + return; + } } /// diff --git a/Hazel/UdpServerConnection.cs b/Hazel/UdpServerConnection.cs index 5ea8a49..f1008a4 100644 --- a/Hazel/UdpServerConnection.cs +++ b/Hazel/UdpServerConnection.cs @@ -85,7 +85,7 @@ namespace Hazel byte[] data = HandleReceive(buffer, buffer.Length); if (data != null) - InvokeDataReceived(new DataEventArgs(data, (SendOption)buffer[0])); + InvokeDataReceived(data, (SendOption)buffer[0]); } /// @@ -109,7 +109,7 @@ namespace Hazel //Invoke event outide lock if need be if (invoke) { - InvokeDisconnected(new DisconnectedEventArgs(e)); + InvokeDisconnected(e); Dispose(); } -- 2.39.5