fix: Fixes issue with wepoll losing GCHandle. (#1039)
- [X] Fixes issue with wepoll losing GCHandle. - [X] `NetState.Disconnect()` is no longer thread safe. - Use `Core.LoopContext.Post()` to post disconnects - [X] Optimizes PollGroup by not processing IntPtr -> GCHandle for discard polls.
This commit is contained in:
parent
d261973cee
commit
aeec7f78fd
3 changed files with 72 additions and 25 deletions
37
Projects/Server.Tests/Tests/Network/PollGroupTests.cs
Normal file
37
Projects/Server.Tests/Tests/Network/PollGroupTests.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using Server.Network;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Server.Tests.Network;
|
||||||
|
|
||||||
|
public class PollGroupTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestPollGroup()
|
||||||
|
{
|
||||||
|
// var group = new KQueuePollGroup();
|
||||||
|
var nss = new NetState[2048];
|
||||||
|
var handles = new IntPtr[2048];
|
||||||
|
for (var i = 0; i < nss.Length; i++)
|
||||||
|
{
|
||||||
|
nss[i] = PacketTestUtilities.CreateTestNetState();
|
||||||
|
handles[i] = (IntPtr)nss[i].Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
GC.AddMemoryPressure(10000000000);
|
||||||
|
GC.Collect();
|
||||||
|
GC.RemoveMemoryPressure(10000000000);
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
|
||||||
|
for (var i = 0; i < nss.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.Equal(nss[i].Handle, (GCHandle)handles[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// group.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
@ -51,9 +50,9 @@ public partial class NetState : IComparable<NetState>
|
||||||
|
|
||||||
private static GCHandle[] _polledStates = new GCHandle[2048];
|
private static GCHandle[] _polledStates = new GCHandle[2048];
|
||||||
private static readonly IPollGroup _pollGroup = PollGroup.Create();
|
private static readonly IPollGroup _pollGroup = PollGroup.Create();
|
||||||
private static readonly Queue<NetState> FlushPending = new(2048);
|
private static readonly Queue<NetState> _flushPending = new(2048);
|
||||||
private static readonly Queue<NetState> FlushedPartials = new(2048);
|
private static readonly Queue<NetState> _flushedPartials = new(256);
|
||||||
private static readonly ConcurrentQueue<NetState> Disposed = new();
|
private static readonly Queue<NetState> _disposed = new(256);
|
||||||
|
|
||||||
public static NetStateCreatedCallback CreatedCallback { get; set; }
|
public static NetStateCreatedCallback CreatedCallback { get; set; }
|
||||||
|
|
||||||
|
|
@ -75,6 +74,8 @@ public partial class NetState : IComparable<NetState>
|
||||||
internal GCHandle _handle;
|
internal GCHandle _handle;
|
||||||
private bool _packetLogging;
|
private bool _packetLogging;
|
||||||
|
|
||||||
|
public GCHandle Handle => _handle;
|
||||||
|
|
||||||
internal enum ParserState
|
internal enum ParserState
|
||||||
{
|
{
|
||||||
AwaitingNextPacket,
|
AwaitingNextPacket,
|
||||||
|
|
@ -508,7 +509,7 @@ public partial class NetState : IComparable<NetState>
|
||||||
|
|
||||||
if (!_flushQueued)
|
if (!_flushQueued)
|
||||||
{
|
{
|
||||||
FlushPending.Enqueue(this);
|
_flushPending.Enqueue(this);
|
||||||
_flushQueued = true;
|
_flushQueued = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -930,15 +931,15 @@ public partial class NetState : IComparable<NetState>
|
||||||
|
|
||||||
public static void FlushAll()
|
public static void FlushAll()
|
||||||
{
|
{
|
||||||
while (FlushPending.Count != 0)
|
while (_flushPending.Count != 0)
|
||||||
{
|
{
|
||||||
FlushPending.Dequeue()?.Flush();
|
_flushPending.Dequeue()?.Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Slice()
|
public static void Slice()
|
||||||
{
|
{
|
||||||
int count = _pollGroup.Poll(ref _polledStates);
|
int count = _pollGroup.Poll(_polledStates);
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -949,24 +950,34 @@ public partial class NetState : IComparable<NetState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (FlushPending.TryDequeue(out var ns))
|
while (_flushPending.TryDequeue(out var ns))
|
||||||
{
|
{
|
||||||
if (!ns.Flush())
|
if (!ns.Flush())
|
||||||
{
|
{
|
||||||
// Incomplete data, so we need to requeue
|
// Incomplete data, so we need to requeue
|
||||||
FlushedPartials.Enqueue(ns);
|
_flushedPartials.Enqueue(ns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasDisposes = !Disposed.IsEmpty;
|
var hasDisposes = false;
|
||||||
while (Disposed.TryDequeue(out var ns))
|
while (_disposed.TryDequeue(out var ns))
|
||||||
{
|
{
|
||||||
|
hasDisposes = true;
|
||||||
ns.Dispose();
|
ns.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If they weren't disconnected, requeue them
|
||||||
|
while (_flushedPartials.TryDequeue(out var ns))
|
||||||
|
{
|
||||||
|
if (ns.Running)
|
||||||
|
{
|
||||||
|
_flushPending.Enqueue(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasDisposes)
|
if (hasDisposes)
|
||||||
{
|
{
|
||||||
_pollGroup.Poll(ref _polledStates);
|
_pollGroup.Poll(_polledStates.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1025,20 +1036,19 @@ public partial class NetState : IComparable<NetState>
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
try
|
#if THREADGUARD
|
||||||
{
|
if (Thread.CurrentThread != Core.Thread)
|
||||||
if (_disconnectReason != string.Empty)
|
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to disconnect a netstate twice.");
|
Utility.PushColor(ConsoleColor.Red);
|
||||||
|
Console.WriteLine("Attempting to disconnect a netstate from an invalid thread!");
|
||||||
|
Console.WriteLine(new StackTrace());
|
||||||
|
Utility.PopColor();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
TraceException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
_disconnectReason = reason;
|
_disconnectReason = reason;
|
||||||
Disposed.Enqueue(this);
|
_disposed.Enqueue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TraceDisconnect(string reason, string ip)
|
public static void TraceDisconnect(string reason, string ip)
|
||||||
|
|
@ -1088,7 +1098,7 @@ public partial class NetState : IComparable<NetState>
|
||||||
TcpServer.Instances.Remove(this);
|
TcpServer.Instances.Remove(this);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pollGroup.Remove(Connection);
|
_pollGroup.Remove(Connection, _handle);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
</Target>
|
</Target>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
||||||
<PackageReference Include="PollGroup" Version="1.1.0" />
|
<PackageReference Include="PollGroup" Version="1.2.1" />
|
||||||
<PackageReference Include="Zlib.Bindings" Version="1.9.2" />
|
<PackageReference Include="Zlib.Bindings" Version="1.9.2" />
|
||||||
|
|
||||||
<PackageReference Include="ModernUO.Serialization.Annotations" Version="2.0.2" />
|
<PackageReference Include="ModernUO.Serialization.Annotations" Version="2.0.2" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue