diff --git a/Hazel/Tools/PingBuffer.cs b/Hazel/Tools/PingBuffer.cs
new file mode 100644
index 0000000..40bca7b
--- /dev/null
+++ b/Hazel/Tools/PingBuffer.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace Hazel.Tools
+{
+ public class PingBuffer
+ {
+ private const ushort InvalidatingFactor = ushort.MaxValue / 2;
+
+ private struct PingInfo
+ {
+ public ushort Id;
+ public DateTime SentAt;
+ }
+
+ private PingInfo[] activePings;
+ private int head; // The location of the next usable activePing
+
+ public PingBuffer(int maxPings)
+ {
+ this.activePings = new PingInfo[maxPings];
+
+ // We don't want the first few packets to match id before we set anything.
+ for (int i = 0; i < this.activePings.Length; ++i)
+ {
+ this.activePings[i].Id = InvalidatingFactor;
+ }
+ }
+
+ public void AddPing(ushort id)
+ {
+ lock (this.activePings)
+ {
+ this.activePings[this.head].Id = id;
+ this.activePings[this.head].SentAt = DateTime.UtcNow;
+ this.head++;
+ if (this.head >= this.activePings.Length)
+ {
+ this.head = 0;
+ }
+ }
+ }
+
+ public bool TryFindPing(ushort id, out DateTime sentAt)
+ {
+ lock (this.activePings)
+ {
+ for (int i = 0; i < this.activePings.Length; ++i)
+ {
+ if (this.activePings[i].Id == id)
+ {
+ sentAt = this.activePings[i].SentAt;
+ this.activePings[i].Id += InvalidatingFactor;
+ return true;
+ }
+ }
+ }
+
+ sentAt = default;
+ return false;
+ }
+ }
+}
diff --git a/Hazel/Udp/UdpConnection.KeepAlive.cs b/Hazel/Udp/UdpConnection.KeepAlive.cs
index 75b4f1d..eaa1563 100644
--- a/Hazel/Udp/UdpConnection.KeepAlive.cs
+++ b/Hazel/Udp/UdpConnection.KeepAlive.cs
@@ -1,6 +1,6 @@
-using System;
+using Hazel.Tools;
+using System;
using System.Collections.Concurrent;
-using System.Diagnostics;
using System.Threading;
@@ -8,30 +8,6 @@ namespace Hazel.Udp
{
partial class UdpConnection
{
-
- ///
- /// Class to hold packet data
- ///
- public class PingPacket : IRecyclable
- {
- private static readonly ObjectPool PacketPool = new ObjectPool(() => new PingPacket());
-
- public readonly Stopwatch Stopwatch = new Stopwatch();
-
- internal static PingPacket GetObject()
- {
- return PacketPool.GetObject();
- }
-
- public void Recycle()
- {
- Stopwatch.Stop();
- PacketPool.PutObject(this);
- }
- }
-
- internal ConcurrentDictionary activePingPackets = new ConcurrentDictionary();
-
///
/// The interval from data being received or transmitted to a keepalive packet being sent in milliseconds.
///
@@ -63,6 +39,10 @@ public int KeepAliveInterval
public int MissingPingsUntilDisconnect { get; set; } = 6;
private volatile int pingsSinceAck = 0;
+ // TODO: Technically, Min(MissingPingsUntilDisconnect + 1, 16) would be better, but I don't want to mess with it.
+ // The real point is that we're bounding the number of active pings.
+ private PingBuffer activePings = new PingBuffer(16);
+
///
/// The timer creating keepalive pulses.
///
@@ -116,18 +96,9 @@ private void SendPing()
bytes[1] = (byte)(id >> 8);
bytes[2] = (byte)id;
- PingPacket pkt;
- if (!this.activePingPackets.TryGetValue(id, out pkt))
- {
- pkt = PingPacket.GetObject();
- if (!this.activePingPackets.TryAdd(id, pkt))
- {
- throw new Exception("This shouldn't be possible");
- }
- }
-
- pkt.Stopwatch.Restart();
-
+ // TODO: This could overwrite a date, perhaps we should track pings that are simply never ack'd?
+ this.activePings.AddPing(id);
+
WriteBytesToConnection(bytes, bytes.Length);
Statistics.LogReliableSend(0);
@@ -150,18 +121,7 @@ protected void ResetKeepAliveTimer()
///
private void DisposeKeepAliveTimer()
{
- if (this.keepAliveTimer != null)
- {
- this.keepAliveTimer.Dispose();
- }
-
- foreach (var kvp in activePingPackets)
- {
- if (this.activePingPackets.TryRemove(kvp.Key, out var pkt))
- {
- pkt.Recycle();
- }
- }
+ this.keepAliveTimer?.Dispose();
}
}
}
\ No newline at end of file
diff --git a/Hazel/Udp/UdpConnection.Reliable.cs b/Hazel/Udp/UdpConnection.Reliable.cs
index 7494913..96d23d8 100644
--- a/Hazel/Udp/UdpConnection.Reliable.cs
+++ b/Hazel/Udp/UdpConnection.Reliable.cs
@@ -427,11 +427,9 @@ private void AcknowledgeMessageId(ushort id)
this._pingMs = this._pingMs * .7f + rt * .3f;
}
}
- else if (this.activePingPackets.TryRemove(id, out PingPacket pingPkt))
+ else if (this.activePings.TryFindPing(id, out DateTime pingPkt))
{
- float rt = pingPkt.Stopwatch.ElapsedMilliseconds;
-
- pingPkt.Recycle();
+ float rt = (float)(DateTime.UtcNow - pingPkt).TotalMilliseconds;
lock (PingLock)
{