| @@ -7,11 +7,11 @@ | |||||
| <TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | <TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | ||||
| </PropertyGroup> | </PropertyGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="System.Buffers" Version="4.4.0-preview2-25405-01" /> | |||||
| <PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> | |||||
| <PackageReference Include="System.Buffers" Version="4.4.0" /> | |||||
| <PackageReference Include="System.Collections.Immutable" Version="1.4.0" /> | |||||
| <PackageReference Include="System.Interactive.Async" Version="3.1.1" /> | <PackageReference Include="System.Interactive.Async" Version="3.1.1" /> | ||||
| <PackageReference Include="System.Memory" Version="4.4.0-preview2-25405-01" /> | <PackageReference Include="System.Memory" Version="4.4.0-preview2-25405-01" /> | ||||
| <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-preview2-25405-01" /> | |||||
| <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" /> | <ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" /> | ||||
| @@ -4,8 +4,6 @@ using Discord.Net; | |||||
| using Discord.Net.Queue; | using Discord.Net.Queue; | ||||
| using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
| using Discord.Serialization; | using Discord.Serialization; | ||||
| using Discord.Serialization.Json; | |||||
| using Discord.Serialization.Json.Converters; | |||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -13,7 +11,6 @@ using System.Diagnostics; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Linq.Expressions; | using System.Linq.Expressions; | ||||
| using System.Net; | using System.Net; | ||||
| using System.Reflection; | |||||
| using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; | ||||
| using System.Text; | using System.Text; | ||||
| using System.Text.Formatting; | using System.Text.Formatting; | ||||
| @@ -31,7 +28,7 @@ namespace Discord.API | |||||
| protected readonly SemaphoreSlim _stateLock; | protected readonly SemaphoreSlim _stateLock; | ||||
| protected readonly Serializer _serializer; | protected readonly Serializer _serializer; | ||||
| protected readonly ConcurrentQueue<ArrayFormatter> _formatters; | |||||
| protected readonly Pool<ArrayFormatter> _formatters; | |||||
| private readonly RestClientProvider _restClientProvider; | private readonly RestClientProvider _restClientProvider; | ||||
| protected bool _isDisposed; | protected bool _isDisposed; | ||||
| @@ -56,7 +53,7 @@ namespace Discord.API | |||||
| RequestQueue = new RequestQueue(); | RequestQueue = new RequestQueue(); | ||||
| _stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
| _formatters = new ConcurrentQueue<ArrayFormatter>(); | |||||
| _formatters = new Pool<ArrayFormatter>(() => new ArrayFormatter(128, SymbolTable.InvariantUtf8)); | |||||
| SetBaseUrl(DiscordConfig.APIUrl); | SetBaseUrl(DiscordConfig.APIUrl); | ||||
| } | } | ||||
| @@ -190,9 +187,8 @@ namespace Discord.API | |||||
| options.HeaderOnly = true; | options.HeaderOnly = true; | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | ||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | options.IsClientBucket = AuthTokenType == TokenType.User; | ||||
| if (!_formatters.TryDequeue(out var data)) | |||||
| data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); | |||||
| var data = _formatters.Rent(); | |||||
| try | try | ||||
| { | { | ||||
| var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | ||||
| @@ -201,7 +197,7 @@ namespace Discord.API | |||||
| finally | finally | ||||
| { | { | ||||
| data.Clear(); | 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.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | ||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | options.IsClientBucket = AuthTokenType == TokenType.User; | ||||
| if (!_formatters.TryDequeue(out var data)) | |||||
| data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); | |||||
| var data = _formatters.Rent(); | |||||
| try | try | ||||
| { | { | ||||
| var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | ||||
| @@ -254,7 +249,7 @@ namespace Discord.API | |||||
| finally | finally | ||||
| { | { | ||||
| data.Clear(); | data.Clear(); | ||||
| _formatters.Enqueue(data); | |||||
| _formatters.Return(data); | |||||
| } | } | ||||
| } | } | ||||
| @@ -225,8 +225,7 @@ namespace Discord.API | |||||
| private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> evt, RequestOptions options) | private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> evt, RequestOptions options) | ||||
| where TResponse : class, new() | where TResponse : class, new() | ||||
| { | { | ||||
| if (_formatters.TryDequeue(out var data)) | |||||
| data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); | |||||
| var data = _formatters.Rent(); | |||||
| try | try | ||||
| { | { | ||||
| var guid = Guid.NewGuid(); | var guid = Guid.NewGuid(); | ||||
| @@ -241,7 +240,8 @@ namespace Discord.API | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| _formatters.Enqueue(data); | |||||
| data.Clear(); | |||||
| _formatters.Return(data); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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<T> | |||||
| where T : class | |||||
| { | |||||
| private const int DefaultMaxElementsPerBucket = 50; | |||||
| private readonly T[] _buffer; | |||||
| private readonly Func<T> _createFunc; | |||||
| private SpinLock _lock; | |||||
| private int _index; | |||||
| public Pool(Func<T> createFunc) | |||||
| : this(createFunc, DefaultMaxElementsPerBucket) { } | |||||
| public Pool(Func<T> 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -174,8 +174,7 @@ namespace Discord.API | |||||
| { | { | ||||
| CheckState(); | CheckState(); | ||||
| if (!_formatters.TryDequeue(out var data)) | |||||
| data = new ArrayFormatter(128, SymbolTable.InvariantUtf8); | |||||
| var data = _formatters.Rent(); | |||||
| try | try | ||||
| { | { | ||||
| var frame = new GatewaySocketFrame { Operation = opCode, Payload = payload }; | var frame = new GatewaySocketFrame { Operation = opCode, Payload = payload }; | ||||
| @@ -185,7 +184,7 @@ namespace Discord.API | |||||
| finally | finally | ||||
| { | { | ||||
| data.Clear(); | data.Clear(); | ||||
| _formatters.Enqueue(data); | |||||
| _formatters.Return(data); | |||||
| } | } | ||||
| } | } | ||||
| @@ -41,7 +41,7 @@ namespace Discord.Audio | |||||
| private readonly Serializer _serializer; | private readonly Serializer _serializer; | ||||
| private readonly SemaphoreSlim _connectionLock; | private readonly SemaphoreSlim _connectionLock; | ||||
| private readonly MemoryStream _decompressionStream; | private readonly MemoryStream _decompressionStream; | ||||
| protected readonly ConcurrentQueue<ArrayFormatter> _formatters; | |||||
| protected readonly Pool<ArrayFormatter> _formatters; | |||||
| private CancellationTokenSource _connectCancelToken; | private CancellationTokenSource _connectCancelToken; | ||||
| private IUdpSocket _udp; | private IUdpSocket _udp; | ||||
| @@ -61,7 +61,7 @@ namespace Discord.Audio | |||||
| _udp = udpSocketProvider(); | _udp = udpSocketProvider(); | ||||
| _udp.ReceivedDatagram += (data, index, count) => _receivedPacketEvent.InvokeAsync(data, index, count); | _udp.ReceivedDatagram += (data, index, count) => _receivedPacketEvent.InvokeAsync(data, index, count); | ||||
| _serializer = serializer; | _serializer = serializer; | ||||
| _formatters = new ConcurrentQueue<ArrayFormatter>(); | |||||
| _formatters = new Pool<ArrayFormatter>(() => new ArrayFormatter(128, SymbolTable.InvariantUtf8)); | |||||
| _decompressionStream = new MemoryStream(10 * 1024); //10 KB | _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) | 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 | try | ||||
| { | { | ||||
| var frame = new VoiceSocketFrame { Operation = opCode, Payload = payload }; | var frame = new VoiceSocketFrame { Operation = opCode, Payload = payload }; | ||||
| @@ -125,7 +124,7 @@ namespace Discord.Audio | |||||
| finally | finally | ||||
| { | { | ||||
| data.Clear(); | data.Clear(); | ||||
| _formatters.Enqueue(data); | |||||
| _formatters.Return(data); | |||||
| } | } | ||||
| } | } | ||||
| public async Task SendAsync(byte[] data, int offset, int bytes) | public async Task SendAsync(byte[] data, int offset, int bytes) | ||||