Browse Source

Dont use a spinlock for DiscordClient.Block >.>.

More async adjustments.
pull/6/head
RogueException 9 years ago
parent
commit
489ad42235
3 changed files with 92 additions and 69 deletions
  1. +53
    -36
      src/Discord.Net/DiscordClient.cs
  2. +1
    -1
      src/Discord.Net/DiscordVoiceSocket.cs
  3. +38
    -32
      src/Discord.Net/DiscordWebSocket.cs

+ 53
- 36
src/Discord.Net/DiscordClient.cs View File

@@ -23,12 +23,12 @@ namespace Discord
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly Regex _userRegex, _channelRegex; private readonly Regex _userRegex, _channelRegex;
private readonly MatchEvaluator _userRegexEvaluator, _channelRegexEvaluator; private readonly MatchEvaluator _userRegexEvaluator, _channelRegexEvaluator;
private readonly ManualResetEventSlim _blockEvent;
private readonly object _disconnectEvent;
private readonly Random _rand; private readonly Random _rand;
private readonly ConcurrentQueue<Message> _pendingMessages; private readonly ConcurrentQueue<Message> _pendingMessages;
private readonly DiscordClientConfig _config; private readonly DiscordClientConfig _config;


private volatile Task _mainTask;
private volatile Task _runTask;
protected volatile string _myId, _sessionId; protected volatile string _myId, _sessionId;


/// <summary> Returns the User object for the current logged in user. </summary> /// <summary> Returns the User object for the current logged in user. </summary>
@@ -37,7 +37,7 @@ namespace Discord


/// <summary> Returns true if the user has successfully logged in and the websocket connection has been established. </summary> /// <summary> Returns true if the user has successfully logged in and the websocket connection has been established. </summary>
public bool IsConnected => _isConnected; public bool IsConnected => _isConnected;
private bool _isConnected;
private bool _isConnected, _isDisconnecting;


/// <summary> Returns true if this client was requested to disconnect. </summary> /// <summary> Returns true if this client was requested to disconnect. </summary>
public bool IsClosing => _disconnectToken.IsCancellationRequested; public bool IsClosing => _disconnectToken.IsCancellationRequested;
@@ -56,7 +56,7 @@ namespace Discord
/// <summary> Initializes a new instance of the DiscordClient class. </summary> /// <summary> Initializes a new instance of the DiscordClient class. </summary>
public DiscordClient(DiscordClientConfig config = null) public DiscordClient(DiscordClientConfig config = null)
{ {
_blockEvent = new ManualResetEventSlim(false);
_disconnectEvent = new object();
_config = config ?? new DiscordClientConfig(); _config = config ?? new DiscordClientConfig();
_isDebugMode = _config.EnableDebug; _isDebugMode = _config.EnableDebug;
_rand = new Random(); _rand = new Random();
@@ -521,7 +521,6 @@ namespace Discord


private async Task<string> ConnectInternal(string token) private async Task<string> ConnectInternal(string token)
{ {
_blockEvent.Reset();
_http.Token = token; _http.Token = token;
string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url; string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url;
if (_isDebugMode) if (_isDebugMode)
@@ -530,45 +529,62 @@ namespace Discord
await _webSocket.ConnectAsync(url).ConfigureAwait(false); await _webSocket.ConnectAsync(url).ConfigureAwait(false);
await _webSocket.Login(token).ConfigureAwait(false); await _webSocket.Login(token).ConfigureAwait(false);


_disconnectToken = new CancellationTokenSource();
if (_config.UseMessageQueue)
_mainTask = MessageQueueLoop();
else
_mainTask = _disconnectToken.Wait();
_mainTask = _mainTask.ContinueWith(async x =>
{
await _webSocket.DisconnectAsync().ConfigureAwait(false);
#if !DNXCORE50
if (_config.EnableVoice)
await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false);
#endif

//Clear send queue
Message ignored;
while (_pendingMessages.TryDequeue(out ignored)) { }

_channels.Clear();
_messages.Clear();
_roles.Clear();
_servers.Clear();
_users.Clear();

_blockEvent.Set();
_mainTask = null;
}).Unwrap();
_isConnected = true;
_runTask = Run();
return token; return token;
} }
/// <summary> Disconnects from the Discord server, canceling any pending requests. </summary> /// <summary> Disconnects from the Discord server, canceling any pending requests. </summary>
public async Task Disconnect() public async Task Disconnect()
{ {
if (_mainTask != null)
Task task = _runTask;
if (task != null)
{ {
try { _disconnectToken.Cancel(); } catch (NullReferenceException) { } try { _disconnectToken.Cancel(); } catch (NullReferenceException) { }
try { await _mainTask.ConfigureAwait(false); } catch (NullReferenceException) { }
try { await task.ConfigureAwait(false); } catch (NullReferenceException) { }
} }
} }


private async Task Run()
{
_disconnectToken = new CancellationTokenSource();

//Run Loops
Task task;
if (_config.UseMessageQueue)
task = MessageQueueLoop();
else
task = _disconnectToken.Wait();

_isConnected = true;

await task.ConfigureAwait(false);
await Cleanup();
}
//TODO: What happens if a reconnect occurs and caches havent been cleared yet? Compare to DiscordWebSocket.Cleanup()
private async Task Cleanup()
{
_isDisconnecting = true;

await _webSocket.DisconnectAsync().ConfigureAwait(false);
#if !DNXCORE50
if (_config.EnableVoice)
await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false);
#endif

Message ignored;
while (_pendingMessages.TryDequeue(out ignored)) { }

_channels.Clear();
_messages.Clear();
_roles.Clear();
_servers.Clear();
_users.Clear();

_runTask = null;
_isConnected = false;
_isDisconnecting = false;
Monitor.Pulse(_disconnectEvent);
}

