diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs
index f6981d552..14e156769 100644
--- a/src/Discord.Net.Core/IDiscordClient.cs
+++ b/src/Discord.Net.Core/IDiscordClient.cs
@@ -8,7 +8,7 @@ namespace Discord
///
/// Represents a generic Discord client.
///
- public interface IDiscordClient : IDisposable
+ public interface IDiscordClient : IDisposable, IAsyncDisposable
{
///
/// Gets the current state of connection.
diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs
index 525875232..2bf08e3c7 100644
--- a/src/Discord.Net.Rest/BaseDiscordClient.cs
+++ b/src/Discord.Net.Rest/BaseDiscordClient.cs
@@ -149,9 +149,24 @@ namespace Discord.Rest
_isDisposed = true;
}
}
+
+ internal virtual async ValueTask DisposeAsync(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+#pragma warning disable IDISP007
+ await ApiClient.DisposeAsync().ConfigureAwait(false);
+#pragma warning restore IDISP007
+ _stateLock?.Dispose();
+ _isDisposed = true;
+ }
+ }
+
///
public void Dispose() => Dispose(true);
+ public ValueTask DisposeAsync() => DisposeAsync(true);
+
///
public Task GetRecommendedShardCountAsync(RequestOptions options = null)
=> ClientHelper.GetRecommendShardCountAsync(this, options);
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index c2f2fbc99..c9a4d6c30 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -21,7 +21,7 @@ using System.Threading.Tasks;
namespace Discord.API
{
- internal class DiscordRestApiClient : IDisposable
+ internal class DiscordRestApiClient : IDisposable, IAsyncDisposable
{
#region DiscordRestApiClient
private static readonly ConcurrentDictionary> _bucketIdGenerators = new ConcurrentDictionary>();
@@ -97,8 +97,29 @@ namespace Discord.API
_isDisposed = true;
}
}
+
+ internal virtual async ValueTask DisposeAsync(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _loginCancelToken?.Dispose();
+ RestClient?.Dispose();
+
+ if (!(RequestQueue is null))
+ await RequestQueue.DisposeAsync().ConfigureAwait(false);
+
+ _stateLock?.Dispose();
+ }
+ _isDisposed = true;
+ }
+ }
+
public void Dispose() => Dispose(true);
+ public ValueTask DisposeAsync() => DisposeAsync(true);
+
public async Task LoginAsync(TokenType tokenType, string token, RequestOptions options = null)
{
await _stateLock.WaitAsync().ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs
index 93183161b..cd2033c8c 100644
--- a/src/Discord.Net.Rest/DiscordRestClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestClient.cs
@@ -47,6 +47,14 @@ namespace Discord.Rest
base.Dispose(disposing);
}
+ internal override async ValueTask DisposeAsync(bool disposing)
+ {
+ if (disposing)
+ await ApiClient.DisposeAsync().ConfigureAwait(false);
+
+ base.Dispose(disposing);
+ }
+
///
internal override async Task OnLoginAsync(TokenType tokenType, string token)
{
diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
index 2bf8e20b0..75e79eec2 100644
--- a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
+++ b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
@@ -1,3 +1,4 @@
+using Newtonsoft.Json.Bson;
using System;
using System.Collections.Concurrent;
#if DEBUG_LIMITS
@@ -10,7 +11,7 @@ using System.Threading.Tasks;
namespace Discord.Net.Queue
{
- internal class RequestQueue : IDisposable
+ internal class RequestQueue : IDisposable, IAsyncDisposable
{
public event Func RateLimitTriggered;
@@ -187,13 +188,31 @@ namespace Discord.Net.Queue
await Task.Delay(60000, _cancelTokenSource.Token).ConfigureAwait(false); //Runs each minute
}
}
- catch (OperationCanceledException) { }
+ catch (TaskCanceledException) { }
catch (ObjectDisposedException) { }
}
public void Dispose()
{
- _cancelTokenSource?.Dispose();
+ if (!(_cancelTokenSource is null))
+ {
+ _cancelTokenSource.Cancel();
+ _cancelTokenSource.Dispose();
+ _cleanupTask.GetAwaiter().GetResult();
+ }
+ _tokenLock?.Dispose();
+ _clearToken?.Dispose();
+ _requestCancelTokenSource?.Dispose();
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ if (!(_cancelTokenSource is null))
+ {
+ _cancelTokenSource.Cancel();
+ _cancelTokenSource.Dispose();
+ await _cleanupTask.ConfigureAwait(false);
+ }
_tokenLock?.Dispose();
_clearToken?.Dispose();
_requestCancelTokenSource?.Dispose();
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index 9a13c8ff8..6e943dde3 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -568,6 +568,25 @@ namespace Discord.WebSocket
base.Dispose(disposing);
}
+
+ internal override ValueTask DisposeAsync(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ if (_shards != null)
+ {
+ foreach (var client in _shards)
+ client?.Dispose();
+ }
+ }
+
+ _isDisposed = true;
+ }
+
+ return base.DisposeAsync(disposing);
+ }
#endregion
}
}
diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
index 150da9d89..bf15967d3 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
@@ -124,12 +124,39 @@ namespace Discord.API
_decompressor?.Dispose();
_compressed?.Dispose();
}
- _isDisposed = true;
}
base.Dispose(disposing);
}
+#if NETSTANDARD2_1
+ internal override async ValueTask DisposeAsync(bool disposing)
+#else
+ internal override ValueTask DisposeAsync(bool disposing)
+#endif
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ _connectCancelToken?.Dispose();
+ (WebSocketClient as IDisposable)?.Dispose();
+#if NETSTANDARD2_1
+ if (!(_decompressor is null))
+ await _decompressor.DisposeAsync().ConfigureAwait(false);
+#else
+ _decompressor?.Dispose();
+#endif
+ }
+ }
+
+#if NETSTANDARD2_1
+ await base.DisposeAsync(disposing).ConfigureAwait(false);
+#else
+ return base.DisposeAsync(disposing);
+#endif
+ }
+
public async Task ConnectAsync()
{
await _stateLock.WaitAsync().ConfigureAwait(false);
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index fa93239b5..69c16f88a 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -213,6 +213,27 @@ namespace Discord.WebSocket
base.Dispose(disposing);
}
+
+ internal override async ValueTask DisposeAsync(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ await StopAsync().ConfigureAwait(false);
+
+ if (!(ApiClient is null))
+ await ApiClient.DisposeAsync().ConfigureAwait(false);
+
+ _stateLock?.Dispose();
+ }
+ _isDisposed = true;
+ }
+
+ await base.DisposeAsync(disposing).ConfigureAwait(false);
+ }
+
+ ///
internal override async Task OnLoginAsync(TokenType tokenType, string token)
{
if (_shardedClient == null && _defaultStickers.Length == 0 && AlwaysDownloadDefaultStickers)