Skip to content

Commit

Permalink
fix: Multiplex transport exception (#415)
Browse files Browse the repository at this point in the history
Unitask cannot await multiple times.
reimplement accept in multiplex transport to avoid the problem.

fixes #414
  • Loading branch information
paulpach committed Oct 20, 2020
1 parent 13dc1f7 commit 6534fbb
Showing 1 changed file with 48 additions and 24 deletions.
72 changes: 48 additions & 24 deletions Assets/Mirror/Runtime/Transport/MultiplexTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;

namespace Mirror
{
Expand All @@ -10,7 +11,10 @@ public class MultiplexTransport : Transport

public Transport[] transports;

private Dictionary<UniTask<IConnection>, Transport> Accepters;
AutoResetUniTaskCompletionSource completionSource;

Queue<IConnection> acceptedConnections;
Queue<Transport> acceptedTransport;

public override IEnumerable<string> Scheme =>
transports
Expand All @@ -31,43 +35,62 @@ private Transport GetTransport()

public override async UniTask<IConnection> AcceptAsync()
{
if (Accepters == null)
if (acceptedTransport == null)
{
Accepters = new Dictionary<UniTask<IConnection>, Transport>();
acceptedTransport = new Queue<Transport>();
acceptedConnections = new Queue<IConnection>();

foreach (Transport transport in transports)
{
UniTask<IConnection> transportAccepter = transport.AcceptAsync();
Accepters[transportAccepter] = transport;
acceptedTransport.Enqueue(transport);
}
}

// that's it nobody is left to accept
if (Accepters.Count == 0)
return null;
while (true)
{
if (acceptedConnections.Count > 0)
return acceptedConnections.Dequeue();

// all transports already closed
if (acceptedTransport.Count == 0)
return null;

var tasks = Accepters.Keys;
completionSource = AutoResetUniTaskCompletionSource.Create();

// wait for any one of them to accept
var (index, connection) = await UniTask.WhenAny(tasks);
// no pending connections, accept from any transport again
while (acceptedTransport.Count > 0 && acceptedConnections.Count == 0)
{
Transport transport = acceptedTransport.Dequeue();
AcceptConnection(transport).Forget();
}

var task = tasks.ElementAt(index);
await completionSource.Task;

Transport acceptedTransport = Accepters[task];
Accepters.Remove(task);
completionSource = null;

if (connection == null)
{
// this transport closed. Get the next one
return await AcceptAsync();
}
else

}

private async UniTaskVoid AcceptConnection(Transport transport)
{
try
{
// transport may accept more connections
task = acceptedTransport.AcceptAsync();
Accepters[task] = acceptedTransport;
IConnection connection = await transport.AcceptAsync();

return connection;
if (connection != null)
{
acceptedConnections.Enqueue(connection);
acceptedTransport.Enqueue(transport);
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
finally
{
completionSource?.TrySetResult();
}
}

Expand All @@ -91,7 +114,8 @@ public override async UniTask ListenAsync()
{
IEnumerable<UniTask> tasks = from t in transports select t.ListenAsync();
await UniTask.WhenAll(tasks);
Accepters = null;
acceptedTransport = null;
acceptedConnections = null;
}

public override IEnumerable<Uri> ServerUri() =>
Expand Down

0 comments on commit 6534fbb

Please sign in to comment.