//Voice //Voice
public Task JoinVoiceServer(string channelId) public Task JoinVoiceServer(string channelId)
=> JoinVoiceServer(_channels[channelId]); => JoinVoiceServer(_channels[channelId]);
@@ -647,7 +663,7 @@ namespace Discord
//Helpers //Helpers
private void CheckReady() private void CheckReady()
{ {
if (_blockEvent.IsSet)
if (_isDisconnecting)
throw new InvalidOperationException("The client is currently disconnecting."); throw new InvalidOperationException("The client is currently disconnecting.");
else if (!_isConnected) else if (!_isConnected)
throw new InvalidOperationException("The client is not currently connected to Discord"); throw new InvalidOperationException("The client is not currently connected to Discord");
@@ -669,7 +685,8 @@ namespace Discord
/// <summary> Blocking call that will not return until client has been stopped. This is mainly intended for use in console applications. </summary> /// <summary> Blocking call that will not return until client has been stopped. This is mainly intended for use in console applications. </summary>
public void Block() public void Block()
{ {
_blockEvent.Wait();
while (_isConnected)
Monitor.Wait(_disconnectEvent, TimeSpan.FromSeconds(3));
} }
} }
} }

+ 1
- 1
src/Discord.Net/DiscordVoiceSocket.cs View File

@@ -129,7 +129,7 @@ namespace Discord
await base.BeginConnect().ConfigureAwait(false); await base.BeginConnect().ConfigureAwait(false);
var cancelToken = _disconnectToken.Token; var cancelToken = _disconnectToken.Token;


await Task.Factory.StartNew(() =>
await Task.Run(() =>
{ {
try try
{ {


+ 38
- 32
src/Discord.Net/DiscordWebSocket.cs View File

@@ -27,7 +27,7 @@ namespace Discord
protected ExceptionDispatchInfo _disconnectReason; protected ExceptionDispatchInfo _disconnectReason;
private ClientWebSocket _webSocket; private ClientWebSocket _webSocket;
private DateTime _lastHeartbeat; private DateTime _lastHeartbeat;
private Task _task;
private Task _runTask;
private bool _isConnected, _wasDisconnectUnexpected; private bool _isConnected, _wasDisconnectUnexpected;


public DiscordWebSocket(DiscordClient client, int timeout, int interval, bool isDebug) public DiscordWebSocket(DiscordClient client, int timeout, int interval, bool isDebug)
@@ -60,44 +60,17 @@ namespace Discord


OnConnect(); OnConnect();


_lastHeartbeat = DateTime.UtcNow;
_task = Task.Factory.ContinueWhenAll(CreateTasks(), x =>
{
if (_isDebug)
RaiseOnDebugMessage(DebugMessageType.Connection, $"Disconnected.");

//Do not clean up until all tasks have ended
OnDisconnect();

bool wasUnexpected = _wasDisconnectUnexpected;
_disconnectToken.Dispose();
_disconnectToken = null;
_wasDisconnectUnexpected = false;

//Clear send queue
_heartbeatInterval = 0;
_lastHeartbeat = DateTime.MinValue;
_webSocket.Dispose();
_webSocket = null;
byte[] ignored;
while (_sendQueue.TryDequeue(out ignored)) { }
_task = null;
if (_isConnected)
{
_isConnected = false;
RaiseDisconnected(wasUnexpected);
}
});
_runTask = Run();
} }
public Task ReconnectAsync() public Task ReconnectAsync()
=> ConnectAsync(_host); => ConnectAsync(_host);
public async Task DisconnectAsync() public async Task DisconnectAsync()
{ {
if (_task != null)
Task task = _runTask;
if (task != null)
{ {
try { DisconnectInternal(new Exception("Disconnect requested by user."), false); } catch (NullReferenceException) { } try { DisconnectInternal(new Exception("Disconnect requested by user."), false); } catch (NullReferenceException) { }
try { await _task.ConfigureAwait(false); } catch (NullReferenceException) { }
try { await task.ConfigureAwait(false); } catch (NullReferenceException) { }
} }
} }
@@ -120,6 +93,39 @@ namespace Discord
RaiseConnected(); RaiseConnected();
} }


private async Task Run()
{
_lastHeartbeat = DateTime.UtcNow;

await Task.WhenAll(CreateTasks());
Cleanup();
}
private void Cleanup()
{
if (_isDebug)
RaiseOnDebugMessage(DebugMessageType.Connection, $"Disconnected.");
OnDisconnect();

bool wasUnexpected = _wasDisconnectUnexpected;
_disconnectToken.Dispose();
_disconnectToken = null;
_wasDisconnectUnexpected = false;
_heartbeatInterval = 0;
_lastHeartbeat = DateTime.MinValue;
_webSocket.Dispose();
_webSocket = null;
byte[] ignored;
while (_sendQueue.TryDequeue(out ignored)) { }

_runTask = null;
if (_isConnected)
{
_isConnected = false;
RaiseDisconnected(wasUnexpected);
}
}

protected virtual Task[] CreateTasks() protected virtual Task[] CreateTasks()
{ {
return new Task[] return new Task[]


Loading…
Cancel
Save