Dont use a spinlock for DiscordClient.Block >.>.
More async adjustments.
This commit is contained in:
@@ -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();
|
_runTask = Run();
|
||||||
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;
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
_runTask = Run();
|
||||||
_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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
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[]
|
||||||
|
|||||||
Reference in New Issue
Block a user