feature: Implement Dispose for types which have disposable data (#1171)
* Initial set of dispose implementations Not handled yet: - Discord.Net.Websocket/Entities/SocketGuild - Discord.Net.Tests * Refactor DiscordSocketClient init into ctor This way we remove an IDisposableAnalyzer warning for not disposing the client when we set the client variable. * Dispose of clients when disposing sharded client * Finish implementing IDisposable where appropriate I opted to use NoWarn in the Tests project as it wasn't really necessary considering that our tests only run once * Tweak samples after feedback
This commit is contained in:
@@ -13,24 +13,29 @@ namespace Discord.Net.Udp
|
||||
private readonly SemaphoreSlim _lock;
|
||||
private UdpClient _udp;
|
||||
private IPEndPoint _destination;
|
||||
private CancellationTokenSource _cancelTokenSource;
|
||||
private CancellationTokenSource _stopCancelTokenSource, _cancelTokenSource;
|
||||
private CancellationToken _cancelToken, _parentToken;
|
||||
private Task _task;
|
||||
private bool _isDisposed;
|
||||
|
||||
|
||||
public ushort Port => (ushort)((_udp?.Client.LocalEndPoint as IPEndPoint)?.Port ?? 0);
|
||||
|
||||
public DefaultUdpSocket()
|
||||
{
|
||||
_lock = new SemaphoreSlim(1, 1);
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
_stopCancelTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
StopInternalAsync(true).GetAwaiter().GetResult();
|
||||
_stopCancelTokenSource?.Dispose();
|
||||
_cancelTokenSource?.Dispose();
|
||||
_lock?.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
@@ -56,9 +61,14 @@ namespace Discord.Net.Udp
|
||||
{
|
||||
await StopInternalAsync().ConfigureAwait(false);
|
||||
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||
_stopCancelTokenSource?.Dispose();
|
||||
_cancelTokenSource?.Dispose();
|
||||
|
||||
_stopCancelTokenSource = new CancellationTokenSource();
|
||||
_cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _stopCancelTokenSource.Token);
|
||||
_cancelToken = _cancelTokenSource.Token;
|
||||
|
||||
_udp?.Dispose();
|
||||
_udp = new UdpClient(0);
|
||||
|
||||
_task = RunAsync(_cancelToken);
|
||||
@@ -77,7 +87,7 @@ namespace Discord.Net.Udp
|
||||
}
|
||||
public async Task StopInternalAsync(bool isDisposing = false)
|
||||
{
|
||||
try { _cancelTokenSource.Cancel(false); } catch { }
|
||||
try { _stopCancelTokenSource.Cancel(false); } catch { }
|
||||
|
||||
if (!isDisposing)
|
||||
await (_task ?? Task.Delay(0)).ConfigureAwait(false);
|
||||
@@ -96,8 +106,11 @@ namespace Discord.Net.Udp
|
||||
}
|
||||
public void SetCancelToken(CancellationToken cancelToken)
|
||||
{
|
||||
_cancelTokenSource?.Dispose();
|
||||
|
||||
_parentToken = cancelToken;
|
||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||
_cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _stopCancelTokenSource.Token);
|
||||
_cancelToken = _cancelTokenSource.Token;
|
||||
}
|
||||
|
||||
public async Task SendAsync(byte[] data, int index, int count)
|
||||
|
||||
@@ -25,14 +25,14 @@ namespace Discord.Net.WebSockets
|
||||
private readonly IWebProxy _proxy;
|
||||
private ClientWebSocket _client;
|
||||
private Task _task;
|
||||
private CancellationTokenSource _cancelTokenSource;
|
||||
private CancellationTokenSource _disconnectTokenSource, _cancelTokenSource;
|
||||
private CancellationToken _cancelToken, _parentToken;
|
||||
private bool _isDisposed, _isDisconnecting;
|
||||
|
||||
public DefaultWebSocketClient(IWebProxy proxy = null)
|
||||
{
|
||||
_lock = new SemaphoreSlim(1, 1);
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
_disconnectTokenSource = new CancellationTokenSource();
|
||||
_cancelToken = CancellationToken.None;
|
||||
_parentToken = CancellationToken.None;
|
||||
_headers = new Dictionary<string, string>();
|
||||
@@ -43,7 +43,12 @@ namespace Discord.Net.WebSockets
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
DisconnectInternalAsync(true).GetAwaiter().GetResult();
|
||||
_disconnectTokenSource?.Dispose();
|
||||
_cancelTokenSource?.Dispose();
|
||||
_lock?.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
@@ -68,9 +73,14 @@ namespace Discord.Net.WebSockets
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||
_disconnectTokenSource?.Dispose();
|
||||
_cancelTokenSource?.Dispose();
|
||||
|
||||
_disconnectTokenSource = new CancellationTokenSource();
|
||||
_cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _disconnectTokenSource.Token);
|
||||
_cancelToken = _cancelTokenSource.Token;
|
||||
|
||||
_client?.Dispose();
|
||||
_client = new ClientWebSocket();
|
||||
_client.Options.Proxy = _proxy;
|
||||
_client.Options.KeepAliveInterval = TimeSpan.Zero;
|
||||
@@ -98,7 +108,7 @@ namespace Discord.Net.WebSockets
|
||||
}
|
||||
private async Task DisconnectInternalAsync(bool isDisposing = false)
|
||||
{
|
||||
try { _cancelTokenSource.Cancel(false); } catch { }
|
||||
try { _disconnectTokenSource.Cancel(false); } catch { }
|
||||
|
||||
_isDisconnecting = true;
|
||||
try
|
||||
@@ -117,7 +127,7 @@ namespace Discord.Net.WebSockets
|
||||
}
|
||||
try { _client.Dispose(); }
|
||||
catch { }
|
||||
|
||||
|
||||
_client = null;
|
||||
}
|
||||
}
|
||||
@@ -144,8 +154,11 @@ namespace Discord.Net.WebSockets
|
||||
}
|
||||
public void SetCancelToken(CancellationToken cancelToken)
|
||||
{
|
||||
_cancelTokenSource?.Dispose();
|
||||
|
||||
_parentToken = cancelToken;
|
||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||
_cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _disconnectTokenSource.Token);
|
||||
_cancelToken = _cancelTokenSource.Token;
|
||||
}
|
||||
|
||||
public async Task SendAsync(byte[] data, int index, int count, bool isText)
|
||||
@@ -166,7 +179,7 @@ namespace Discord.Net.WebSockets
|
||||
frameSize = count - (i * SendChunkSize);
|
||||
else
|
||||
frameSize = SendChunkSize;
|
||||
|
||||
|
||||
var type = isText ? WebSocketMessageType.Text : WebSocketMessageType.Binary;
|
||||
await _client.SendAsync(new ArraySegment<byte>(data, index, count), type, isLast, _cancelToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -176,7 +189,7 @@ namespace Discord.Net.WebSockets
|
||||
_lock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task RunAsync(CancellationToken cancelToken)
|
||||
{
|
||||
var buffer = new ArraySegment<byte>(new byte[ReceiveChunkSize]);
|
||||
@@ -188,7 +201,7 @@ namespace Discord.Net.WebSockets
|
||||
WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false);
|
||||
byte[] result;
|
||||
int resultCount;
|
||||
|
||||
|
||||
if (socketResult.MessageType == WebSocketMessageType.Close)
|
||||
throw new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription);
|
||||
|
||||
@@ -219,7 +232,7 @@ namespace Discord.Net.WebSockets
|
||||
resultCount = socketResult.Count;
|
||||
result = buffer.Array;
|
||||
}
|
||||
|
||||
|
||||
if (socketResult.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
string text = Encoding.UTF8.GetString(result, 0, resultCount);
|
||||
|
||||
Reference in New Issue
Block a user