refactor(link): bound every teardown and handler wait behind one helper [CI]
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/dev-image Pipeline was successful

This commit is contained in:
justme committed 2026-10-19 10:07:36 +08:00
1 parent c05842fd91
commit d95acc6575
10 files changed
+130 -31

No files matched your search

+19 -5
View File
@@ -118,6 +118,7 @@ public sealed class WsTransport : IRpcTransport
public sealed class TlsFrameTransport : IRpcTransport
{
private const int HeaderBytes = 5;
private static readonly TimeSpan s_sendTimeout = TimeSpan.FromSeconds(20);
private readonly Stream _stream;
private readonly int _maxFrameBytes;
@@ -139,16 +140,29 @@ public sealed class TlsFrameTransport : IRpcTransport
byte[] header = new byte[HeaderBytes];
BinaryPrimitives.WriteUInt32LittleEndian(header, (uint)frame.Data.Length);
header[4] = (byte)frame.Kind;
await _sendLock.WaitAsync(ct);
// 对端黑洞时写会一直挂着并占着发送锁,心跳、时钟同步和重连都跟着卡住:超时就掐掉这条连接
using var timeout = new CancellationTokenSource(s_sendTimeout);
using CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(ct, timeout.Token);
bool lockTaken = false;
try
{
await _stream.WriteAsync(header, ct);
await _stream.WriteAsync(frame.Data, ct);
await _stream.FlushAsync(ct);
await _sendLock.WaitAsync(linked.Token);
lockTaken = true;
await _stream.WriteAsync(header, linked.Token);
await _stream.WriteAsync(frame.Data, linked.Token);
await _stream.FlushAsync(linked.Token);
}
catch (OperationCanceledException) when (timeout.IsCancellationRequested && !ct.IsCancellationRequested)
{
await _stream.DisposeAsync();
throw new TimeoutException($"peer send timed out after {s_sendTimeout.TotalSeconds:0} seconds");
}
finally
{
_sendLock.Release();
if (lockTaken)
{
_sendLock.Release();
}
}
}