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); => _http.Get<APIResponses.Gateway>(Endpoints.Gateway);
public async Task<APIResponses.AuthRegister> LoginAnonymous(string username) 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 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; return registerResponse;
} }
public async Task<APIResponses.AuthLogin> Login(string email, string password) public async Task<APIResponses.AuthLogin> Login(string email, string password)
{ {
var request = new APIRequests.AuthLogin { Email = email, Password = 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; return response;
} }
public Task Logout() 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 (name == null) throw new ArgumentNullException(nameof(name));
if (region == null) throw new ArgumentNullException(nameof(region)); 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); return _servers.Update(response.Id, response);
} }


@@ -37,7 +37,7 @@ namespace Discord
CheckReady(); CheckReady();
if (serverId == null) throw new ArgumentNullException(nameof(serverId)); 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) { } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
return _servers.Remove(serverId); return _servers.Remove(serverId);
} }
@@ -54,7 +54,7 @@ namespace Discord
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
if (type == null) throw new ArgumentNullException(nameof(type)); 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); return _channels.Update(response.Id, serverId, response);
} }


@@ -67,7 +67,7 @@ namespace Discord
CheckReady(); CheckReady();
if (channelId == null) throw new ArgumentNullException(nameof(channelId)); 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) { } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
return _channels.Remove(channelId); return _channels.Remove(channelId);
} }
@@ -108,7 +108,7 @@ namespace Discord
if (serverId == null) throw new ArgumentNullException(nameof(serverId)); if (serverId == null) throw new ArgumentNullException(nameof(serverId));
if (userId == null) throw new ArgumentNullException(nameof(userId)); 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) { } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
} }


@@ -139,7 +139,7 @@ namespace Discord
if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
if (maxUses <= 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); 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); _channels.Update(response.Channel.Id, response.Server.Id, response.Channel);
_servers.Update(response.Server.Id, response.Server); _servers.Update(response.Server.Id, response.Server);
_users.Update(response.Inviter.Id, response.Inviter); _users.Update(response.Inviter.Id, response.Inviter);
@@ -163,7 +163,7 @@ namespace Discord
CheckReady(); CheckReady();
if (id == null) throw new ArgumentNullException(nameof(id)); 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) return new Invite(response.Code, response.XkcdPass, this)
{ {
ChannelId = response.Channel.Id, ChannelId = response.Channel.Id,
@@ -195,8 +195,8 @@ namespace Discord
id = id.Substring(0, id.Length - 1); id = id.Substring(0, id.Length - 1);


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


/// <summary> Sends a private message to the provided channel. </summary> /// <summary> Sends a private message to the provided channel. </summary>
public async Task<Message[]> SendPrivateMessage(User user, string text) 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> /// <summary> Sends a private message to the provided channel. </summary>
public async Task<Message[]> SendPrivateMessage(string userId, string text) 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> /// <summary> Edits a message the provided message. </summary>
public Task EditMessage(Message message, string text) public Task EditMessage(Message message, string text)
@@ -313,7 +310,7 @@ namespace Discord
if (text.Length > DiscordAPI.MaxMessageSize) if (text.Length > DiscordAPI.MaxMessageSize)
text = text.Substring(0, 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); _messages.Update(msg.Id, channelId, msg);
} }


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


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


