@@ -14,9 +14,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
14
14
{
15
15
public class SocketOutput : ISocketOutput
16
16
{
17
+ public const int MaxPooledWriteReqs = 1024 ;
18
+
17
19
private const int _maxPendingWrites = 3 ;
18
20
private const int _maxBytesPreCompleted = 65536 ;
19
21
private const int _initialTaskQueues = 64 ;
22
+ private const int _maxPooledWriteContexts = 32 ;
20
23
21
24
private static WaitCallback _returnBlocks = ( state ) => ReturnBlocks ( ( MemoryPoolBlock2 ) state ) ;
22
25
@@ -42,12 +45,13 @@ public class SocketOutput : ISocketOutput
42
45
// The number of write operations that have been scheduled so far
43
46
// but have not completed.
44
47
private int _writesPending = 0 ;
45
-
46
48
private int _numBytesPreCompleted = 0 ;
47
49
private Exception _lastWriteError ;
48
50
private WriteContext _nextWriteContext ;
49
51
private readonly Queue < TaskCompletionSource < object > > _tasksPending ;
50
52
private readonly Queue < TaskCompletionSource < object > > _tasksCompleted ;
53
+ private readonly Queue < WriteContext > _writeContextPool ;
54
+ private readonly Queue < UvWriteReq > _writeReqPool ;
51
55
52
56
public SocketOutput (
53
57
KestrelThread thread ,
@@ -56,7 +60,8 @@ public SocketOutput(
56
60
Connection connection ,
57
61
long connectionId ,
58
62
IKestrelTrace log ,
59
- IThreadPool threadPool )
63
+ IThreadPool threadPool ,
64
+ Queue < UvWriteReq > writeReqPool )
60
65
{
61
66
_thread = thread ;
62
67
_socket = socket ;
@@ -66,6 +71,8 @@ public SocketOutput(
66
71
_threadPool = threadPool ;
67
72
_tasksPending = new Queue < TaskCompletionSource < object > > ( _initialTaskQueues ) ;
68
73
_tasksCompleted = new Queue < TaskCompletionSource < object > > ( _initialTaskQueues ) ;
74
+ _writeContextPool = new Queue < WriteContext > ( _maxPooledWriteContexts ) ;
75
+ _writeReqPool = writeReqPool ;
69
76
70
77
_head = memory . Lease ( ) ;
71
78
_tail = _head ;
@@ -92,7 +99,14 @@ public Task WriteAsync(
92
99
{
93
100
if ( _nextWriteContext == null )
94
101
{
95
- _nextWriteContext = new WriteContext ( this ) ;
102
+ if ( _writeContextPool . Count > 0 )
103
+ {
104
+ _nextWriteContext = _writeContextPool . Dequeue ( ) ;
105
+ }
106
+ else
107
+ {
108
+ _nextWriteContext = new WriteContext ( this ) ;
109
+ }
96
110
}
97
111
98
112
if ( socketShutdownSend )
@@ -274,9 +288,12 @@ private void WriteAllPending()
274
288
}
275
289
276
290
// This is called on the libuv event loop
277
- private void OnWriteCompleted ( int bytesWritten , int status , Exception error )
291
+ private void OnWriteCompleted ( WriteContext writeContext )
278
292
{
279
- _log . ConnectionWriteCallback ( _connectionId , status ) ;
293
+ var bytesWritten = writeContext . ByteCount ;
294
+ var status = writeContext . WriteStatus ;
295
+ var error = writeContext . WriteError ;
296
+
280
297
281
298
if ( error != null )
282
299
{
@@ -290,6 +307,7 @@ private void OnWriteCompleted(int bytesWritten, int status, Exception error)
290
307
291
308
lock ( _contextLock )
292
309
{
310
+ PoolWriteContext ( writeContext ) ;
293
311
if ( _nextWriteContext != null )
294
312
{
295
313
scheduleWrite = true ;
@@ -332,11 +350,11 @@ private void OnWriteCompleted(int bytesWritten, int status, Exception error)
332
350
}
333
351
}
334
352
353
+ _log . ConnectionWriteCallback ( _connectionId , status ) ;
354
+
335
355
if ( scheduleWrite )
336
356
{
337
- // ScheduleWrite();
338
- // on right thread, fairness issues?
339
- WriteAllPending ( ) ;
357
+ ScheduleWrite ( ) ;
340
358
}
341
359
342
360
_tasksCompleted . Clear ( ) ;
@@ -367,6 +385,16 @@ private void ReturnAllBlocks()
367
385
}
368
386
}
369
387
388
+ private void PoolWriteContext ( WriteContext writeContext )
389
+ {
390
+ // called inside _contextLock
391
+ if ( _writeContextPool . Count < _maxPooledWriteContexts )
392
+ {
393
+ writeContext . Reset ( ) ;
394
+ _writeContextPool . Enqueue ( writeContext ) ;
395
+ }
396
+ }
397
+
370
398
void ISocketOutput . Write ( ArraySegment < byte > buffer , bool immediate )
371
399
{
372
400
var task = WriteAsync ( buffer , immediate ) ;
@@ -412,19 +440,18 @@ private class WriteContext
412
440
{
413
441
private static WaitCallback _returnWrittenBlocks = ( state ) => ReturnWrittenBlocks ( ( MemoryPoolBlock2 ) state ) ;
414
442
443
+ private SocketOutput Self ;
444
+ private UvWriteReq _writeReq ;
415
445
private MemoryPoolIterator2 _lockedStart ;
416
446
private MemoryPoolIterator2 _lockedEnd ;
417
447
private int _bufferCount ;
418
- private int _byteCount ;
419
-
420
- public SocketOutput Self ;
421
448
449
+ public int ByteCount ;
422
450
public bool SocketShutdownSend ;
423
451
public bool SocketDisconnect ;
424
452
425
453
public int WriteStatus ;
426
454
public Exception WriteError ;
427
-
428
455
public int ShutdownSendStatus ;
429
456
430
457
public WriteContext ( SocketOutput self )
@@ -439,27 +466,40 @@ public void DoWriteIfNeeded()
439
466
{
440
467
LockWrite ( ) ;
441
468
442
- if ( _byteCount == 0 || Self . _socket . IsClosed )
469
+ if ( ByteCount == 0 || Self . _socket . IsClosed )
443
470
{
444
471
DoShutdownIfNeeded ( ) ;
445
472
return ;
446
473
}
447
474
448
- var writeReq = new UvWriteReq ( Self . _log ) ;
449
- writeReq . Init ( Self . _thread . Loop ) ;
475
+ // Sample values locally in case write completes inline
476
+ // to allow block to be Reset and still complete this function
477
+ var lockedEndBlock = _lockedEnd . Block ;
478
+ var lockedEndIndex = _lockedEnd . Index ;
450
479
451
- writeReq . Write ( Self . _socket , _lockedStart , _lockedEnd , _bufferCount , ( _writeReq , status , error , state ) =>
480
+ if ( Self . _writeReqPool . Count > 0 )
452
481
{
453
- _writeReq . Dispose ( ) ;
454
- var _this = ( WriteContext ) state ;
455
- _this . ScheduleReturnFullyWrittenBlocks ( ) ;
456
- _this . WriteStatus = status ;
457
- _this . WriteError = error ;
458
- _this . DoShutdownIfNeeded ( ) ;
482
+ _writeReq = Self . _writeReqPool . Dequeue ( ) ;
483
+ }
484
+ else
485
+ {
486
+ _writeReq = new UvWriteReq ( Self . _log ) ;
487
+ _writeReq . Init ( Self . _thread . Loop ) ;
488
+ }
489
+
490
+ _writeReq . Write ( Self . _socket , _lockedStart , _lockedEnd , _bufferCount , ( _writeReq , status , error , state ) =>
491
+ {
492
+ var writeContext = ( WriteContext ) state ;
493
+ writeContext . PoolWriteReq ( writeContext . _writeReq ) ;
494
+ writeContext . _writeReq = null ;
495
+ writeContext . ScheduleReturnFullyWrittenBlocks ( ) ;
496
+ writeContext . WriteStatus = status ;
497
+ writeContext . WriteError = error ;
498
+ writeContext . DoShutdownIfNeeded ( ) ;
459
499
} , this ) ;
460
500
461
- Self . _head = _lockedEnd . Block ;
462
- Self . _head . Start = _lockedEnd . Index ;
501
+ Self . _head = lockedEndBlock ;
502
+ Self . _head . Start = lockedEndIndex ;
463
503
}
464
504
465
505
/// <summary>
@@ -506,9 +546,21 @@ public void DoDisconnectIfNeeded()
506
546
507
547
public void Complete ( )
508
548
{
509
- Self . OnWriteCompleted ( _byteCount , WriteStatus , WriteError ) ;
549
+ Self . OnWriteCompleted ( this ) ;
550
+ }
551
+
552
+ private void PoolWriteReq ( UvWriteReq writeReq )
553
+ {
554
+ if ( Self . _writeReqPool . Count < MaxPooledWriteReqs )
555
+ {
556
+ Self . _writeReqPool . Enqueue ( writeReq ) ;
557
+ }
558
+ else
559
+ {
560
+ writeReq . Dispose ( ) ;
561
+ }
510
562
}
511
-
563
+
512
564
private void ScheduleReturnFullyWrittenBlocks ( )
513
565
{
514
566
var block = _lockedStart . Block ;
@@ -556,7 +608,23 @@ private void LockWrite()
556
608
_lockedStart = new MemoryPoolIterator2 ( head , head . Start ) ;
557
609
_lockedEnd = new MemoryPoolIterator2 ( tail , tail . End ) ;
558
610
559
- BytesBetween ( _lockedStart , _lockedEnd , out _byteCount , out _bufferCount ) ;
611
+ BytesBetween ( _lockedStart , _lockedEnd , out ByteCount , out _bufferCount ) ;
612
+ }
613
+
614
+ public void Reset ( )
615
+ {
616
+ _lockedStart = default ( MemoryPoolIterator2 ) ;
617
+ _lockedEnd = default ( MemoryPoolIterator2 ) ;
618
+ _bufferCount = 0 ;
619
+ ByteCount = 0 ;
620
+
621
+ SocketShutdownSend = false ;
622
+ SocketDisconnect = false ;
623
+
624
+ WriteStatus = 0 ;
625
+ WriteError = null ;
626
+
627
+ ShutdownSendStatus = 0 ;
560
628
}
561
629
}
562
630
}
0 commit comments