Browse Source

Switched locks over to Nito.AsyncEx locks

tags/docs-0.9
RogueException 9 years ago
parent
commit
6e1dbb0e0e
20 changed files with 176 additions and 153 deletions
  1. +12
    -0
      src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
  2. +1
    -0
      src/Discord.Net.Audio.Net45/packages.config
  3. +5
    -14
      src/Discord.Net.Audio/AudioClient.cs
  4. +1
    -1
      src/Discord.Net.Audio/AudioServiceConfig.cs
  5. +19
    -29
      src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs
  6. +5
    -14
      src/Discord.Net.Audio/SimpleAudioClient.cs
  7. +26
    -24
      src/Discord.Net.Audio/VoiceBuffer.cs
  8. +15
    -0
      src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj
  9. +4
    -0
      src/Discord.Net.Modules.Net45/packages.config
  10. +23
    -19
      src/Discord.Net.Modules/ModuleManager.cs
  11. +12
    -0
      src/Discord.Net.Net45/Discord.Net.csproj
  12. +1
    -0
      src/Discord.Net.Net45/packages.config
  13. +4
    -8
      src/Discord.Net/DiscordClient.cs
  14. +1
    -1
      src/Discord.Net/Format.cs
  15. +1
    -1
      src/Discord.Net/Models/User.cs
  16. +4
    -3
      src/Discord.Net/Net/Rest/BuiltInEngine.cs
  17. +7
    -5
      src/Discord.Net/Net/Rest/SharpRestEngine.cs
  18. +4
    -8
      src/Discord.Net/Net/WebSockets/WebSocket.cs
  19. +24
    -23
      src/Discord.Net/TaskManager.cs
  20. +7
    -3
      src/Discord.Net/project.json

+ 12
- 0
src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj View File

@@ -42,6 +42,18 @@
<HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>


+ 1
- 0
src/Discord.Net.Audio.Net45/packages.config View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net45" />
</packages>

+ 5
- 14
src/Discord.Net.Audio/AudioClient.cs View File

@@ -2,6 +2,7 @@
using Discord.Logging;
using Discord.Net.WebSockets;
using Newtonsoft.Json;
using Nito.AsyncEx;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -10,7 +11,7 @@ namespace Discord.Audio
{
internal class AudioClient : IAudioClient
{
private readonly Semaphore _connectionLock;
private readonly AsyncLock _connectionLock;
private readonly JsonSerializer _serializer;
private CancellationTokenSource _cancelTokenSource;

@@ -31,7 +32,7 @@ namespace Discord.Audio
GatewaySocket = gatewaySocket;
Logger = logger;
_connectionLock = new Semaphore(1, 1);
_connectionLock = new AsyncLock();
_serializer = new JsonSerializer();
_serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
@@ -92,8 +93,7 @@ namespace Discord.Audio
if (VoiceSocket.Server == null)
throw new InvalidOperationException("This client has been closed.");

_connectionLock.WaitOne();
try
using (await _connectionLock.LockAsync())
{
_cancelTokenSource = new CancellationTokenSource();
var cancelToken = _cancelTokenSource.Token;
@@ -106,26 +106,17 @@ namespace Discord.Audio
VoiceSocket.WaitForConnection(cancelToken);
});
}
finally
{
_connectionLock.Release();
}
}
public async Task Disconnect()
{
_connectionLock.WaitOne();
try
using (await _connectionLock.LockAsync())
{
Service.RemoveClient(VoiceSocket.Server, this);
VoiceSocket.Channel = null;
SendVoiceUpdate();
await VoiceSocket.Disconnect();
}
finally
{
_connectionLock.Release();
}
}

private async void OnReceivedDispatch(object sender, WebSocketEventEventArgs e)


+ 1
- 1
src/Discord.Net.Audio/AudioServiceConfig.cs View File

@@ -29,7 +29,7 @@ namespace Discord.Audio
public bool EnableMultiserver { get { return _enableMultiserver; } set { SetValue(ref _enableMultiserver, value); } }
private bool _enableMultiserver = false;

/// <summary> Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. This value is the target maximum but is not guaranteed, the buffer will often go slightly above this value. </summary>
/// <summary> Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. </summary>
public int BufferLength { get { return _bufferLength; } set { SetValue(ref _bufferLength, value); } }
private int _bufferLength = 1000;



+ 19
- 29
src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs View File

