Browse Source

Don't capture the UI thread for async methods. Use Task.Run instead of Task.Factory.StartNew.

tags/docs-0.9
RogueException 9 years ago
parent
commit
4fef9f0555
9 changed files with 197 additions and 209 deletions
  1. +3
    -3
      src/Discord.Net/API/DiscordAPI.cs
  2. +30
    -33
      src/Discord.Net/DiscordClient.API.cs
  3. +2
    -2
      src/Discord.Net/DiscordClient.Cache.cs
  4. +28
    -41
      src/Discord.Net/DiscordClient.cs
  5. +3
    -3
      src/Discord.Net/DiscordDataSocket.cs
  6. +56
    -54
      src/Discord.Net/DiscordVoiceSocket.cs
  7. +66
    -64
      src/Discord.Net/DiscordWebSocket.cs
  8. +2
    -2
      src/Discord.Net/Helpers/Extensions.cs
  9. +7
    -7
      src/Discord.Net/Helpers/JsonHttpClient.cs

+ 3
- 3
src/Discord.Net/API/DiscordAPI.cs View File

@@ -21,15 +21,15 @@ namespace Discord.API
=> _http.Get<APIResponses.Gateway>(Endpoints.Gateway);
public async Task<APIResponses.AuthRegister> LoginAnonymous(string username)
{
var fingerprintResponse = await _http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint);
var fingerprintResponse = await _http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint).ConfigureAwait(false);
var registerRequest = new APIRequests.AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username };
var registerResponse = await _http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest);
var registerResponse = await _http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest).ConfigureAwait(false);
return registerResponse;
}
public async Task<APIResponses.AuthLogin> Login(string email, string password)
{
var request = new APIRequests.AuthLogin { Email = email, Password = password };
var response = await _http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request);
var response = await _http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request).ConfigureAwait(false);
return response;
}
public Task Logout()


+ 30
- 33
src/Discord.Net/DiscordClient.API.cs View File

@@ -24,7 +24,7 @@ namespace Discord
if (name == null) throw new ArgumentNullException(nameof(name));
if (region == null) throw new ArgumentNullException(nameof(region));

var response = await _api.CreateServer(name, region);
var response = await _api.CreateServer(name, region).ConfigureAwait(false);
return _servers.Update(response.Id, response);
}

@@ -37,7 +37,7 @@ namespace Discord
CheckReady();
if (serverId == null) throw new ArgumentNullException(nameof(serverId));

try { await _api.LeaveServer(serverId); }
try { await _api.LeaveServer(serverId).ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
return _servers.Remove(serverId);
}
@@ -54,7 +54,7 @@ namespace Discord
if (name == null) throw new ArgumentNullException(nameof(name));
if (type == null) throw new ArgumentNullException(nameof(type));

var response = await _api.CreateChannel(serverId, name, type);
var response = await _api.CreateChannel(serverId, name, type).ConfigureAwait(false);
return _channels.Update(response.Id, serverId, response);
}

@@ -67,7 +67,7 @@ namespace Discord
CheckReady();
if (channelId == null) throw new ArgumentNullException(nameof(channelId));

try { await _api.DestroyChannel(channelId); }
try { await _api.DestroyChannel(channelId).ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
return _channels.Remove(channelId);
}
@@ -108,7 +108,7 @@ namespace Discord
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
if (userId == null) throw new ArgumentNullException(nameof(userId));

try { await _api.Unban(serverId, userId); }
try { await _api.Unban(serverId, userId).ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
}

@@ -139,7 +139,7 @@ namespace Discord
if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
if (maxUses <= 0) throw new ArgumentOutOfRangeException(nameof(maxUses));

var response = await _api.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass);
var response = await _api.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass).ConfigureAwait(false);
_channels.Update(response.Channel.Id, response.Server.Id, response.Channel);
_servers.Update(response.Server.Id, response.Server);
_users.Update(response.Inviter.Id, response.Inviter);
@@ -163,7 +163,7 @@ namespace Discord
CheckReady();
if (id == null) throw new ArgumentNullException(nameof(id));

