Browse Source

Fixed reconnects due to connection loss

tags/docs-0.9
RogueException 9 years ago
parent
commit
52934bf390
5 changed files with 67 additions and 52 deletions
  1. +4
    -4
      src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs
  2. +17
    -15
      src/Discord.Net/DiscordClient.cs
  3. +29
    -14
      src/Discord.Net/Helpers/TaskManager.cs
  4. +1
    -0
      src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
  5. +16
    -19
      src/Discord.Net/Net/WebSockets/WebSocket.cs

+ 4
- 4
src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs View File

@@ -177,7 +177,7 @@ namespace Discord.Net.WebSockets


if (packetLength > 0 && endpoint.Equals(_endpoint)) if (packetLength > 0 && endpoint.Equals(_endpoint))
{ {
if (_state != (int)ConnectionState.Connected)
if (_state != ConnectionState.Connected)
{ {
if (packetLength != 70) if (packetLength != 70)
return; return;
@@ -247,7 +247,7 @@ namespace Discord.Net.WebSockets
{ {
try try
{ {
while (!cancelToken.IsCancellationRequested && _state != (int)ConnectionState.Connected)
while (!cancelToken.IsCancellationRequested && _state != ConnectionState.Connected)
Thread.Sleep(1); Thread.Sleep(1);


if (cancelToken.IsCancellationRequested) if (cancelToken.IsCancellationRequested)
@@ -399,7 +399,7 @@ namespace Discord.Net.WebSockets
{ {
case VoiceOpCodes.Ready: case VoiceOpCodes.Ready:
{ {
if (_state != (int)ConnectionState.Connected)
if (_state != ConnectionState.Connected)
{ {
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_serializer); var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_serializer);
_heartbeatInterval = payload.HeartbeatInterval; _heartbeatInterval = payload.HeartbeatInterval;
@@ -488,7 +488,7 @@ namespace Discord.Net.WebSockets
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
ThrowError();
_taskManager.ThrowException();
} }
}); });
} }


+ 17
- 15
src/Discord.Net/DiscordClient.cs View File

@@ -68,8 +68,8 @@ namespace Discord
private readonly DiscordConfig _config; private readonly DiscordConfig _config;
/// <summary> Returns the current connection state of this client. </summary> /// <summary> Returns the current connection state of this client. </summary>
public ConnectionState State => (ConnectionState)_state;
private int _state;
public ConnectionState State => _state;
private ConnectionState _state;


/// <summary> Gives direct access to the underlying DiscordAPIClient. This can be used to modify objects not in cache. </summary> /// <summary> Gives direct access to the underlying DiscordAPIClient. This can be used to modify objects not in cache. </summary>
public DiscordAPIClient APIClient => _api; public DiscordAPIClient APIClient => _api;
@@ -145,7 +145,7 @@ namespace Discord
var settings = new JsonSerializerSettings(); var settings = new JsonSerializerSettings();
_webSocket.Connected += (s, e) => _webSocket.Connected += (s, e) =>
{ {
if (_state == (int)ConnectionState.Connecting)
if (_state == ConnectionState.Connecting)
EndConnect(); EndConnect();
}; };
_webSocket.Disconnected += (s, e) => _webSocket.Disconnected += (s, e) =>
@@ -277,7 +277,7 @@ namespace Discord
if (!_sentInitialLog) if (!_sentInitialLog)
SendInitialLog(); SendInitialLog();


if (State != (int)ConnectionState.Disconnected)
if (State != ConnectionState.Disconnected)
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);


_token = token; _token = token;
@@ -293,7 +293,8 @@ namespace Discord
try try
{ {
await _taskManager.Stop().ConfigureAwait(false); await _taskManager.Stop().ConfigureAwait(false);
_state = (int)ConnectionState.Connecting;
_taskManager.ClearException();
_state = ConnectionState.Connecting;


var gatewayResponse = await _api.Gateway().ConfigureAwait(false); var gatewayResponse = await _api.Gateway().ConfigureAwait(false);
string gateway = gatewayResponse.Url; string gateway = gatewayResponse.Url;
@@ -326,7 +327,7 @@ namespace Discord
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_webSocket.ThrowError(); //Throws data socket's internal error if any occured
_webSocket.TaskManager.ThrowException(); //Throws data socket's internal error if any occured
throw; throw;
} }
} }
@@ -335,15 +336,15 @@ namespace Discord
_lock.Release(); _lock.Release();
} }
} }
catch
catch (Exception ex)
{ {
await Disconnect().ConfigureAwait(false);
_taskManager.SignalError(ex, true);
throw; throw;
} }
} }
private void EndConnect() private void EndConnect()
{ {
_state = (int)ConnectionState.Connected;
_state = ConnectionState.Connected;
_connectedEvent.Set(); _connectedEvent.Set();
RaiseConnected(); RaiseConnected();
} }
@@ -352,8 +353,9 @@ namespace Discord
public Task Disconnect() => _taskManager.Stop(); public Task Disconnect() => _taskManager.Stop();
private async Task Cleanup() private async Task Cleanup()
{
if (Config.UseMessageQueue)
{
_state = ConnectionState.Disconnecting;
if (Config.UseMessageQueue)
{ {
MessageQueueItem ignored; MessageQueueItem ignored;
while (_pendingMessages.TryDequeue(out ignored)) { } while (_pendingMessages.TryDequeue(out ignored)) { }
@@ -373,8 +375,8 @@ namespace Discord
_token = null; _token = null;
_state = (int)ConnectionState.Disconnected; _state = (int)ConnectionState.Disconnected;
_disconnectedEvent.Set();
_connectedEvent.Reset(); _connectedEvent.Reset();
_disconnectedEvent.Set();
} }
private void OnReceivedEvent(WebSocketEventEventArgs e) private void OnReceivedEvent(WebSocketEventEventArgs e)
@@ -822,11 +824,11 @@ namespace Discord
{ {
switch (_state) switch (_state)
{ {
case (int)ConnectionState.Disconnecting:
case ConnectionState.Disconnecting:
throw new InvalidOperationException("The client is disconnecting."); throw new InvalidOperationException("The client is disconnecting.");
case (int)ConnectionState.Disconnected:
case ConnectionState.Disconnected:
throw new InvalidOperationException("The client is not connected to Discord"); throw new InvalidOperationException("The client is not connected to Discord");
case (int)ConnectionState.Connecting:
case ConnectionState.Connecting:
throw new InvalidOperationException("The client is connecting."); throw new InvalidOperationException("The client is connecting.");
} }
} }


+ 29
- 14
src/Discord.Net/Helpers/TaskManager.cs View File

@@ -16,8 +16,8 @@ namespace Discord
private CancellationTokenSource _cancelSource; private CancellationTokenSource _cancelSource;
private Task _task; private Task _task;


public bool WasUnexpected => _wasUnexpected;
private bool _wasUnexpected;
public bool WasUnexpected => _wasStopUnexpected;
private bool _wasStopUnexpected;


public Exception Exception => _stopReason.SourceException; public Exception Exception => _stopReason.SourceException;
private ExceptionDispatchInfo _stopReason; private ExceptionDispatchInfo _stopReason;
@@ -53,7 +53,7 @@ namespace Discord
continue; //Another thread sneaked in and started this manager before we got a lock, loop and try again continue; //Another thread sneaked in and started this manager before we got a lock, loop and try again


_stopReason = null; _stopReason = null;
_wasUnexpected = false;
_wasStopUnexpected = false;


Task[] tasksArray = tasks.ToArray(); Task[] tasksArray = tasks.ToArray();
Task<Task> anyTask = Task.WhenAny(tasksArray); Task<Task> anyTask = Task.WhenAny(tasksArray);
@@ -74,8 +74,10 @@ namespace Discord
await allTasks.ConfigureAwait(false); await allTasks.ConfigureAwait(false);


//Run the cleanup function within our lock //Run the cleanup function within our lock
await _stopAction().ConfigureAwait(false);
if (_stopAction != null)
await _stopAction().ConfigureAwait(false);
_task = null; _task = null;
_cancelSource = null;
}); });
return; return;
} }
@@ -89,7 +91,8 @@ namespace Discord
if (_task == null) return; //Are we running? if (_task == null) return; //Are we running?
if (_cancelSource.IsCancellationRequested) return; if (_cancelSource.IsCancellationRequested) return;


