[c# epoxy] Clean up unstarted EpoxyConnections

If an EpoxyListener or EpoxyTransport is shutdown between creation of a
EpoxyConnection, but before the connection was started, calling
StopAsync() on the connection would never complete, as the connection
loop needed to run to transition the connection to Disconnected.

Now, EpoxyConnection.StopAsync() can be called before the connection has
been started to clean up the network resources.
This commit is contained in:
Christopher Warrington 2017-02-15 16:47:36 -08:00
Родитель 059a593f5f
Коммит 9b070671dd
3 изменённых файлов: 27 добавлений и 17 удалений

Просмотреть файл

@ -832,9 +832,25 @@ namespace Bond.Comm.Epoxy
public override Task StopAsync()
{
EnsureCorrectState(State.All);
shutdownTokenSource.Cancel();
networkStream.Shutdown();
if (state == State.Created)
{
// If the connection has yet to be started, we cannot rely on
// the main loop to advance states, so we just transition to
// Disconnected and signal that we've stopped.
//
// It's OK to directly transition to Disconnected. No one else
// is going to call StartAsync() on this connection, as the
// thread that is responsible for calling StartAsync() is the
// same thread that has decided to abandon the connection
// BEFORE calling StartAsync().
state = State.Disconnected;
stopTask.TrySetResult(true);
}
return stopTask.Task;
}

Просмотреть файл

@ -4,7 +4,6 @@
namespace Bond.Comm.Epoxy
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
@ -191,14 +190,16 @@ namespace Bond.Comm.Epoxy
logger,
metrics);
// TODO
// Handle a race condition: if the listener is shutdown after we've
// accepted a connection, but before we've added it to this
// collection, we need to clean up this connection on its own.
// However, we haven't started the connection yet, so
// EpoxyConnection.StopAsync() will never complete. For now, we're
// just going to leak this connection.
connections.Add(connection);
try
{
connections.Add(connection);
}
catch (InvalidOperationException)
{
logger.Site().Debug("Listener was shutdown while accepting connection from {0}. Connection has been abandoned.", connection.RemoteEndPoint);
await connection.StopAsync();
throw;
}
logger.Site().Debug("Setup server-side connection for {0}. Starting Epoxy handshake.", connection.RemoteEndPoint);
await connection.StartAsync();

Просмотреть файл

@ -305,14 +305,7 @@ namespace Bond.Comm.Epoxy
}
catch (InvalidOperationException)
{
// TODO
// Handle a race condition: if the transport is shutdown
// after we've created this connection, but before we've
// added it to this collection, we need to clean up this
// connection on its own. However, we haven't started the
// connection yet, so EpoxyConnection.StopAsync() will
// never complete. For now, we're just going to leak this
// connection.
await connection.StopAsync();
throw new InvalidOperationException("This EpoxyTransport has been stopped already.");
}