@@ -29,7 +29,7 @@ namespace Discord.Net.WebSockets
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
private readonly AudioClient _audioClient;
private readonly AudioServiceConfig _config;
private Thread _sendThread, _receiveThread;
private Task _sendTask, _receiveTask;
private VoiceBuffer _sendBuffer;
private OpusEncoder _encoder;
private uint _ssrc;
@@ -95,20 +95,9 @@ namespace Discord.Net.WebSockets
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));

List<Task> tasks = new List<Task>();
if ((_config.Mode & AudioMode.Outgoing) != 0)
{
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(CancelToken)));
_sendThread.IsBackground = true;
_sendThread.Start();
}
/*if ((_config.Mode & AudioMode.Incoming) != 0)
{*/
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(CancelToken)));
_receiveThread.IsBackground = true;
_receiveThread.Start();
/*}
else
tasks.Add(Task.Run(() => ReceiveVoiceAsync(CancelToken)));*/
if (_config.Mode.HasFlag(AudioMode.Outgoing))
_sendTask = Task.Run(() => SendVoiceAsync(CancelToken));
_receiveTask = Task.Run(() => ReceiveVoiceAsync(CancelToken));

SendIdentify();

@@ -119,14 +108,15 @@ namespace Discord.Net.WebSockets
tasks.Add(HeartbeatAsync(CancelToken));
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
}
protected override Task Cleanup()
protected override async Task Cleanup()
{
if (_sendThread != null)
_sendThread.Join();
if (_receiveThread != null)
_receiveThread.Join();
_sendThread = null;
_receiveThread = null;
var sendThread = _sendTask;
if (sendThread != null) await sendThread;
_sendTask = null;

var receiveThread = _receiveTask;
if (receiveThread != null) await receiveThread;
_receiveTask = null;

OpusDecoder decoder;
foreach (var pair in _decoders)
@@ -138,10 +128,10 @@ namespace Discord.Net.WebSockets
ClearPCMFrames();
_udp = null;

return base.Cleanup();
await base.Cleanup();
}

private void ReceiveVoiceAsync(CancellationToken cancelToken)
private async Task ReceiveVoiceAsync(CancellationToken cancelToken)
{
var closeTask = cancelToken.Wait();
try
@@ -158,7 +148,7 @@ namespace Discord.Net.WebSockets

while (!cancelToken.IsCancellationRequested)
{
Thread.Sleep(1);
await Task.Delay(1);
if (_udp.Available > 0)
{
#if !DOTNET5_4
@@ -243,12 +233,12 @@ namespace Discord.Net.WebSockets
catch (InvalidOperationException) { } //Includes ObjectDisposedException
}

private void SendVoiceAsync(CancellationToken cancelToken)
private async Task SendVoiceAsync(CancellationToken cancelToken)
{
try
{
while (!cancelToken.IsCancellationRequested && State != ConnectionState.Connected)
Thread.Sleep(1);
await Task.Delay(1);

if (cancelToken.IsCancellationRequested)
return;
@@ -370,10 +360,10 @@ namespace Discord.Net.WebSockets
{
int time = (int)Math.Floor(ticksToNextFrame / ticksPerMillisecond);
if (time > 0)
Thread.Sleep(time);
await Task.Delay(time);
}
else
Thread.Sleep(1); //Give as much time to the encrypter as possible
await Task.Delay(1); //Give as much time to the encrypter as possible
}
}
}


+ 5
- 14
src/Discord.Net.Audio/SimpleAudioClient.cs View File

@@ -1,4 +1,5 @@
using Discord.Logging;
using Nito.AsyncEx;
using System.Threading;
using System.Threading.Tasks;

@@ -27,21 +28,20 @@ namespace Discord.Audio
void IAudioClient.Wait() => _client.Wait();
}

private readonly Semaphore _connectionLock;
private readonly AsyncLock _connectionLock;

internal VirtualClient CurrentClient { get; private set; }

public SimpleAudioClient(AudioService service, int id, Logger logger)
: base(service, id, null, service.Client.GatewaySocket, logger)
{
_connectionLock = new Semaphore(1, 1);
_connectionLock = new AsyncLock();
}

//Only disconnects if is current a member of this server
public async Task Leave(VirtualClient client)
{
_connectionLock.WaitOne();
try
using (await _connectionLock.LockAsync())
{
if (CurrentClient == client)
{
@@ -49,16 +49,11 @@ namespace Discord.Audio
await Disconnect();
}
}
finally
{
_connectionLock.Release();
}
}

