| @@ -7,11 +7,11 @@ | |||
| <TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | |||
| </PropertyGroup> | |||
| <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.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> | |||
| <ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" /> | |||
| @@ -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<ArrayFormatter> _formatters; | |||
| protected readonly Pool<ArrayFormatter> _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<ArrayFormatter>(); | |||
| _formatters = new Pool<ArrayFormatter>(() => 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); | |||
| } | |||
| } | |||
| @@ -225,8 +225,7 @@ namespace Discord.API | |||
| private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> 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); | |||
| } | |||
| } | |||
| @@ -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(); | |||
| 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); | |||
| } | |||
| } | |||
| @@ -41,7 +41,7 @@ namespace Discord.Audio | |||
| private readonly Serializer _serializer; | |||
| private readonly SemaphoreSlim _connectionLock; | |||
| private readonly MemoryStream _decompressionStream; | |||
| protected readonly ConcurrentQueue<ArrayFormatter> _formatters; | |||
| protected readonly Pool<ArrayFormatter> _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<ArrayFormatter>(); | |||
| _formatters = new Pool<ArrayFormatter>(() => 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) | |||