var response = await _api.GetInvite(id);
var response = await _api.GetInvite(id).ConfigureAwait(false);
return new Invite(response.Code, response.XkcdPass, this)
{
ChannelId = response.Channel.Id,
@@ -195,8 +195,8 @@ namespace Discord
id = id.Substring(0, id.Length - 1);

//Check if this is a human-readable link and get its ID
var response = await _api.GetInvite(id);
await _api.AcceptInvite(response.Code);
var response = await _api.GetInvite(id).ConfigureAwait(false);
await _api.AcceptInvite(response.Code).ConfigureAwait(false);
}

/// <summary> Deletes the provided invite. </summary>
@@ -208,8 +208,8 @@ namespace Discord
try
{
//Check if this is a human-readable link and get its ID
var response = await _api.GetInvite(id);
await _api.DeleteInvite(response.Code);
var response = await _api.GetInvite(id).ConfigureAwait(false);
await _api.DeleteInvite(response.Code).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
}
@@ -257,31 +257,28 @@ namespace Discord
}
else
{
var msg = await _api.SendMessage(channelId, blockText, mentions, nonce);
var msg = await _api.SendMessage(channelId, blockText, mentions, nonce).ConfigureAwait(false);
result[i] = _messages.Update(msg.Id, channelId, msg);
result[i].Nonce = nonce;
try { RaiseMessageSent(result[i]); } catch { }
}
await Task.Delay(1000);
await Task.Delay(1000).ConfigureAwait(false);
}
return result;
}

/// <summary> Sends a private message to the provided channel. </summary>
public async Task<Message[]> SendPrivateMessage(User user, string text)
=> await SendMessage(await GetPMChannel(user), text, new string[0]);
{
var channel = await GetPMChannel(user).ConfigureAwait(false);
return await SendMessage(channel, text, new string[0]).ConfigureAwait(false);
}
/// <summary> Sends a private message to the provided channel. </summary>
public async Task<Message[]> SendPrivateMessage(string userId, string text)
=> await SendMessage(await GetPMChannel(userId), text, new string[0]);
/*/// <summary> Sends a private message to the provided user, mentioning certain users. </summary>
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
public async Task<Message[]> SendPrivateMessage(User user, string text, string[] mentions)
=> SendMessage(await GetOrCreatePMChannel(user), text, mentions);
/// <summary> Sends a private message to the provided user, mentioning certain users. </summary>
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
public async Task<Message[]> SendPrivateMessage(string userId, string text, string[] mentions)
=> SendMessage(await GetOrCreatePMChannel(userId), text, mentions);*/

{
var channel = await GetPMChannel(userId).ConfigureAwait(false);
return await SendMessage(channel, text, new string[0]).ConfigureAwait(false);
}

/// <summary> Edits a message the provided message. </summary>
public Task EditMessage(Message message, string text)
@@ -313,7 +310,7 @@ namespace Discord
if (text.Length > DiscordAPI.MaxMessageSize)
text = text.Substring(0, DiscordAPI.MaxMessageSize);

var msg = await _api.EditMessage(channelId, messageId, text, mentions);
var msg = await _api.EditMessage(channelId, messageId, text, mentions).ConfigureAwait(false);
_messages.Update(msg.Id, channelId, msg);
}

@@ -329,7 +326,7 @@ namespace Discord

