Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 2a32d33

Browse files
committed
Initialize queue, pin sizes, reuse sends
1 parent 5d59a20 commit 2a32d33

File tree

3 files changed

+107
-26
lines changed

3 files changed

+107
-26
lines changed

src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class SocketOutput : ISocketOutput
1616
{
1717
private const int _maxPendingWrites = 3;
1818
private const int _maxBytesPreCompleted = 65536;
19+
private const int _maxPooledWriteContexts = 16;
20+
private const int _maxPooledBufferQueues = 16;
1921

2022
private readonly KestrelThread _thread;
2123
private readonly UvStreamHandle _socket;
@@ -25,6 +27,7 @@ public class SocketOutput : ISocketOutput
2527

2628
// This locks access to to all of the below fields
2729
private readonly object _lockObj = new object();
30+
private bool _isDisposed;
2831

2932
// The number of write operations that have been scheduled so far
3033
// but have not completed.
@@ -34,6 +37,7 @@ public class SocketOutput : ISocketOutput
3437
private Exception _lastWriteError;
3538
private WriteContext _nextWriteContext;
3639
private readonly Queue<TaskCompletionSource<object>> _tasksPending;
40+
private readonly Queue<WriteContext> _writeContexts;
3741

3842
public SocketOutput(
3943
KestrelThread thread,
@@ -47,7 +51,8 @@ public SocketOutput(
4751
_connection = connection;
4852
_connectionId = connectionId;
4953
_log = log;
50-
_tasksPending = new Queue<TaskCompletionSource<object>>();
54+
_tasksPending = new Queue<TaskCompletionSource<object>>(16);
55+
_writeContexts = new Queue<WriteContext>(_maxPooledWriteContexts);
5156
}
5257

5358
public Task WriteAsync(
@@ -71,7 +76,14 @@ public Task WriteAsync(
7176
{
7277
if (_nextWriteContext == null)
7378
{
74-
_nextWriteContext = new WriteContext(this);
79+
if (_writeContexts.Count > 0)
80+
{
81+
_nextWriteContext = _writeContexts.Dequeue();
82+
}
83+
else
84+
{
85+
_nextWriteContext = new WriteContext(this);
86+
}
7587
}
7688

7789
if (buffer.Array != null)
@@ -180,13 +192,13 @@ private void WriteAllPending()
180192
}
181193

182194
// This is called on the libuv event loop
183-
private void OnWriteCompleted(Queue<ArraySegment<byte>> writtenBuffers, int status, Exception error)
195+
private void OnWriteCompleted(WriteContext write)
184196
{
185-
_log.ConnectionWriteCallback(_connectionId, status);
197+
var status = write.WriteStatus;
186198

187-
if (error != null)
199+
if (write.WriteError != null)
188200
{
189-
_lastWriteError = new IOException(error.Message, error);
201+
_lastWriteError = new IOException(write.WriteError.Message, write.WriteError);
190202

191203
// Abort the connection for any failed write.
192204
_connection.Abort();
@@ -203,7 +215,7 @@ private void OnWriteCompleted(Queue<ArraySegment<byte>> writtenBuffers, int stat
203215
_writesPending--;
204216
}
205217

206-
foreach (var writeBuffer in writtenBuffers)
218+
foreach (var writeBuffer in write.Buffers)
207219
{
208220
// _numBytesPreCompleted can temporarily go negative in the event there are
209221
// completed writes that we haven't triggered callbacks for yet.
@@ -230,16 +242,31 @@ private void OnWriteCompleted(Queue<ArraySegment<byte>> writtenBuffers, int stat
230242
}
231243
else
232244
{
245+
var error = _lastWriteError;
233246
// error is closure captured
234247
ThreadPool.QueueUserWorkItem(
235-
(o) => ((TaskCompletionSource<object>)o).SetException(_lastWriteError),
248+
(o) => ((TaskCompletionSource<object>)o).SetException(error),
236249
tcs);
237250
}
238251
}
239252

253+
if (_writeContexts.Count < _maxPooledWriteContexts
254+
&& write.Buffers.Count <= _maxPooledBufferQueues
255+
&& !_isDisposed)
256+
{
257+
write.Reset();
258+
_writeContexts.Enqueue(write);
259+
}
260+
else
261+
{
262+
write.Dispose();
263+
}
264+
240265
// Now that the while loop has completed the following invariants should hold true:
241266
Debug.Assert(_numBytesPreCompleted >= 0);
242267
}
268+
269+
_log.ConnectionWriteCallback(_connectionId, status);
243270
}
244271

245272
void ISocketOutput.Write(ArraySegment<byte> buffer, bool immediate)
@@ -261,8 +288,24 @@ Task ISocketOutput.WriteAsync(ArraySegment<byte> buffer, bool immediate, Cancell
261288
return WriteAsync(buffer, immediate);
262289
}
263290

264-
private class WriteContext
291+
private void Dispose()
265292
{
293+
lock (_lockObj)
294+
{
295+
_isDisposed = true;
296+
297+
while (_writeContexts.Count > 0)
298+
{
299+
_writeContexts.Dequeue().Dispose();
300+
}
301+
}
302+
303+
}
304+
305+
private class WriteContext : IDisposable
306+
{
307+
private const int BUFFER_COUNT = 4;
308+
266309
public SocketOutput Self;
267310

268311
public Queue<ArraySegment<byte>> Buffers;
@@ -272,12 +315,18 @@ private class WriteContext
272315
public int WriteStatus;
273316
public Exception WriteError;
274317

318+
private UvWriteReq _writeReq;
319+
public ArraySegment<byte>[] _segments;
320+
275321
public int ShutdownSendStatus;
276322

277323
public WriteContext(SocketOutput self)
278324
{
279325
Self = self;
280-
Buffers = new Queue<ArraySegment<byte>>();
326+
Buffers = new Queue<ArraySegment<byte>>(_maxPooledBufferQueues);
327+
_segments = new ArraySegment<byte>[BUFFER_COUNT];
328+
_writeReq = new UvWriteReq(Self._log);
329+
_writeReq.Init(Self._thread.Loop);
281330
}
282331

283332
/// <summary>
@@ -291,19 +340,24 @@ public void DoWriteIfNeeded()
291340
return;
292341
}
293342

294-
var buffers = new ArraySegment<byte>[Buffers.Count];
343+
ArraySegment<byte>[] segments;
344+
if (Buffers.Count > BUFFER_COUNT)
345+
{
346+
segments = new ArraySegment<byte>[Buffers.Count];
347+
}
348+
else
349+
{
350+
segments = _segments;
351+
}
295352

296353
var i = 0;
297354
foreach (var buffer in Buffers)
298355
{
299-
buffers[i++] = buffer;
356+
segments[i++] = buffer;
300357
}
301-
302-
var writeReq = new UvWriteReq(Self._log);
303-
writeReq.Init(Self._thread.Loop);
304-
writeReq.Write(Self._socket, new ArraySegment<ArraySegment<byte>>(buffers), (_writeReq, status, error, state) =>
358+
359+
_writeReq.Write(Self._socket, new ArraySegment<ArraySegment<byte>>(segments, 0, Buffers.Count), (_writeReq, status, error, state) =>
305360
{
306-
_writeReq.Dispose();
307361
var _this = (WriteContext)state;
308362
_this.WriteStatus = status;
309363
_this.WriteError = error;
@@ -330,7 +384,7 @@ public void DoShutdownIfNeeded()
330384
var _this = (WriteContext)state;
331385
_this.ShutdownSendStatus = status;
332386

333-
_this.Self._log.ConnectionWroteFin(Self._connectionId, status);
387+
_this.Self._log.ConnectionWroteFin(_this.Self._connectionId, status);
334388

335389
_this.DoDisconnectIfNeeded();
336390
}, this);
@@ -341,8 +395,14 @@ public void DoShutdownIfNeeded()
341395
/// </summary>
342396
public void DoDisconnectIfNeeded()
343397
{
344-
if (SocketDisconnect == false || Self._socket.IsClosed)
398+
if (SocketDisconnect == false)
399+
{
400+
Complete();
401+
return;
402+
}
403+
else if (Self._socket.IsClosed)
345404
{
405+
Self.Dispose();
346406
Complete();
347407
return;
348408
}
@@ -354,7 +414,28 @@ public void DoDisconnectIfNeeded()
354414

355415
public void Complete()
356416
{
357-
Self.OnWriteCompleted(Buffers, WriteStatus, WriteError);
417+
Self.OnWriteCompleted(this);
418+
}
419+
420+
public void Reset()
421+
{
422+
Buffers.Clear();
423+
SocketDisconnect = false;
424+
SocketShutdownSend = false;
425+
WriteStatus = 0;
426+
WriteError = null;
427+
ShutdownSendStatus = 0;
428+
429+
var segments = _segments;
430+
for (var i = 0; i < segments.Length; i++)
431+
{
432+
segments[i] = default(ArraySegment<byte>);
433+
}
434+
}
435+
436+
public void Dispose()
437+
{
438+
_writeReq.Dispose();
358439
}
359440
}
360441
}

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public class KestrelThread
2424
private Thread _thread;
2525
private UvLoopHandle _loop;
2626
private UvAsyncHandle _post;
27-
private Queue<Work> _workAdding = new Queue<Work>();
28-
private Queue<Work> _workRunning = new Queue<Work>();
29-
private Queue<CloseHandle> _closeHandleAdding = new Queue<CloseHandle>();
30-
private Queue<CloseHandle> _closeHandleRunning = new Queue<CloseHandle>();
27+
private Queue<Work> _workAdding = new Queue<Work>(1024);
28+
private Queue<Work> _workRunning = new Queue<Work>(1024);
29+
private Queue<CloseHandle> _closeHandleAdding = new Queue<CloseHandle>(256);
30+
private Queue<CloseHandle> _closeHandleRunning = new Queue<CloseHandle>(256);
3131
private object _workSync = new Object();
3232
private bool _stopImmediate = false;
3333
private bool _initCompleted = false;

src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
1414
/// </summary>
1515
public class UvWriteReq : UvRequest
1616
{
17-
private readonly static Libuv.uv_write_cb _uv_write_cb = UvWriteCb;
17+
private readonly static Libuv.uv_write_cb _uv_write_cb = (IntPtr ptr, int status) => UvWriteCb(ptr, status);
1818

1919
private IntPtr _bufs;
2020

2121
private Action<UvWriteReq, int, Exception, object> _callback;
2222
private object _state;
2323
private const int BUFFER_COUNT = 4;
2424

25-
private List<GCHandle> _pins = new List<GCHandle>();
25+
private List<GCHandle> _pins = new List<GCHandle>(BUFFER_COUNT + 1);
2626

2727
public UvWriteReq(IKestrelTrace logger) : base(logger)
2828
{

0 commit comments

Comments
 (0)