/// <summary>
/// Invokes the DataReceived event to alert subscribers we received data.
/// </summary>
- /// <param name="args">The arguments to supply.</param>
- protected void InvokeDataReceived(DataEventArgs args)
+ /// <param name="bytes">The bytes to supply.</param>
+ /// <param name="sendOption">The sendOption to supply.</param>
+ 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<DataEventArgs> handler = DataReceived;
if (handler != null)
/// <summary>
/// Invokes the Disconnected event to alert hooked up methods there was an error or the remote end point disconnected.
/// </summary>
- /// <param name="args">The arguments to supply.</param>
- protected void InvokeDisconnected(DisconnectedEventArgs args)
+ /// <param name="e">The exception, if any, that occured to cause this.</param>
+ 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<DisconnectedEventArgs> handler = Disconnected;
if (handler != null)
/// Invokes the NewConnection event with the supplied args.
/// </summary>
/// <param name="args">The arguments for the event.</param>
- 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<NewConnectionEventArgs> handler = NewConnection;
if (handler != null)
namespace Hazel
{
- public class DataEventArgs : EventArgs
+ public class DataEventArgs : EventArgs, IRecyclable
{
+ /// <summary>
+ /// Object pool for this event.
+ /// </summary>
+ static readonly ObjectPool<DataEventArgs> objectPool = new ObjectPool<DataEventArgs>(() => new DataEventArgs());
+
+ /// <summary>
+ /// Returns an instance of this object from the pool.
+ /// </summary>
+ /// <returns></returns>
+ internal static DataEventArgs GetObject()
+ {
+ return objectPool.GetObject();
+ }
+
/// <summary>
/// The bytes received.
/// </summary>
public object SendOption { get; private set; }
/// <summary>
- /// Creates DataEventArgs from bytes received.
+ /// Private constructor for object pool.
/// </summary>
- /// <param name="bytes"></param>
- public DataEventArgs(byte[] bytes, SendOption sendOption)
+ DataEventArgs()
+ {
+
+ }
+
+ /// <summary>
+ /// Sets the members of the arguments.
+ /// </summary>
+ /// <param name="bytes">The bytes received.</param>
+ /// <param name="sendOption">The send option used to send the data.</param>
+ internal void Set(byte[] bytes, SendOption sendOption)
{
this.Bytes = bytes;
this.SendOption = sendOption;
}
+
+ /// <summary>
+ /// Returns this object back to the object pool.
+ /// </summary>
+ public void Recycle()
+ {
+ objectPool.PutObject(this);
+ }
}
}
/// <summary>
/// Events args for disconnected events.
/// </summary>
- public class DisconnectedEventArgs
+ public class DisconnectedEventArgs : IRecyclable
{
+ /// <summary>
+ /// Object pool for this event.
+ /// </summary>
+ static readonly ObjectPool<DisconnectedEventArgs> objectPool = new ObjectPool<DisconnectedEventArgs>(() => new DisconnectedEventArgs());
+
+ /// <summary>
+ /// Returns an instance of this object from the pool.
+ /// </summary>
+ /// <returns></returns>
+ internal static DisconnectedEventArgs GetObject()
+ {
+ return objectPool.GetObject();
+ }
+
/// <summary>
/// The exception, if any, that caused the disconnect, otherwise null.
/// </summary>
public Exception Exception { get; private set; }
/// <summary>
- /// Creates a DisconnectedEventArgs from the given exception or null
+ /// Private constructor for object pool.
+ /// </summary>
+ DisconnectedEventArgs()
+ {
+
+ }
+
+ /// <summary>
+ /// Sets the given exception for the arguments.
/// </summary>
/// <param name="e">The exception if the cause.</param>
- internal DisconnectedEventArgs(Exception e)
+ internal void Set(Exception e)
{
this.Exception = e;
}
+
+ /// <summary>
+ /// Returns this object back to the object pool.
+ /// </summary>
+ public void Recycle()
+ {
+ objectPool.PutObject(this);
+ }
}
}
<Compile Include="DataEventArgs.cs" />
<Compile Include="DisconnectedEventArgs.cs" />
<Compile Include="HazelException.cs" />
+ <Compile Include="IRecyclable.cs" />
<Compile Include="NetworkConnection.cs" />
<Compile Include="NetworkConnectionListener.cs" />
<Compile Include="NetworkEndPoint.cs" />
<Compile Include="NewConnectionEventArgs.cs" />
+ <Compile Include="ObjectPool.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SendOption.cs" />
<Compile Include="SendOptionInternal.cs" />
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+ /// <summary>
+ /// Interface for all items that can be returned to an object pool.
+ /// </summary>
+ interface IRecyclable
+ {
+ /// <summary>
+ /// Returns this object back to the object pool.
+ /// </summary>
+ void Recycle();
+ }
+}
/// <summary>
/// Event args for new connection events.
/// </summary>
- public class NewConnectionEventArgs : EventArgs
+ public class NewConnectionEventArgs : EventArgs, IRecyclable
{
+ /// <summary>
+ /// Object pool for this event.
+ /// </summary>
+ static readonly ObjectPool<NewConnectionEventArgs> objectPool = new ObjectPool<NewConnectionEventArgs>(() => new NewConnectionEventArgs());
+
+ /// <summary>
+ /// Returns an instance of this object from the pool.
+ /// </summary>
+ /// <returns></returns>
+ internal static NewConnectionEventArgs GetObject()
+ {
+ return objectPool.GetObject();
+ }
+
/// <summary>
/// The new connection.
/// </summary>
public Connection Connection { get; private set; }
- internal NewConnectionEventArgs(Connection Connection)
+ /// <summary>
+ /// Private constructor for thread pool.
+ /// </summary>
+ NewConnectionEventArgs()
+ {
+
+ }
+
+ /// <summary>
+ /// Sets the members of the arguments.
+ /// </summary>
+ /// <param name="Connection"></param>
+ internal void Set(Connection Connection)
{
this.Connection = Connection;
}
+
+ /// <summary>
+ /// Returns this object back to the object pool.
+ /// </summary>
+ public void Recycle()
+ {
+ objectPool.PutObject(this);
+ }
}
}
--- /dev/null
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+ /// <summary>
+ /// A fairly simple object pool for items that will be created a lot.
+ /// </summary>
+ /// <typeparam name="T">The type that is pooled.</typeparam>
+ class ObjectPool<T> where T : IRecyclable
+ {
+ /// <summary>
+ /// Our pool of objects
+ /// </summary>
+ ConcurrentBag<T> pool = new ConcurrentBag<T>();
+
+ /// <summary>
+ /// The generator for creating new objects.
+ /// </summary>
+ /// <returns></returns>
+ Func<T> objectFactory;
+
+ /// <summary>
+ /// Internal constructor for our ObjectPool.
+ /// </summary>
+ internal ObjectPool(Func<T> objectFactory)
+ {
+ this.objectFactory = objectFactory;
+ }
+
+ /// <summary>
+ /// Returns a pooled object of type T, if none are available another is created.
+ /// </summary>
+ /// <returns>An instance of T.</returns>
+ internal T GetObject()
+ {
+ T item;
+ if (pool.TryTake(out item))
+ return item;
+
+ return objectFactory.Invoke();
+ }
+
+ /// <summary>
+ /// Returns an object to the pool.
+ /// </summary>
+ /// <param name="item">The item to return.</param>
+ internal void PutObject(T item)
+ {
+ pool.Add(item);
+ }
+ }
+}
Statistics.LogReceive(bytes.Length, bytes.Length + 4);
//Fire DataReceived event
- InvokeDataReceived(new DataEventArgs(bytes, SendOption.OrderedFragmentedReliable));
+ InvokeDataReceived(bytes, SendOption.OrderedFragmentedReliable);
}
/// <summary>
//Invoke event outide lock if need be
if (invoke)
{
- InvokeDisconnected(new DisconnectedEventArgs(e));
+ InvokeDisconnected(e);
Dispose();
}
//Sort the event out
TcpConnection tcpConnection = new TcpConnection(tcpSocket);
- NewConnectionEventArgs args = new NewConnectionEventArgs(tcpConnection);
-
- FireNewConnectionEvent(args);
+ //Invoke
+ InvokeNewConnection(tcpConnection);
tcpConnection.StartListening();
}
}
if (buffer != null)
- InvokeDataReceived(new DataEventArgs(buffer, sendOption));
+ InvokeDataReceived(buffer, sendOption);
}
/// <summary>
//Invoke event outide lock if need be
if (invoke)
{
- InvokeDisconnected(new DisconnectedEventArgs(e));
+ InvokeDisconnected(e);
Dispose();
}
/// <summary>
/// Class to hold packet data
/// </summary>
- class Packet
+ class Packet : IRecyclable
{
+ /// <summary>
+ /// Object pool for this event.
+ /// </summary>
+ static readonly ObjectPool<Packet> objectPool = new ObjectPool<Packet>(() => new Packet());
+
+ /// <summary>
+ /// Returns an instance of this object from the pool.
+ /// </summary>
+ /// <returns></returns>
+ 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<Packet> resendAction, int timeout, Action ackCallback)
+ }
+
+ internal void Set(byte[] data, Action<Packet> resendAction, int timeout, Action ackCallback)
{
Data = data;
LastTimeout = timeout;
AckCallback = ackCallback;
+ Acknowledged = false;
+ }
+
+ /// <summary>
+ /// Returns this object back to the object pool from whence it came.
+ /// </summary>
+ public void Recycle()
+ {
+ objectPool.PutObject(this);
}
}
bytes[2] = (byte)id;
//Create packet object
- Packet packet = new Packet(
+ Packet packet = Packet.GetObject();
+ packet.Set(
bytes,
(Packet p) =>
{
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.");
if (packet.AckCallback != null)
packet.AckCallback.Invoke();
+ packet.Recycle();
+
reliableDataPacketsSent.Remove(id);
}
}
if (aware)
connection.InvokeDataReceived(buffer);
else
- FireNewConnectionEvent(new NewConnectionEventArgs(connection));
+ InvokeNewConnection(connection);
}
/// <summary>
{
throw new HazelException("Could not send data as a SocketException occured.", e);
}
+ catch (ObjectDisposedException)
+ {
+ //Keep alive timer probably ran, ignore
+ return;
+ }
}
/// <summary>
byte[] data = HandleReceive(buffer, buffer.Length);
if (data != null)
- InvokeDataReceived(new DataEventArgs(data, (SendOption)buffer[0]));
+ InvokeDataReceived(data, (SendOption)buffer[0]);
}
/// <summary>
//Invoke event outide lock if need be
if (invoke)
{
- InvokeDisconnected(new DisconnectedEventArgs(e));
+ InvokeDisconnected(e);
Dispose();
}