try
{
await _api.DeleteMessage(channelId, msgId);
await _api.DeleteMessage(channelId, msgId).ConfigureAwait(false);
_messages.Remove(msgId);
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
@@ -343,7 +340,7 @@ namespace Discord
{
try
{
await _api.DeleteMessage(msg.ChannelId, msg.Id);
await _api.DeleteMessage(msg.ChannelId, msg.Id).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
}
@@ -357,7 +354,7 @@ namespace Discord
{
try
{
await _api.DeleteMessage(channelId, msgId);
await _api.DeleteMessage(channelId, msgId).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
}
@@ -404,7 +401,7 @@ namespace Discord
{
try
{
var msgs = await _api.GetMessages(channel.Id, count);
var msgs = await _api.GetMessages(channel.Id, count).ConfigureAwait(false);
return msgs.OrderBy(x => x.Timestamp)
.Select(x =>
{
@@ -503,21 +500,21 @@ namespace Discord
public async Task ChangeUsername(string newName, string currentEmail, string currentPassword)
{
CheckReady();
var response = await _api.ChangeUsername(newName, currentEmail, currentPassword);
var response = await _api.ChangeUsername(newName, currentEmail, currentPassword).ConfigureAwait(false);
_users.Update(response.Id, response);
}
/// <summary> Changes your email to newEmail. </summary>
public async Task ChangeEmail(string newEmail, string currentPassword)
{
CheckReady();
var response = await _api.ChangeEmail(newEmail, currentPassword);
var response = await _api.ChangeEmail(newEmail, currentPassword).ConfigureAwait(false);
_users.Update(response.Id, response);
}
/// <summary> Changes your password to newPassword. </summary>
public async Task ChangePassword(string newPassword, string currentEmail, string currentPassword)
{
CheckReady();
var response = await _api.ChangePassword(newPassword, currentEmail, currentPassword);
var response = await _api.ChangePassword(newPassword, currentEmail, currentPassword).ConfigureAwait(false);
_users.Update(response.Id, response);
}

@@ -526,7 +523,7 @@ namespace Discord
public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword)
{
CheckReady();
var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword);
var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword).ConfigureAwait(false);
_users.Update(response.Id, response);
}
}


+ 2
- 2
src/Discord.Net/DiscordClient.Cache.cs View File

@@ -403,14 +403,14 @@ namespace Discord
var channel = user.PrivateChannel;
if (channel != null)
return channel;
return await CreatePMChannel(user?.Id);
return await CreatePMChannel(user?.Id).ConfigureAwait(false);
}
private async Task<Channel> CreatePMChannel(string userId)
{
CheckReady();
if (userId == null) throw new ArgumentNullException(nameof(userId));

var response = await _api.CreatePMChannel(_myId, userId);
var response = await _api.CreatePMChannel(_myId, userId).ConfigureAwait(false);
return _channels.Update(response.Id, response);
}



+ 28
- 41
src/Discord.Net/DiscordClient.cs View File

@@ -107,21 +107,21 @@ namespace Discord
//Reconnect if we didn't cause the disconnect
if (e.WasUnexpected)
{
await Task.Delay(_config.ReconnectDelay);
await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false);
while (!_disconnectToken.IsCancellationRequested)
{
try
{
await _webSocket.ReconnectAsync();
await _webSocket.ReconnectAsync().ConfigureAwait(false);
if (_http.Token != null)
await _webSocket.Login(_http.Token);
await _webSocket.Login(_http.Token).ConfigureAwait(false);
break;
}
catch (Exception ex)
{
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
await Task.Delay(_config.FailedReconnectDelay);
await Task.Delay(_config.FailedReconnectDelay).ConfigureAwait(false);
}
}
}
@@ -141,12 +141,12 @@ namespace Discord
//Reconnect if we didn't cause the disconnect
if (e.WasUnexpected)
{
await Task.Delay(_config.ReconnectDelay);
await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false);
while (!_disconnectToken.IsCancellationRequested)
{
try
{
await _voiceWebSocket.ReconnectAsync();
await _voiceWebSocket.ReconnectAsync().ConfigureAwait(false);
break;
}
catch (Exception ex)
@@ -154,7 +154,7 @@ namespace Discord
if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
await Task.Delay(_config.FailedReconnectDelay);
await Task.Delay(_config.FailedReconnectDelay).ConfigureAwait(false);
}
}
}
@@ -426,7 +426,7 @@ namespace Discord
if (_config.EnableVoice)
{
_voiceWebSocket.SetSessionData(data.ServerId, _myId, _sessionId, data.Token);
await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]);
await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]).ConfigureAwait(false);
}
#endif
}
@@ -469,7 +469,7 @@ namespace Discord
APIResponses.SendMessage apiMsg = null;
try
{
apiMsg = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce);
apiMsg = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce).ConfigureAwait(false);
}
catch (WebException) { break; }
catch (HttpException) { hasFailed = true; }
@@ -483,7 +483,7 @@ namespace Discord
msg.HasFailed = hasFailed;
try { RaiseMessageSent(msg); } catch { }
}
await Task.Delay(_config.MessageQueueInterval);
await Task.Delay(_config.MessageQueueInterval).ConfigureAwait(false);
}
}
catch { }
@@ -499,49 +499,36 @@ namespace Discord
/// <summary> Connects to the Discord server with the provided token. </summary>
public async Task Connect(string token)
{
await Disconnect();
await Disconnect().ConfigureAwait(false);

if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket is using cached token.");

await ConnectInternal(token);
await ConnectInternal(token).ConfigureAwait(false);
}
/// <summary> Connects to the Discord server with the provided email and password. </summary>
/// <returns> Returns a token for future connections. </returns>
public async Task<string> Connect(string email, string password)
{
await Disconnect();
await Disconnect().ConfigureAwait(false);

var response = await _api.Login(email, password);
var response = await _api.Login(email, password).ConfigureAwait(false);
if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token.");

return await ConnectInternal(response.Token);
return await ConnectInternal(response.Token).ConfigureAwait(false);
}
/// <summary> Connects to the Discord server as an anonymous user with the provided username. </summary>
/// <returns> Returns a token for future connections. </returns>
/*public async Task<string> ConnectAnonymous(string username)
{
await Disconnect();

var response = await _api.LoginAnonymous(username);
if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got anonymous token.");
_http.Token = response.Token;

return await ConnectInternal(response.Token);
}*/

