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