internal async Task<IAudioClient> Connect(Channel channel)
{
_connectionLock.WaitOne();
try
using (await _connectionLock.LockAsync())
{
bool changeServer = channel.Server != VoiceSocket.Server;
if (changeServer || CurrentClient == null)
@@ -70,10 +65,6 @@ namespace Discord.Audio
await Join(channel);
return CurrentClient;
}
finally
{
_connectionLock.Release();
}
}
}
}

+ 26
- 24
src/Discord.Net.Audio/VoiceBuffer.cs View File

@@ -1,4 +1,5 @@
using System;
using Nito.AsyncEx;
using System;
using System.Threading;

namespace Discord.Audio
@@ -9,8 +10,9 @@ namespace Discord.Audio
private readonly byte[] _buffer;
private readonly byte[] _blankFrame;
private ushort _readCursor, _writeCursor;
private ManualResetEventSlim _notOverflowEvent;
private ManualResetEventSlim _notOverflowEvent;
private bool _isClearing;
private AsyncLock _lock;

public int FrameSize => _frameSize;
public int FrameCount => _frameCount;
@@ -27,6 +29,7 @@ namespace Discord.Audio
_buffer = new byte[_bufferSize];
_blankFrame = new byte[_frameSize];
_notOverflowEvent = new ManualResetEventSlim(); //Notifies when an overflow is solved
_lock = new AsyncLock();
}

public void Push(byte[] buffer, int bytes, CancellationToken cancelToken)
@@ -38,8 +41,8 @@ namespace Discord.Audio
int expectedBytes = wholeFrames * _frameSize;
int lastFrameSize = bytes - expectedBytes;