@@ -526,7 +523,7 @@ namespace Discord
public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword) public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword)
{ {
CheckReady(); 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); _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; var channel = user.PrivateChannel;
if (channel != null) if (channel != null)
return channel; return channel;
return await CreatePMChannel(user?.Id);
return await CreatePMChannel(user?.Id).ConfigureAwait(false);
} }
private async Task<Channel> CreatePMChannel(string userId) private async Task<Channel> CreatePMChannel(string userId)
{ {
CheckReady(); CheckReady();
if (userId == null) throw new ArgumentNullException(nameof(userId)); 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); 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 //Reconnect if we didn't cause the disconnect
if (e.WasUnexpected) if (e.WasUnexpected)
{ {
await Task.Delay(_config.ReconnectDelay);
await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false);
while (!_disconnectToken.IsCancellationRequested) while (!_disconnectToken.IsCancellationRequested)
{ {
try try
{ {
await _webSocket.ReconnectAsync();
await _webSocket.ReconnectAsync().ConfigureAwait(false);
if (_http.Token != null) if (_http.Token != null)
await _webSocket.Login(_http.Token);
await _webSocket.Login(_http.Token).ConfigureAwait(false);
break; break;
} }
catch (Exception ex) catch (Exception ex)
{ {
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}"); RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect() //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 //Reconnect if we didn't cause the disconnect
if (e.WasUnexpected) if (e.WasUnexpected)
{ {
await Task.Delay(_config.ReconnectDelay);
await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false);
while (!_disconnectToken.IsCancellationRequested) while (!_disconnectToken.IsCancellationRequested)
{ {
try try
{ {
await _voiceWebSocket.ReconnectAsync();
await _voiceWebSocket.ReconnectAsync().ConfigureAwait(false);
break; break;
} }
catch (Exception ex) catch (Exception ex)
@@ -154,7 +154,7 @@ namespace Discord
if (_isDebugMode) if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}"); RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect() //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) if (_config.EnableVoice)
{ {
_voiceWebSocket.SetSessionData(data.ServerId, _myId, _sessionId, data.Token); _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 #endif
} }
@@ -469,7 +469,7 @@ namespace Discord
APIResponses.SendMessage apiMsg = null; APIResponses.SendMessage apiMsg = null;
try 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 (WebException) { break; }
catch (HttpException) { hasFailed = true; } catch (HttpException) { hasFailed = true; }
@@ -483,7 +483,7 @@ namespace Discord
msg.HasFailed = hasFailed; msg.HasFailed = hasFailed;
try { RaiseMessageSent(msg); } catch { } try { RaiseMessageSent(msg); } catch { }
} }
await Task.Delay(_config.MessageQueueInterval);
await Task.Delay(_config.MessageQueueInterval).ConfigureAwait(false);
} }
} }
catch { } catch { }
@@ -499,49 +499,36 @@ namespace Discord
/// <summary> Connects to the Discord server with the provided token. </summary> /// <summary> Connects to the Discord server with the provided token. </summary>
public async Task Connect(string token) public async Task Connect(string token)
{ {
await Disconnect();
await Disconnect().ConfigureAwait(false);


if (_isDebugMode) if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket is using cached token."); 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> /// <summary> Connects to the Discord server with the provided email and password. </summary>
/// <returns> Returns a token for future connections. </returns> /// <returns> Returns a token for future connections. </returns>
public async Task<string> Connect(string email, string password) 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) if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); 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) private async Task<string> ConnectInternal(string token)
{ {
_blockEvent.Reset(); _blockEvent.Reset();
_http.Token = token; _http.Token = token;
string url = (await _api.GetWebSocketEndpoint()).Url;
string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url;
if (_isDebugMode) if (_isDebugMode)
RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got endpoint."); 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(); _disconnectToken = new CancellationTokenSource();
if (_config.UseMessageQueue) if (_config.UseMessageQueue)
@@ -550,10 +537,10 @@ namespace Discord
_mainTask = _disconnectToken.Wait(); _mainTask = _disconnectToken.Wait();
_mainTask = _mainTask.ContinueWith(async x => _mainTask = _mainTask.ContinueWith(async x =>
{ {
await _webSocket.DisconnectAsync();
await _webSocket.DisconnectAsync().ConfigureAwait(false);
#if !DNXCORE50 #if !DNXCORE50
if (_config.EnableVoice) if (_config.EnableVoice)
await _voiceWebSocket.DisconnectAsync();
await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false);
#endif #endif


//Clear send queue //Clear send queue
@@ -569,7 +556,7 @@ namespace Discord
_blockEvent.Set(); _blockEvent.Set();
_mainTask = null; _mainTask = null;
}).Unwrap(); }).Unwrap();
_isConnected = true;
_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>
@@ -578,7 +565,7 @@ namespace Discord
if (_mainTask != null) if (_mainTask != null)
{ {
try { _disconnectToken.Cancel(); } catch (NullReferenceException) { } try { _disconnectToken.Cancel(); } catch (NullReferenceException) { }
try { await _mainTask; } catch (NullReferenceException) { }
try { await _mainTask.ConfigureAwait(false); } catch (NullReferenceException) { }
} }
} }


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


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


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


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


//Helpers //Helpers


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

