From 5005ddf98aaa5186de6b907487b95c83c5f808f4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 12 Aug 2017 02:27:26 -0300 Subject: [PATCH] Added Pool utility class --- src/Discord.Net.Core/Discord.Net.Core.csproj | 6 +- src/Discord.Net.Rest/DiscordRestApiClient.cs | 19 +++--- src/Discord.Net.Rpc/DiscordRpcApiClient.cs | 6 +- src/Discord.Net.Serialization/Utils/Pool.cs | 71 ++++++++++++++++++++++ .../DiscordSocketApiClient.cs | 5 +- src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs | 9 ++- 6 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 src/Discord.Net.Serialization/Utils/Pool.cs diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index f5a86113d..bac6fd4e1 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -7,11 +7,11 @@ net45;netstandard1.1;netstandard1.3 - - + + - + diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index ab677c12b..f5e3e31b2 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -4,8 +4,6 @@ using Discord.Net; using Discord.Net.Queue; using Discord.Net.Rest; using Discord.Serialization; -using Discord.Serialization.Json; -using Discord.Serialization.Json.Converters; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -13,7 +11,6 @@ using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Net; -using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.Formatting; @@ -31,7 +28,7 @@ namespace Discord.API protected readonly SemaphoreSlim _stateLock; protected readonly Serializer _serializer; - protected readonly ConcurrentQueue _formatters; + protected readonly Pool _formatters; private readonly RestClientProvider _restClientProvider; protected bool _isDisposed; @@ -56,7 +53,7 @@ namespace Discord.API RequestQueue = new RequestQueue(); _stateLock = new SemaphoreSlim(1, 1); - _formatters = new ConcurrentQueue(); + _formatters = new Pool(() => new ArrayFormatter(128, SymbolTable.InvariantUtf8)); SetBaseUrl(DiscordConfig.APIUrl); } @@ -190,9 +187,8 @@ namespace Discord.API options.HeaderOnly = true; options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - - if (!_formatters.TryDequeue(out var data)) - data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); + + var data = _formatters.Rent(); try { var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); @@ -201,7 +197,7 @@ namespace Discord.API finally { data.Clear(); - _formatters.Enqueue(data); + _formatters.Return(data); } } @@ -244,8 +240,7 @@ namespace Discord.API options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - if (!_formatters.TryDequeue(out var data)) - data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); + var data = _formatters.Rent(); try { var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); @@ -254,7 +249,7 @@ namespace Discord.API finally { data.Clear(); - _formatters.Enqueue(data); + _formatters.Return(data); } } diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs index 6c99e2fe3..0995fe2e7 100644 --- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -225,8 +225,7 @@ namespace Discord.API private async Task SendRpcAsyncInternal(string cmd, object payload, Optional evt, RequestOptions options) where TResponse : class, new() { - if (_formatters.TryDequeue(out var data)) - data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); + var data = _formatters.Rent(); try { var guid = Guid.NewGuid(); @@ -241,7 +240,8 @@ namespace Discord.API } finally { - _formatters.Enqueue(data); + data.Clear(); + _formatters.Return(data); } } diff --git a/src/Discord.Net.Serialization/Utils/Pool.cs b/src/Discord.Net.Serialization/Utils/Pool.cs new file mode 100644 index 000000000..7b77e93fa --- /dev/null +++ b/src/Discord.Net.Serialization/Utils/Pool.cs @@ -0,0 +1,71 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Discord.Serialization +{ + //TODO: Replace pools in audio with this + public sealed class Pool + where T : class + { + private const int DefaultMaxElementsPerBucket = 50; + + private readonly T[] _buffer; + private readonly Func _createFunc; + + private SpinLock _lock; + private int _index; + + public Pool(Func createFunc) + : this(createFunc, DefaultMaxElementsPerBucket) { } + public Pool(Func createFunc, int maxElementsPerBucket) + { + _createFunc = createFunc; + _lock = new SpinLock(Debugger.IsAttached); + _buffer = new T[maxElementsPerBucket]; + } + + public T Rent() + { + T result = null; + bool lockTaken = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index < _buffer.Length) + { + result = _buffer[_index]; + _buffer[_index++] = null; + } + } + finally + { + if (lockTaken) + _lock.Exit(false); + } + + if (result == null) + result = _createFunc(); + + return result; + } + + public void Return(T obj) + { + bool lockTaken = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index != 0) + _buffer[--_index] = obj; + } + finally + { + if (lockTaken) + _lock.Exit(false); + } + } + } +} diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 473b520c6..2f7474c7a 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -174,8 +174,7 @@ namespace Discord.API { CheckState(); - if (!_formatters.TryDequeue(out var data)) - data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); + var data = _formatters.Rent(); try { var frame = new GatewaySocketFrame { Operation = opCode, Payload = payload }; @@ -185,7 +184,7 @@ namespace Discord.API finally { data.Clear(); - _formatters.Enqueue(data); + _formatters.Return(data); } } diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index 0c3fdc188..12edbfea9 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -41,7 +41,7 @@ namespace Discord.Audio private readonly Serializer _serializer; private readonly SemaphoreSlim _connectionLock; private readonly MemoryStream _decompressionStream; - protected readonly ConcurrentQueue _formatters; + protected readonly Pool _formatters; private CancellationTokenSource _connectCancelToken; private IUdpSocket _udp; @@ -61,7 +61,7 @@ namespace Discord.Audio _udp = udpSocketProvider(); _udp.ReceivedDatagram += (data, index, count) => _receivedPacketEvent.InvokeAsync(data, index, count); _serializer = serializer; - _formatters = new ConcurrentQueue(); + _formatters = new Pool(() => new ArrayFormatter(128, SymbolTable.InvariantUtf8)); _decompressionStream = new MemoryStream(10 * 1024); //10 KB @@ -114,8 +114,7 @@ namespace Discord.Audio public async Task SendAsync(VoiceOpCode opCode, object payload, RequestOptions options = null) { - if (!_formatters.TryDequeue(out var data)) - data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); + var data = _formatters.Rent(); try { var frame = new VoiceSocketFrame { Operation = opCode, Payload = payload }; @@ -125,7 +124,7 @@ namespace Discord.Audio finally { data.Clear(); - _formatters.Enqueue(data); + _formatters.Return(data); } } public async Task SendAsync(byte[] data, int offset, int bytes)