private async Task<string> ConnectInternal(string token)
{
_blockEvent.Reset();
_http.Token = token;
string url = (await _api.GetWebSocketEndpoint()).Url;
string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url;
if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got endpoint.");

await _webSocket.ConnectAsync(url);
await _webSocket.Login(token);
await _webSocket.ConnectAsync(url).ConfigureAwait(false);
await _webSocket.Login(token).ConfigureAwait(false);

_disconnectToken = new CancellationTokenSource();
if (_config.UseMessageQueue)
@@ -550,10 +537,10 @@ namespace Discord
_mainTask = _disconnectToken.Wait();
_mainTask = _mainTask.ContinueWith(async x =>
{
await _webSocket.DisconnectAsync();
await _webSocket.DisconnectAsync().ConfigureAwait(false);
#if !DNXCORE50
if (_config.EnableVoice)
await _voiceWebSocket.DisconnectAsync();
await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false);
#endif

//Clear send queue
@@ -569,7 +556,7 @@ namespace Discord
_blockEvent.Set();
_mainTask = null;
}).Unwrap();
_isConnected = true;
_isConnected = true;
return token;
}
/// <summary> Disconnects from the Discord server, canceling any pending requests. </summary>
@@ -578,7 +565,7 @@ namespace Discord
if (_mainTask != null)
{
try { _disconnectToken.Cancel(); } catch (NullReferenceException) { }
try { await _mainTask; } catch (NullReferenceException) { }
try { await _mainTask.ConfigureAwait(false); } catch (NullReferenceException) { }
}
}

@@ -591,13 +578,13 @@ namespace Discord
CheckVoice();
if (channel == null) throw new ArgumentNullException(nameof(channel));

await LeaveVoiceServer();
await LeaveVoiceServer().ConfigureAwait(false);
//_currentVoiceServerId = channel.ServerId;
_webSocket.JoinVoice(channel);
#if !DNXCORE50
await _voiceWebSocket.BeginConnect();
await _voiceWebSocket.BeginConnect().ConfigureAwait(false);
#else
await Task.CompletedTask;
await Task.CompletedTask.ConfigureAwait(false);
#endif
}

@@ -607,9 +594,9 @@ namespace Discord
CheckVoice();

#if !DNXCORE50
await _voiceWebSocket.DisconnectAsync();
await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false);
#else
await Task.CompletedTask;
await Task.CompletedTask.ConfigureAwait(false);
#endif
//if (_voiceWebSocket.CurrentVoiceServerId != null)
_webSocket.LeaveVoice();
@@ -654,7 +641,7 @@ namespace Discord
#if !DNXCORE50
_voiceWebSocket.Wait();
#endif
await TaskHelper.CompletedTask;
await TaskHelper.CompletedTask.ConfigureAwait(false);
}

//Helpers


+ 3
- 3
src/Discord.Net/DiscordDataSocket.cs View File

