@@ -8,7 +8,7 @@ namespace Discord | |||||
/// <summary> | /// <summary> | ||||
/// Represents a generic Discord client. | /// Represents a generic Discord client. | ||||
/// </summary> | /// </summary> | ||||
public interface IDiscordClient : IDisposable | |||||
public interface IDiscordClient : IDisposable, IAsyncDisposable | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Gets the current state of connection. | /// Gets the current state of connection. | ||||
@@ -145,9 +145,24 @@ namespace Discord.Rest | |||||
_isDisposed = true; | _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; | |||||
} | |||||
} | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public void Dispose() => Dispose(true); | public void Dispose() => Dispose(true); | ||||
public ValueTask DisposeAsync() => DisposeAsync(true); | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public Task<int> GetRecommendedShardCountAsync(RequestOptions options = null) | public Task<int> GetRecommendedShardCountAsync(RequestOptions options = null) | ||||
=> ClientHelper.GetRecommendShardCountAsync(this, options); | => ClientHelper.GetRecommendShardCountAsync(this, options); | ||||
@@ -22,7 +22,7 @@ using System.Threading.Tasks; | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
internal class DiscordRestApiClient : IDisposable | |||||
internal class DiscordRestApiClient : IDisposable, IAsyncDisposable | |||||
{ | { | ||||
private static readonly ConcurrentDictionary<string, Func<BucketIds, string>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, string>>(); | private static readonly ConcurrentDictionary<string, Func<BucketIds, string>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, string>>(); | ||||
@@ -106,8 +106,29 @@ namespace Discord.API | |||||
_isDisposed = true; | _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 void Dispose() => Dispose(true); | ||||
public ValueTask DisposeAsync() => DisposeAsync(true); | |||||
public async Task LoginAsync(TokenType tokenType, string token, RequestOptions options = null) | public async Task LoginAsync(TokenType tokenType, string token, RequestOptions options = null) | ||||
{ | { | ||||
await _stateLock.WaitAsync().ConfigureAwait(false); | await _stateLock.WaitAsync().ConfigureAwait(false); | ||||
@@ -41,6 +41,14 @@ namespace Discord.Rest | |||||
base.Dispose(disposing); | base.Dispose(disposing); | ||||
} | } | ||||
internal override async ValueTask DisposeAsync(bool disposing) | |||||
{ | |||||
if (disposing) | |||||
await ApiClient.DisposeAsync().ConfigureAwait(false); | |||||
base.Dispose(disposing); | |||||
} | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
{ | { | ||||
@@ -1,3 +1,4 @@ | |||||
using Newtonsoft.Json.Bson; | |||||
using System; | using System; | ||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
@@ -10,7 +11,7 @@ using System.Threading.Tasks; | |||||
namespace Discord.Net.Queue | namespace Discord.Net.Queue | ||||
{ | { | ||||
internal class RequestQueue : IDisposable | |||||
internal class RequestQueue : IDisposable, IAsyncDisposable | |||||
{ | { | ||||
public event Func<string, RateLimitInfo?, Task> RateLimitTriggered; | public event Func<string, RateLimitInfo?, Task> RateLimitTriggered; | ||||
@@ -143,12 +144,25 @@ namespace Discord.Net.Queue | |||||
if (!(_cancelTokenSource is null)) | if (!(_cancelTokenSource is null)) | ||||
{ | { | ||||
_cancelTokenSource.Cancel(); | _cancelTokenSource.Cancel(); | ||||
_cancelTokenSource?.Dispose(); | |||||
_cancelTokenSource.Dispose(); | |||||
_cleanupTask.GetAwaiter().GetResult(); | _cleanupTask.GetAwaiter().GetResult(); | ||||
} | } | ||||
_tokenLock?.Dispose(); | _tokenLock?.Dispose(); | ||||
_clearToken?.Dispose(); | _clearToken?.Dispose(); | ||||
_requestCancelTokenSource?.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(); | |||||
} | |||||
} | } | ||||
} | } |
@@ -402,5 +402,25 @@ namespace Discord.WebSocket | |||||
base.Dispose(disposing); | base.Dispose(disposing); | ||||
} | } | ||||
internal override ValueTask DisposeAsync(bool disposing) | |||||
{ | |||||
if (!_isDisposed) | |||||
{ | |||||
if (disposing) | |||||
{ | |||||
if (_shards != null) | |||||
{ | |||||
foreach (var client in _shards) | |||||
client?.Dispose(); | |||||
} | |||||
_connectionGroupLock?.Dispose(); | |||||
} | |||||
_isDisposed = true; | |||||
} | |||||
return base.DisposeAsync(disposing); | |||||
} | |||||
} | } | ||||
} | } |
@@ -114,6 +114,35 @@ namespace Discord.API | |||||
base.Dispose(disposing); | 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 | |||||
} | |||||
_isDisposed = true; | |||||
} | |||||
#if NETSTANDARD2_1 | |||||
await base.DisposeAsync(disposing).ConfigureAwait(false); | |||||
#else | |||||
return base.DisposeAsync(disposing); | |||||
#endif | |||||
} | |||||
public async Task ConnectAsync() | public async Task ConnectAsync() | ||||
{ | { | ||||
await _stateLock.WaitAsync().ConfigureAwait(false); | await _stateLock.WaitAsync().ConfigureAwait(false); | ||||
@@ -197,6 +197,25 @@ namespace Discord.WebSocket | |||||
base.Dispose(disposing); | 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); | |||||
} | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
{ | { | ||||