@@ -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) | ||||