feat: serve planning snapshots on loopback
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TrajectoryPlanningVisualization;
|
||||
|
||||
internal sealed class SseClientConnection : IDisposable
|
||||
{
|
||||
private readonly TcpClient client;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
private readonly AutoResetEvent pendingSignal = new AutoResetEvent(false);
|
||||
private readonly Task writerTask;
|
||||
private string pendingPayload;
|
||||
private int completeAfterWrite;
|
||||
private int disposed;
|
||||
|
||||
public SseClientConnection(TcpClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
this.client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
this.cancellationToken = cancellationToken;
|
||||
writerTask = Task.Run((Action)WriteLoop);
|
||||
}
|
||||
|
||||
public Task Completion => writerTask;
|
||||
|
||||
public void Offer(string payload)
|
||||
{
|
||||
if (payload == null || Volatile.Read(ref disposed) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref pendingPayload, payload);
|
||||
pendingSignal.Set();
|
||||
}
|
||||
|
||||
public void OfferTerminal(string payload)
|
||||
{
|
||||
if (payload == null || Volatile.Read(ref disposed) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref pendingPayload, payload);
|
||||
Interlocked.Exchange(ref completeAfterWrite, 1);
|
||||
pendingSignal.Set();
|
||||
}
|
||||
|
||||
public bool WaitForCompletion(TimeSpan timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
return writerTask.Wait(timeout);
|
||||
}
|
||||
catch (AggregateException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref disposed, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pendingSignal.Set();
|
||||
try
|
||||
{
|
||||
client.Close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
pendingSignal.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
NetworkStream stream = client.GetStream();
|
||||
stream.WriteTimeout = 1000;
|
||||
while (!cancellationToken.IsCancellationRequested && Volatile.Read(ref disposed) == 0)
|
||||
{
|
||||
string payload = Interlocked.Exchange(ref pendingPayload, null);
|
||||
if (payload == null)
|
||||
{
|
||||
pendingSignal.WaitOne(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(payload);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Flush();
|
||||
if (Interlocked.Exchange(ref completeAfterWrite, 0) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user