@@ -27,8 +27,8 @@ namespace Discord
_lastSeq = 0; _lastSeq = 0;
_lastSession = null; _lastSession = null;
_redirectServer = 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) public async Task Login(string token)
{ {
@@ -44,7 +44,7 @@ namespace Discord
msg.Payload.Properties["$device"] = "Discord.Net"; msg.Payload.Properties["$device"] = "Discord.Net";
msg.Payload.Properties["$referrer"] = ""; msg.Payload.Properties["$referrer"] = "";
msg.Payload.Properties["$referring_domain"] = ""; msg.Payload.Properties["$referring_domain"] = "";
await SendMessage(msg, cancelToken);
await SendMessage(msg, cancelToken).ConfigureAwait(false);


try try
{ {


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

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


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


var cancelToken = _disconnectToken.Token; 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 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)) if (!_connectWaitOnLogin.Wait(_timeout, cancelToken))
return; return;
}
catch (OperationCanceledException)
{
return;
}


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


public new async Task BeginConnect() public new async Task BeginConnect()
{ {
await base.BeginConnect();
await base.BeginConnect().ConfigureAwait(false);
var cancelToken = _disconnectToken.Token; 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() private async Task ReceiveVoiceAsync()
{ {
var cancelSource = _disconnectToken; var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token; 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 #if USE_THREAD
@@ -170,11 +172,12 @@ namespace Discord
{ {
var cancelToken = cancelSource.Token; var cancelToken = cancelSource.Token;
#else #else
private async Task SendVoiceAsync()
private Task SendVoiceAsync()
{ {
var cancelSource = _disconnectToken; var cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token; var cancelToken = cancelSource.Token;
await Task.Yield();
return Task.Run(() =>
{
#endif #endif


byte[] packet; byte[] packet;
@@ -224,7 +227,7 @@ namespace Discord
#if USE_THREAD #if USE_THREAD
_udp.Send(rtpPacket, packet.Length + 12); _udp.Send(rtpPacket, packet.Length + 12);
#else #else
await _udp.SendAsync(rtpPacket, packet.Length + 12);
await _udp.SendAsync(rtpPacket, packet.Length + 12).ConfigureAwait(false);
#endif #endif
} }
timestamp = unchecked(timestamp + samplesPerFrame); timestamp = unchecked(timestamp + samplesPerFrame);
@@ -247,24 +250,23 @@ namespace Discord
#if USE_THREAD #if USE_THREAD
Thread.Sleep(1); Thread.Sleep(1);
#else #else
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
#endif #endif
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
catch (Exception ex) { DisconnectInternal(ex); } 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; 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) protected override async Task ProcessMessage(string json)
@@ -279,7 +281,7 @@ namespace Discord
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>(); var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval; _heartbeatInterval = payload.HeartbeatInterval;
_ssrc = payload.SSRC; _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 = payload.Modes.LastOrDefault();
_mode = "plain"; _mode = "plain";
_udp.Connect(_endpoint); _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, 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; break;


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

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


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

var cancelToken = _disconnectToken.Token; var cancelToken = _disconnectToken.Token;


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


if (_isDebug) if (_isDebug)
@@ -99,7 +97,7 @@ namespace Discord
if (_task != null) 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; } 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 cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token; 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 #if DEBUG
System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString());
System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString());
#endif #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 cancelSource = _disconnectToken;
var cancelToken = cancelSource.Token; 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); protected abstract Task ProcessMessage(string json);
@@ -252,7 +254,7 @@ namespace Discord
try 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) catch (Win32Exception ex)
when (ex.HResult == HR_TIMEOUT) 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) public static async Task Wait(this CancellationTokenSource tokenSource)
{ {
var token = tokenSource.Token; var token = tokenSource.Token;
try { await Task.Delay(-1, token); }
try { await Task.Delay(-1, token).ConfigureAwait(false); }
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
} }
public static async Task Wait(this CancellationToken token) public static async Task Wait(this CancellationToken token)
{ {
try { await Task.Delay(-1, token); }
try { await Task.Delay(-1, token).ConfigureAwait(false); }
catch (OperationCanceledException) { } 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) private async Task<ResponseT> Send<ResponseT>(HttpMethod method, string path, HttpContent content)
where ResponseT : class 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 TEST_RESPONSES
if (path.StartsWith(Endpoints.BaseApi)) if (path.StartsWith(Endpoints.BaseApi))
return JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings); return JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings);
@@ -127,7 +127,7 @@ namespace Discord.Helpers
#if TEST_RESPONSES #if TEST_RESPONSES
private async Task<string> Send(HttpMethod method, string path, HttpContent content) 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)) if (path.StartsWith(Endpoints.BaseApi) && !string.IsNullOrEmpty(responseJson))
throw new Exception("API check failed: Response is not empty."); throw new Exception("API check failed: Response is not empty.");
return responseJson; return responseJson;
@@ -146,12 +146,12 @@ namespace Discord.Helpers
{ {
if (content is StringContent) 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}"); RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {json}");
} }
else else
{ {
byte[] bytes = await content.ReadAsByteArrayAsync();
byte[] bytes = await content.ReadAsByteArrayAsync().ConfigureAwait(false);
RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {bytes.Length} bytes"); RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {bytes.Length} bytes");
} }
} }
@@ -167,14 +167,14 @@ namespace Discord.Helpers
HttpResponseMessage response; HttpResponseMessage response;
if (hasResponse) if (hasResponse)
{ {
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead);
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
throw new HttpException(response.StatusCode); throw new HttpException(response.StatusCode);
result = await response.Content.ReadAsStringAsync();
result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
} }
else else
{ {
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead);
response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
throw new HttpException(response.StatusCode); throw new HttpException(response.StatusCode);
result = null; result = null;


Loading…
Cancel
Save