@@ -27,8 +27,8 @@ namespace Discord
_lastSeq = 0;
_lastSession = null;
_redirectServer = null;
await BeginConnect();
await base.ConnectAsync(url);
await BeginConnect().ConfigureAwait(false);
await base.ConnectAsync(url).ConfigureAwait(false);
}
public async Task Login(string token)
{
@@ -44,7 +44,7 @@ namespace Discord
msg.Payload.Properties["$device"] = "Discord.Net";
msg.Payload.Properties["$referrer"] = "";
msg.Payload.Properties["$referring_domain"] = "";
await SendMessage(msg, cancelToken);
await SendMessage(msg, cancelToken).ConfigureAwait(false);

try
{


+ 56
- 54
src/Discord.Net/DiscordVoiceSocket.cs View File

@@ -14,6 +14,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Text;
using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage;
using Discord.Helpers;

namespace Discord
{
@@ -65,29 +66,26 @@ namespace Discord
_isClearing = false;

var cancelToken = _disconnectToken.Token;
Task.Factory.StartNew(async () =>
Task.Run(async () =>
{
_connectWaitOnLogin.Reset();

VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login();
msg.Payload.ServerId = _serverId;
msg.Payload.SessionId = _sessionId;
msg.Payload.Token = _token;
msg.Payload.UserId = _userId;
await SendMessage(msg, cancelToken);

try
{
_connectWaitOnLogin.Reset();

VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login();
msg.Payload.ServerId = _serverId;
msg.Payload.SessionId = _sessionId;
msg.Payload.Token = _token;
msg.Payload.UserId = _userId;
await SendMessage(msg, cancelToken).ConfigureAwait(false);

if (!_connectWaitOnLogin.Wait(_timeout, cancelToken))
return;
}
catch (OperationCanceledException)
{
return;
}

SetConnected();
});
SetConnected();
}
catch (OperationCanceledException) { }
}, _disconnectToken.Token);
}
protected override void OnDisconnect()
{
@@ -128,41 +126,45 @@ namespace Discord

public new async Task BeginConnect()
{
await base.BeginConnect();
await base.BeginConnect().ConfigureAwait(false);
var cancelToken = _disconnectToken.Token;

await Task.Yield();
try
{
if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message
throw new Exception("No reply from Discord server");
}
catch (OperationCanceledException)
await Task.Factory.StartNew(() =>
{
if (_disconnectReason == null)
throw new Exception("An unknown websocket error occurred.");
else
_disconnectReason.Throw();
}
try
{
if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message
throw new Exception("No reply from Discord server");
}
catch (OperationCanceledException)
{
if (_disconnectReason == null)
throw new Exception("An unknown websocket error occurred.");
else
_disconnectReason.Throw();
}
}).ConfigureAwait(false);
}
private async Task ReceiveVoiceAsync()
{
var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token;
await Task.Yield();

try
await Task.Run(async () =>
{
while (!cancelToken.IsCancellationRequested)
try
{
var result = await _udp.ReceiveAsync();
ProcessUdpMessage(result);
while (!cancelToken.IsCancellationRequested)
{
var result = await _udp.ReceiveAsync().ConfigureAwait(false);
ProcessUdpMessage(result);
}
}
}
catch (OperationCanceledException) { }
catch (ObjectDisposedException) { }
catch (Exception ex) { DisconnectInternal(ex); }
catch (OperationCanceledException) { }
catch (ObjectDisposedException) { }
catch (Exception ex) { DisconnectInternal(ex); }
}).ConfigureAwait(false);
}

#if USE_THREAD
@@ -170,11 +172,12 @@ namespace Discord
{
var cancelToken = cancelSource.Token;
#else
private async Task SendVoiceAsync()
private Task SendVoiceAsync()
{
var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token;
await Task.Yield();
return Task.Run(() =>
{
#endif

byte[] packet;
@@ -224,7 +227,7 @@ namespace Discord
#if USE_THREAD
_udp.Send(rtpPacket, packet.Length + 12);
#else
await _udp.SendAsync(rtpPacket, packet.Length + 12);
await _udp.SendAsync(rtpPacket, packet.Length + 12).ConfigureAwait(false);
#endif
}
timestamp = unchecked(timestamp + samplesPerFrame);
@@ -247,24 +250,23 @@ namespace Discord
#if USE_THREAD
Thread.Sleep(1);
#else
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
#endif
}
}
catch (OperationCanceledException) { }
catch (ObjectDisposedException) { }
catch (Exception ex) { DisconnectInternal(ex); }
}
//Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken
private async Task WatcherAsync()
#if !USE_THREAD
}).ConfigureAwait(false);
#endif
}
//Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken
private Task WatcherAsync()
{
var cancelToken = _disconnectToken.Token;
try
{
await Task.Delay(-1, cancelToken);
}
catch (OperationCanceledException) { }
finally { _udp.Close(); }
return cancelToken.Wait()
.ContinueWith(_ => _udp.Close());
}
protected override async Task ProcessMessage(string json)
@@ -279,7 +281,7 @@ namespace Discord
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval;
_ssrc = payload.SSRC;
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", ""))).FirstOrDefault(), payload.Port);
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
//_mode = payload.Modes.LastOrDefault();
_mode = "plain";
_udp.Connect(_endpoint);
@@ -295,7 +297,7 @@ namespace Discord
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, 70);
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, 70).ConfigureAwait(false);
}
}
break;