_cancelSource.Cancel();
if (_cancelSource != null)
_cancelSource.Cancel();
} }
} }
public Task Stop() public Task Stop()
@@ -102,7 +105,8 @@ namespace Discord
if (task == null) return TaskHelper.CompletedTask; //Are we running? if (task == null) return TaskHelper.CompletedTask; //Are we running?
if (_cancelSource.IsCancellationRequested) return task; if (_cancelSource.IsCancellationRequested) return task;


_cancelSource.Cancel();
if (_cancelSource != null)
_cancelSource.Cancel();
} }
return task; return task;
} }
@@ -111,11 +115,12 @@ namespace Discord
{ {
lock (_lock) lock (_lock)
{ {
if (_task == null) return; //Are we running?
if (_stopReason != null) return;


_stopReason = ExceptionDispatchInfo.Capture(ex); _stopReason = ExceptionDispatchInfo.Capture(ex);
_wasUnexpected = isUnexpected;
_cancelSource.Cancel();
_wasStopUnexpected = isUnexpected;
if (_cancelSource != null)
_cancelSource.Cancel();
} }
} }
public Task Error(Exception ex, bool isUnexpected = true) public Task Error(Exception ex, bool isUnexpected = true)
@@ -123,20 +128,22 @@ namespace Discord
Task task; Task task;
lock (_lock) lock (_lock)
{ {
if (_stopReason != null) return TaskHelper.CompletedTask;

//Cache the task so we still have something to await if Cleanup is run really quickly //Cache the task so we still have something to await if Cleanup is run really quickly
task = _task;
if (task == null) return TaskHelper.CompletedTask; //Are we running?
task = _task ?? TaskHelper.CompletedTask;
if (_cancelSource.IsCancellationRequested) return task; if (_cancelSource.IsCancellationRequested) return task;


_stopReason = ExceptionDispatchInfo.Capture(ex); _stopReason = ExceptionDispatchInfo.Capture(ex);
_wasUnexpected = isUnexpected;
_cancelSource.Cancel();
_wasStopUnexpected = isUnexpected;
if (_cancelSource != null)
_cancelSource.Cancel();
} }
return task; return task;
} }


