See comment on issue #3: TcpRpcClient+TcpRpcServer should retry if socket cannot be bound due to SocketError.AddressAlreadyInUse

This commit is contained in:
Christian Köllner 2019-07-07 19:59:31 +02:00
parent eaecfda35e
commit 31af304f09
7 changed files with 92 additions and 53 deletions

View File

@ -63,7 +63,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
try try
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
} }
@ -92,7 +92,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -111,7 +111,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -129,7 +129,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -170,7 +170,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -209,7 +209,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
try try
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -253,7 +253,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
try try
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -312,7 +312,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -349,7 +349,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -422,7 +422,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -498,7 +498,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -615,7 +615,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);
@ -640,7 +640,17 @@ namespace Capnp.Net.Runtime.Tests
args2.SetStruct(1, 0); args2.SetStruct(1, 0);
args2.WriteData(0, 654321); args2.WriteData(0, 654321);
Assert.ThrowsException<System.ObjectDisposedException>(() => pipelined.Call(0x8765432187654321, 0x4444, args2, false)); try
{
pipelined.Call(0x8765432187654321, 0x4444, args2, false);
Assert.Fail("Expected an exception here");
}
catch (ObjectDisposedException)
{
}
catch (TaskCanceledException)
{
}
} }
} }
@ -652,7 +662,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); client.WhenConnected.Wait();
SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout);
Assert.AreEqual(1, server.ConnectionCount); Assert.AreEqual(1, server.ConnectionCount);

View File

@ -25,7 +25,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = SetupClient()) using (var client = SetupClient())
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestInterface>()) using (var main = client.GetMain<ITestInterface>())
{ {
@ -101,7 +101,7 @@ namespace Capnp.Net.Runtime.Tests
using (var client = SetupClient()) using (var client = SetupClient())
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestInterface>()) using (var main = client.GetMain<ITestInterface>())
{ {

View File

@ -102,7 +102,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestInterface>()) using (var main = client.GetMain<ITestInterface>())
{ {
@ -152,7 +152,7 @@ namespace Capnp.Net.Runtime.Tests
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestPipeline>()) using (var main = client.GetMain<ITestPipeline>())
{ {
@ -205,7 +205,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -271,7 +271,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -361,7 +361,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestTailCaller>()) using (var main = client.GetMain<ITestTailCaller>())
{ {
@ -417,7 +417,7 @@ namespace Capnp.Net.Runtime.Tests
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -462,7 +462,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -528,7 +528,7 @@ namespace Capnp.Net.Runtime.Tests
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -621,7 +621,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var destructionPromise = new TaskCompletionSource<int>(); var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task; var destructionTask = destructionPromise.Task;
@ -679,7 +679,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -836,7 +836,7 @@ namespace Capnp.Net.Runtime.Tests
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -929,7 +929,7 @@ namespace Capnp.Net.Runtime.Tests
label: label:
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {
@ -1002,7 +1002,7 @@ namespace Capnp.Net.Runtime.Tests
{ {
using (var client = new TcpRpcClient("localhost", TcpPort)) using (var client = new TcpRpcClient("localhost", TcpPort))
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>()) using (var main = client.GetMain<ITestMoreStuff>())
{ {

View File

@ -22,7 +22,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestInterfaceImpl(counters); server.Main = new TestInterfaceImpl(counters);
@ -52,7 +52,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestPipelineImpl(counters); server.Main = new TestPipelineImpl(counters);
@ -87,7 +87,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters); server.Main = new TestMoreStuffImpl(counters);
@ -120,7 +120,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters); server.Main = new TestMoreStuffImpl(counters);
@ -159,7 +159,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestTailCallerImpl(counters); server.Main = new TestTailCallerImpl(counters);
@ -196,7 +196,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters); server.Main = new TestMoreStuffImpl(counters);
@ -226,7 +226,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);
@ -268,7 +268,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var destructionPromise = new TaskCompletionSource<int>(); var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task; var destructionTask = destructionPromise.Task;
@ -345,7 +345,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var destructionPromise = new TaskCompletionSource<int>(); var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task; var destructionTask = destructionPromise.Task;
@ -388,7 +388,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var destructionPromise = new TaskCompletionSource<int>(); var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task; var destructionTask = destructionPromise.Task;
@ -435,7 +435,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);
@ -496,7 +496,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);
@ -553,7 +553,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);
@ -590,7 +590,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);

View File

@ -43,7 +43,7 @@ namespace Capnp.Net.Runtime.Tests
using (server) using (server)
using (client) using (client)
{ {
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout)); client.WhenConnected.Wait();
var counters = new Counters(); var counters = new Counters();
var impl = new TestMoreStuffImpl(counters); var impl = new TestMoreStuffImpl(counters);

View File

@ -55,13 +55,22 @@ namespace Capnp.Rpc
async Task ConnectAsync(string host, int port) async Task ConnectAsync(string host, int port)
{ {
try for (int retry = 0; ; retry++)
{ {
await _client.ConnectAsync(host, port); try
} {
catch (SocketException exception) await _client.ConnectAsync(host, port);
{
throw new RpcException("TcpRpcClient is unable to connect", exception); return;
}
catch (SocketException exception) when (retry < 240 && exception.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
await Task.Delay(1000);
}
catch (SocketException exception)
{
throw new RpcException("TcpRpcClient is unable to connect", exception);
}
} }
} }

View File

@ -169,9 +169,16 @@ namespace Capnp.Rpc
} }
} }
if (!_acceptorThread.Join(500)) try
{ {
Logger.LogError("Unable to join TCP acceptor thread within timeout"); if (!_acceptorThread.Join(500))
{
Logger.LogError("Unable to join TCP acceptor thread within timeout");
}
}
catch (ThreadStateException)
{
// If acceptor thread was not yet started this is not a problem. Ignore.
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -189,7 +196,20 @@ namespace Capnp.Rpc
_rpcEngine = new RpcEngine(); _rpcEngine = new RpcEngine();
_listener = new TcpListener(localAddr, port); _listener = new TcpListener(localAddr, port);
_listener.ExclusiveAddressUse = false; _listener.ExclusiveAddressUse = false;
_listener.Start();
for (int retry = 0; retry < 5; retry++)
{
try
{
_listener.Start();
break;
}
catch (SocketException socketException)
{
Logger.LogWarning($"Failed to listen on port {port}, attempt {retry}: {socketException}");
Thread.Sleep(10);
}
}
_acceptorThread = new Thread(AcceptClients); _acceptorThread = new Thread(AcceptClients);