+ 66
- 64
src/Discord.Net/DiscordWebSocket.cs View File

@@ -42,19 +42,17 @@ namespace Discord

protected virtual async Task BeginConnect()
{
await DisconnectAsync();
await DisconnectAsync().ConfigureAwait(false);
_disconnectToken = new CancellationTokenSource();
_disconnectReason = null;
}
public virtual async Task ConnectAsync(string url)
{
//await DisconnectAsync();

var cancelToken = _disconnectToken.Token;

_webSocket = new ClientWebSocket();
_webSocket.Options.KeepAliveInterval = TimeSpan.Zero;
await _webSocket.ConnectAsync(new Uri(url), cancelToken);
await _webSocket.ConnectAsync(new Uri(url), cancelToken).ConfigureAwait(false);
_host = url;

if (_isDebug)
@@ -99,7 +97,7 @@ namespace Discord
if (_task != null)
{
try { DisconnectInternal(new Exception("Disconnect requested by user."), false); } catch (NullReferenceException) { }
try { await _task; } catch (NullReferenceException) { }
try { await _task.ConfigureAwait(false); } catch (NullReferenceException) { }
}
}
@@ -131,90 +129,94 @@ namespace Discord
};
}

private async Task ReceiveAsync()
private Task ReceiveAsync()
{
var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token;
await Task.Yield();

var buffer = new byte[ReceiveChunkSize];
var builder = new StringBuilder();

try
return Task.Run(async () =>
{
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
var buffer = new byte[ReceiveChunkSize];
var builder = new StringBuilder();

try
{
WebSocketReceiveResult result = null;
do
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
if (_webSocket.State != WebSocketState.Open || cancelToken.IsCancellationRequested)
return;

try
WebSocketReceiveResult result = null;
do
{
result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancelToken);
}
catch (Win32Exception ex)
when (ex.HResult == HR_TIMEOUT)
{
string msg = $"Connection timed out.";
RaiseOnDebugMessage(DebugMessageType.Connection, msg);
DisconnectInternal(new Exception(msg));
return;
}
if (_webSocket.State != WebSocketState.Open || cancelToken.IsCancellationRequested)
return;

try
{
result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancelToken).ConfigureAwait(false);
}
catch (Win32Exception ex)
when (ex.HResult == HR_TIMEOUT)
{
string msg = $"Connection timed out.";
RaiseOnDebugMessage(DebugMessageType.Connection, msg);
DisconnectInternal(new Exception(msg));
return;
}

if (result.MessageType == WebSocketMessageType.Close)
{
string msg = $"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}, {result.CloseStatusDescription ?? "No Reason"})";
RaiseOnDebugMessage(DebugMessageType.Connection, msg);
DisconnectInternal(new Exception(msg));
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
return;
}
else
builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));

if (result.MessageType == WebSocketMessageType.Close)
{
string msg = $"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}, {result.CloseStatusDescription ?? "No Reason"})";
RaiseOnDebugMessage(DebugMessageType.Connection, msg);
DisconnectInternal(new Exception(msg));
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
return;
}
else
builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));

}
while (result == null || !result.EndOfMessage);
while (result == null || !result.EndOfMessage);

#if DEBUG
System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString());
System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString());
#endif
await ProcessMessage(builder.ToString());
await ProcessMessage(builder.ToString()).ConfigureAwait(false);