lock (this)
{
using (_lock.Lock())
{
for (int i = 0, pos = 0; i <= wholeFrames; i++, pos += _frameSize)
{
//If the read cursor is in the next position, wait for it to move.
@@ -83,35 +86,34 @@ namespace Discord.Audio
}

public bool Pop(byte[] buffer)
{
if (_writeCursor == _readCursor)
{
_notOverflowEvent.Set();
return false;
}
{
//using (_lock.Lock())
//{
if (_writeCursor == _readCursor)
{
_notOverflowEvent.Set();
return false;
}

bool isClearing = _isClearing;
if (!isClearing)
Buffer.BlockCopy(_buffer, _readCursor * _frameSize, buffer, 0, _frameSize);
bool isClearing = _isClearing;
if (!isClearing)
Buffer.BlockCopy(_buffer, _readCursor * _frameSize, buffer, 0, _frameSize);

//Advance the read cursor to the next position
AdvanceCursorPos(ref _readCursor);
_notOverflowEvent.Set();
return !isClearing;
//Advance the read cursor to the next position
AdvanceCursorPos(ref _readCursor);
_notOverflowEvent.Set();
return !isClearing;
//}
}

public void Clear(CancellationToken cancelToken)
{
lock (this)
{
using (_lock.Lock())
{
_isClearing = true;
for (int i = 0; i < _frameCount; i++)
Buffer.BlockCopy(_blankFrame, 0, _buffer, i * _frameCount, i++);
try
{
Wait(cancelToken);
}
catch (OperationCanceledException) { }

_writeCursor = 0;
_readCursor = 0;
_isClearing = false;


+ 15
- 0
src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj View File

@@ -38,6 +38,18 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
@@ -71,6 +83,9 @@
<Name>Discord.Net</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Discord.Net.Shared\Discord.Net.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.


+ 4
- 0
src/Discord.Net.Modules.Net45/packages.config View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net45" />
</packages>

+ 23
- 19
src/Discord.Net.Modules/ModuleManager.cs View File

@@ -1,4 +1,5 @@
using Discord.Commands;
using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -48,8 +49,9 @@ namespace Discord.Modules
private readonly ConcurrentDictionary<ulong, Server> _enabledServers;
private readonly ConcurrentDictionary<ulong, Channel> _enabledChannels;
private readonly ConcurrentDictionary<ulong, int> _indirectServers;
private readonly AsyncLock _lock;

public DiscordClient Client => _client;
public DiscordClient Client => _client;
public string Name => _name;
public string Id => _id;
public FilterType FilterType => _filterType;
@@ -60,15 +62,17 @@ namespace Discord.Modules
{
_client = client;
_name = name;

_id = name.ToLowerInvariant();
_lock = new AsyncLock();

_filterType = filterType;
_filterType = filterType;
_allowAll = filterType == FilterType.Unrestricted;
_useServerWhitelist = filterType.HasFlag(FilterType.ServerWhitelist);
_useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist);
_allowPrivate = filterType.HasFlag(FilterType.AllowPrivate);

_enabledServers = new ConcurrentDictionary<ulong, Server>();
_enabledServers = new ConcurrentDictionary<ulong, Server>();
_enabledChannels = new ConcurrentDictionary<ulong, Channel>();
_indirectServers = new ConcurrentDictionary<ulong, int>();

@@ -122,8 +126,8 @@ namespace Discord.Modules
if (server == null) throw new ArgumentNullException(nameof(server));
if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist.");

lock (this)
return EnableServerInternal(server);
using (_lock.Lock())
return EnableServerInternal(server);
}
public void EnableServers(IEnumerable<Server> servers)
{
@@ -131,8 +135,8 @@ namespace Discord.Modules
if (servers.Contains(null)) throw new ArgumentException("Collection cannot contain null.", nameof(servers));
if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist.");

lock (this)
{
using (_lock.Lock())
{
foreach (var server in servers)
EnableServerInternal(server);
}
@@ -153,8 +157,8 @@ namespace Discord.Modules
if (server == null) throw new ArgumentNullException(nameof(server));
if (!_useServerWhitelist) return false;

lock (this)
{
using (_lock.Lock())
{
if (_enabledServers.TryRemove(server.Id, out server))
{
if (ServerDisabled != null)
@@ -169,8 +173,8 @@ namespace Discord.Modules
if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist.");
if (!_useServerWhitelist) return;

lock (this)
{
using (_lock.Lock())
{
if (ServerDisabled != null)
{
foreach (var server in _enabledServers)
@@ -186,8 +190,8 @@ namespace Discord.Modules
if (channel == null) throw new ArgumentNullException(nameof(channel));
if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist.");

lock (this)
return EnableChannelInternal(channel);
using (_lock.Lock())
return EnableChannelInternal(channel);
}
public void EnableChannels(IEnumerable<Channel> channels)
{
@@ -195,8 +199,8 @@ namespace Discord.Modules
if (channels.Contains(null)) throw new ArgumentException("Collection cannot contain null.", nameof(channels));
if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist.");

lock (this)
{
using (_lock.Lock())
{
foreach (var channel in channels)
EnableChannelInternal(channel);
}
@@ -225,8 +229,8 @@ namespace Discord.Modules
if (channel == null) throw new ArgumentNullException(nameof(channel));
if (!_useChannelWhitelist) return false;

lock (this)
{
using (_lock.Lock())
{
Channel ignored;
if (_enabledChannels.TryRemove(channel.Id, out ignored))
{
@@ -252,8 +256,8 @@ namespace Discord.Modules
{
if (!_useChannelWhitelist) return;

lock (this)
{
using (_lock.Lock())
{
if (ChannelDisabled != null)
{
foreach (var channel in _enabledChannels)


+ 12
- 0
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -58,6 +58,18 @@
<HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\DiscordBot\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
<Private>True</Private>


+ 1
- 0
src/Discord.Net.Net45/packages.config View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
<package id="WebSocket4Net" version="0.14.1" targetFramework="net45" />
</packages>

+ 4
- 8
src/Discord.Net/DiscordClient.cs View File

@@ -5,6 +5,7 @@ using Discord.Net;
using Discord.Net.Rest;
using Discord.Net.WebSockets;
using Newtonsoft.Json;
using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -21,7 +22,7 @@ namespace Discord
/// <summary> Provides a connection to the DiscordApp service. </summary>
public partial class DiscordClient
{
private readonly Semaphore _connectionLock;
private readonly AsyncLock _connectionLock;
private readonly ManualResetEvent _disconnectedEvent;
private readonly ManualResetEventSlim _connectedEvent;
private readonly TaskManager _taskManager;
@@ -88,7 +89,7 @@ namespace Discord

//Async
_taskManager = new TaskManager(Cleanup);
_connectionLock = new Semaphore(1, 1);
_connectionLock = new AsyncLock();
_disconnectedEvent = new ManualResetEvent(true);
_connectedEvent = new ManualResetEventSlim(false);
CancelToken = new CancellationToken(true);
@@ -157,8 +158,7 @@ namespace Discord
{
try
{
_connectionLock.WaitOne();
try
using (await _connectionLock.LockAsync())
{
if (State != ConnectionState.Disconnected)
await Disconnect().ConfigureAwait(false);
@@ -182,10 +182,6 @@ namespace Discord
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
GatewaySocket.WaitForConnection(CancelToken);
}
finally
{
_connectionLock.Release();
}
}
catch (Exception ex)
{


+ 1
- 1
src/Discord.Net/Format.cs View File

@@ -7,7 +7,7 @@ namespace Discord
private static readonly string[] _patterns;
private static readonly StringBuilder _builder;

static Format()
static Format()
{
_patterns = new string[] { "__", "_", "**", "*", "~~", "```", "`"};
_builder = new StringBuilder(DiscordConfig.MaxMessageSize);


+ 1
- 1
src/Discord.Net/Models/User.cs View File

@@ -39,7 +39,7 @@ namespace Discord
public override int GetHashCode()
=> unchecked(ServerId.GetHashCode() + UserId.GetHashCode() + 23);
}
private VoiceState _voiceState;
private DateTime? _lastOnline;
private ulong? _voiceChannelId;


+ 4
- 3
src/Discord.Net/Net/Rest/BuiltInEngine.cs View File

@@ -9,6 +9,7 @@ using System.Net.Http;
using System.Net;
using System.Text;
using System.Globalization;
using Nito.AsyncEx;

namespace Discord.Net.Rest
{
@@ -20,7 +21,7 @@ namespace Discord.Net.Rest
private readonly HttpClient _client;
private readonly string _baseUrl;

private readonly object _rateLimitLock;
private readonly AsyncLock _rateLimitLock;
private DateTime _rateLimitTime;

internal Logger Logger { get; }
@@ -31,7 +32,7 @@ namespace Discord.Net.Rest
_baseUrl = baseUrl;
Logger = logger;

_rateLimitLock = new object();
_rateLimitLock = new AsyncLock();
_client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
@@ -102,7 +103,7 @@ namespace Discord.Net.Rest
var now = DateTime.UtcNow;
if (now >= _rateLimitTime)
{
lock (_rateLimitLock)
using (await _rateLimitLock.LockAsync())
{
if (now >= _rateLimitTime)
{


+ 7
- 5
src/Discord.Net/Net/Rest/SharpRestEngine.cs View File

@@ -1,11 +1,13 @@
#if !DOTNET5_4
using Discord.Logging;
using Nito.AsyncEx;
using RestSharp;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using RestSharpClient = RestSharp.RestClient;

namespace Discord.Net.Rest
{
@@ -14,9 +16,9 @@ namespace Discord.Net.Rest
private const int HR_SECURECHANNELFAILED = -2146233079;

private readonly DiscordConfig _config;
private readonly RestSharp.RestClient _client;
private readonly RestSharpClient _client;

private readonly object _rateLimitLock;
private readonly AsyncLock _rateLimitLock;
private DateTime _rateLimitTime;

internal Logger Logger { get; }
@@ -26,8 +28,8 @@ namespace Discord.Net.Rest
_config = config;
Logger = logger;

_rateLimitLock = new object();
_client = new RestSharp.RestClient(baseUrl)
_rateLimitLock = new AsyncLock();
_client = new RestSharpClient(baseUrl)
{
PreAuthenticate = false,
ReadWriteTimeout = _config.RestTimeout,
@@ -90,7 +92,7 @@ namespace Discord.Net.Rest
var now = DateTime.UtcNow;
if (now >= _rateLimitTime)
{
lock (_rateLimitLock)
using (await _rateLimitLock.LockAsync())
{
if (now >= _rateLimitTime)
{


+ 4
- 8
src/Discord.Net/Net/WebSockets/WebSocket.cs View File

@@ -1,6 +1,7 @@
using Discord.API.Client;
using Discord.Logging;
using Newtonsoft.Json;
using Nito.AsyncEx;
using System;
using System.IO;
using System.IO.Compression;
@@ -11,7 +12,7 @@ namespace Discord.Net.WebSockets
{
public abstract partial class WebSocket
{
private readonly Semaphore _lock;
private readonly AsyncLock _lock;
protected readonly IWebSocketEngine _engine;
protected readonly DiscordClient _client;
protected readonly ManualResetEventSlim _connectedEvent;
@@ -45,7 +46,7 @@ namespace Discord.Net.WebSockets
Logger = logger;
_serializer = serializer;

_lock = new Semaphore(1, 1);
_lock = new AsyncLock();
_taskManager = new TaskManager(Cleanup);
CancelToken = new CancellationToken(true);
_connectedEvent = new ManualResetEventSlim(false);
@@ -74,8 +75,7 @@ namespace Discord.Net.WebSockets
{
try
{
_lock.WaitOne();
try
using (await _lock.LockAsync())
{
await _taskManager.Stop().ConfigureAwait(false);
_taskManager.ClearException();
@@ -87,10 +87,6 @@ namespace Discord.Net.WebSockets

await _engine.Connect(Host, CancelToken).ConfigureAwait(false);
await Run().ConfigureAwait(false);
}
finally
{
_lock.Release();
}
}
catch (Exception ex)


+ 24
- 23
src/Discord.Net/TaskManager.cs View File

@@ -1,4 +1,5 @@
using System;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
@@ -10,7 +11,7 @@ namespace Discord
/// <summary> Helper class used to manage several tasks and keep them in sync. If any single task errors or stops, all other tasks will also be stopped. </summary>
public sealed class TaskManager
{
private readonly object _lock;
private readonly AsyncLock _lock;
private readonly Func<Task> _stopAction;

private CancellationTokenSource _cancelSource;
@@ -24,7 +25,7 @@ namespace Discord

internal TaskManager()
{
_lock = new object();
_lock = new AsyncLock();
}
public TaskManager(Action stopAction)
: this()
@@ -45,7 +46,7 @@ namespace Discord
if (task != null)
await Stop().ConfigureAwait(false);

lock (_lock)
using (await _lock.LockAsync())
{
_cancelSource = cancelSource;

@@ -88,7 +89,7 @@ namespace Discord

public void SignalStop(bool isExpected = false)
{
lock (_lock)
using (_lock.Lock())
{
if (isExpected)
_wasStopExpected = true;
@@ -100,28 +101,27 @@ namespace Discord
_cancelSource.Cancel();
}
}
public Task Stop(bool isExpected = false)
public async Task Stop(bool isExpected = false)
{
Task task;
lock (_lock)
using (await _lock.LockAsync())
{
if (isExpected)
_wasStopExpected = true;

//Cache the task so we still have something to await if Cleanup is run really quickly
task = _task;
if (task == null) return TaskHelper.CompletedTask; //Are we running?
if (_cancelSource.IsCancellationRequested) return task;
if (task == null) return; //Are we running?

if (_cancelSource != null)
if (!_cancelSource.IsCancellationRequested && _cancelSource != null)
_cancelSource.Cancel();
}
return task;
await task;
}

public void SignalError(Exception ex)
{
lock (_lock)
using (_lock.Lock())
{
if (_stopReason != null) return;

@@ -130,33 +130,34 @@ namespace Discord
_cancelSource.Cancel();
}
}
public Task Error(Exception ex)
public async Task Error(Exception ex)
{
Task task;
lock (_lock)
using (await _lock.LockAsync())
{
if (_stopReason != null) return TaskHelper.CompletedTask;
if (_stopReason != null) return;

//Cache the task so we still have something to await if Cleanup is run really quickly
task = _task ?? TaskHelper.CompletedTask;
if (_cancelSource.IsCancellationRequested) return task;

_stopReason = ExceptionDispatchInfo.Capture(ex);
if (_cancelSource != null)
_cancelSource.Cancel();
if (!_cancelSource.IsCancellationRequested)
{
_stopReason = ExceptionDispatchInfo.Capture(ex);
if (_cancelSource != null)
_cancelSource.Cancel();
}
}
return task;
await task;
}

/// <summary> Throws an exception if one was captured. </summary>
public void ThrowException()
{
lock (_lock)
using (_lock.Lock())
_stopReason?.Throw();
}
public void ClearException()
{
lock (_lock)
using (_lock.Lock())
{
_stopReason = null;
_wasStopExpected = false;


+ 7
- 3
src/Discord.Net/project.json View File

@@ -33,7 +33,8 @@
},

"dependencies": {
"Newtonsoft.Json": "7.0.1"
"Newtonsoft.Json": "7.0.1",
"Nito.AsyncEx": "3.0.1"
},

"frameworks": {
@@ -52,11 +53,14 @@
"System.Runtime.InteropServices": "4.0.21-beta-23516",
"System.Security.Cryptography.Algorithms": "4.0.0-beta-23516",
"System.Text.RegularExpressions": "4.0.11-beta-23516",
"System.Threading": "4.0.11-beta-23516",
"System.Threading.Thread": "4.0.0-beta-23516"
"System.Threading": "4.0.11-beta-23516"
}
},
"net45": {
"frameworkAssemblies": {
"System.Runtime": "4.0.0.0",
"System.Threading.Tasks": "4.0.0.0"
},
"dependencies": {
"WebSocket4Net": "0.14.1",
"RestSharp": "105.2.3"


Loading…
Cancel
Save