/// <summary> Throws an exception if one was captured. </summary> /// <summary> Throws an exception if one was captured. </summary>
public void Throw()
public void ThrowException()
{ {
lock (_lock) lock (_lock)
{ {
@@ -144,5 +151,13 @@ namespace Discord
_stopReason.Throw(); _stopReason.Throw();
} }
} }
public void ClearException()
{
lock (_lock)
{
_stopReason = null;
_wasStopUnexpected = false;
}
}
} }
} }

+ 1
- 0
src/Discord.Net/Net/WebSockets/WS4NetEngine.cs View File

@@ -57,6 +57,7 @@ namespace Discord.Net.WebSockets
_waitUntilConnect.Reset(); _waitUntilConnect.Reset();
_webSocket.Open(); _webSocket.Open();
_waitUntilConnect.Wait(cancelToken); _waitUntilConnect.Wait(cancelToken);
_parent.TaskManager.ThrowException(); //In case our connection failed
return TaskHelper.CompletedTask; return TaskHelper.CompletedTask;
} }




+ 16
- 19
src/Discord.Net/Net/WebSockets/WebSocket.cs View File

@@ -34,8 +34,8 @@ namespace Discord.Net.WebSockets
public string Host { get { return _host; } set { _host = value; } } public string Host { get { return _host; } set { _host = value; } }
private string _host; private string _host;


public ConnectionState State => (ConnectionState)_state;
protected int _state;
public ConnectionState State => _state;
protected ConnectionState _state;


public event EventHandler Connected; public event EventHandler Connected;
private void RaiseConnected() private void RaiseConnected()
@@ -104,8 +104,9 @@ namespace Discord.Net.WebSockets
_lock.WaitOne(); _lock.WaitOne();
try try
{ {
await _taskManager.Stop().ConfigureAwait(false);
_state = (int)ConnectionState.Connecting;
await _taskManager.Stop().ConfigureAwait(false);
_taskManager.ClearException();
_state = ConnectionState.Connecting;
_cancelTokenSource = new CancellationTokenSource(); _cancelTokenSource = new CancellationTokenSource();
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token; _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token;
@@ -122,13 +123,14 @@ namespace Discord.Net.WebSockets
catch (Exception ex) catch (Exception ex)
{ {
_taskManager.SignalError(ex, true); _taskManager.SignalError(ex, true);
throw;
} }
} }
protected void EndConnect() protected void EndConnect()
{ {
try try
{ {
_state = (int)ConnectionState.Connected;
_state = ConnectionState.Connected;


_connectedEvent.Set(); _connectedEvent.Set();
RaiseConnected(); RaiseConnected();
@@ -142,17 +144,17 @@ namespace Discord.Net.WebSockets
protected abstract Task Run(); protected abstract Task Run();
protected virtual async Task Cleanup() protected virtual async Task Cleanup()
{ {
await _engine.Disconnect().ConfigureAwait(false);
var oldState = _state;
_state = ConnectionState.Disconnecting;

await _engine.Disconnect().ConfigureAwait(false);
_cancelTokenSource = null; _cancelTokenSource = null;
var oldState = _state;
_connectedEvent.Reset(); _connectedEvent.Reset();


if (oldState == (int)ConnectionState.Connected)
{
_state = (int)ConnectionState.Disconnected;
if (oldState == ConnectionState.Connected)
RaiseDisconnected(_taskManager.WasUnexpected, _taskManager.Exception); RaiseDisconnected(_taskManager.WasUnexpected, _taskManager.Exception);
}
}
_state = ConnectionState.Disconnected;
}


protected virtual Task ProcessMessage(string json) protected virtual Task ProcessMessage(string json)
{ {
@@ -176,23 +178,18 @@ namespace Discord.Net.WebSockets
{ {
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
if (_state == (int)ConnectionState.Connected)
if (_state == ConnectionState.Connected)
{ {
SendHeartbeat(); SendHeartbeat();
await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false); await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);
} }
else else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
}); });
} }
public abstract void SendHeartbeat(); public abstract void SendHeartbeat();

protected internal void ThrowError()
{
_taskManager.Throw();
}
} }
} }

Loading…
Cancel
Save