builder.Clear();
builder.Clear();
}
}
}
catch (OperationCanceledException) { }
catch (Exception ex) { DisconnectInternal(ex); }
}
private async Task SendAsync()
catch (OperationCanceledException) { }
catch (Exception ex) { DisconnectInternal(ex); }
});
}
private Task SendAsync()
{
var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token;
await Task.Yield();

try
return Task.Run(async () =>
{
byte[] bytes;
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
try
{
if (_heartbeatInterval > 0)
byte[] bytes;
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
DateTime now = DateTime.UtcNow;
if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval)
if (_heartbeatInterval > 0)
{
await SendMessage(GetKeepAlive(), cancelToken);
_lastHeartbeat = now;
DateTime now = DateTime.UtcNow;
if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval)
{
await SendMessage(GetKeepAlive(), cancelToken).ConfigureAwait(false);
_lastHeartbeat = now;
}
}
while (_sendQueue.TryDequeue(out bytes))
await SendMessage(bytes, cancelToken).ConfigureAwait(false);
await Task.Delay(_sendInterval, cancelToken).ConfigureAwait(false);
}
while (_sendQueue.TryDequeue(out bytes))
await SendMessage(bytes, cancelToken);
await Task.Delay(_sendInterval, cancelToken);
}
}
catch (OperationCanceledException) { }
catch (Exception ex) { DisconnectInternal(ex); }
catch (OperationCanceledException) { }
catch (Exception ex) { DisconnectInternal(ex); }
});
}

protected abstract Task ProcessMessage(string json);
@@ -252,7 +254,7 @@ namespace Discord
try
{
await _webSocket.SendAsync(new ArraySegment<byte>(message, offset, count), WebSocketMessageType.Text, isLast, cancelToken);
await _webSocket.SendAsync(new ArraySegment<byte>(message, offset, count), WebSocketMessageType.Text, isLast, cancelToken).ConfigureAwait(false);
}
catch (Win32Exception ex)
when (ex.HResult == HR_TIMEOUT)


+ 2
- 2
src/Discord.Net/Helpers/Extensions.cs View File

@@ -9,12 +9,12 @@ namespace Discord.Helpers
public static async Task Wait(this CancellationTokenSource tokenSource)
{
var token = tokenSource.Token;
try { await Task.Delay(-1, token); }
try { await Task.Delay(-1, token).ConfigureAwait(false); }
catch (OperationCanceledException) { }
}
public static async Task Wait(this CancellationToken token)
{
try { await Task.Delay(-1, token); }
try { await Task.Delay(-1, token).ConfigureAwait(false); }
catch (OperationCanceledException) { }
}
}


+ 7
- 7
src/Discord.Net/Helpers/JsonHttpClient.cs View File

@@ -117,7 +117,7 @@ namespace Discord.Helpers
private async Task<ResponseT> Send<ResponseT>(HttpMethod method, string path, HttpContent content)
where ResponseT : class
{
string responseJson = await SendRequest(method, path, content, true);
string responseJson = await SendRequest(method, path, content, true).ConfigureAwait(false);
#if TEST_RESPONSES
if (path.StartsWith(Endpoints.BaseApi))
return JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings);
@@ -127,7 +127,7 @@ namespace Discord.Helpers
#if TEST_RESPONSES
private async Task<string> Send(HttpMethod method, string path, HttpContent content)
{
string responseJson = await SendRequest(method, path, content, true);
string responseJson = await SendRequest(method, path, content, true).ConfigureAwait(false);
if (path.StartsWith(Endpoints.BaseApi) && !string.IsNullOrEmpty(responseJson))
throw new Exception("API check failed: Response is not empty.");
return responseJson;
@@ -146,12 +146,12 @@ namespace Discord.Helpers
{
if (content is StringContent)
{
string json = await (content as StringContent).ReadAsStringAsync();
string json = await (content as StringContent).ReadAsStringAsync().ConfigureAwait(false);
RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {json}");
}
else
{
byte[] bytes = await content.ReadAsByteArrayAsync();
byte[] bytes = await content.ReadAsByteArrayAsync().ConfigureAwait(false);
RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {bytes.Length} bytes");
}
}
@@ -167,14 +167,14 @@ namespace Discord.Helpers
HttpResponseMessage response;
if (hasResponse)
{
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead);
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
throw new HttpException(response.StatusCode);
result = await response.Content.ReadAsStringAsync();
result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
else
{
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead);
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
throw new HttpException(response.StatusCode);
result = null;


Loading…
Cancel
Save