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

Commit 81dba39

Browse files
committed
less work in locks
1 parent feb4040 commit 81dba39

File tree

1 file changed

+100
-35
lines changed

1 file changed

+100
-35
lines changed

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

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public class SocketOutput : ISocketOutput
1616
{
1717
private const int _maxPendingWrites = 3;
1818
private const int _maxBytesPreCompleted = 65536;
19+
private const int _initialTaskQueues = 64;
20+
21+
private static WaitCallback _returnBlocks = (state) => ReturnBlocks((MemoryPoolBlock2)state);
1922

2023
private readonly KestrelThread _thread;
2124
private readonly UvStreamHandle _socket;
@@ -44,6 +47,7 @@ public class SocketOutput : ISocketOutput
4447
private Exception _lastWriteError;
4548
private WriteContext _nextWriteContext;
4649
private readonly Queue<TaskCompletionSource<object>> _tasksPending;
50+
private readonly Queue<TaskCompletionSource<object>> _tasksCompleted;
4751

4852
public SocketOutput(
4953
KestrelThread thread,
@@ -58,7 +62,8 @@ public SocketOutput(
5862
_connection = connection;
5963
_connectionId = connectionId;
6064
_log = log;
61-
_tasksPending = new Queue<TaskCompletionSource<object>>();
65+
_tasksPending = new Queue<TaskCompletionSource<object>>(_initialTaskQueues);
66+
_tasksCompleted = new Queue<TaskCompletionSource<object>>(_initialTaskQueues);
6267

6368
_head = memory.Lease();
6469
_tail = _head;
@@ -79,6 +84,8 @@ public Task WriteAsync(
7984
}
8085
TaskCompletionSource<object> tcs = null;
8186

87+
var scheduleWrite = false;
88+
8289
lock (_contextLock)
8390
{
8491
if (_nextWriteContext == null)
@@ -118,11 +125,16 @@ public Task WriteAsync(
118125

119126
if (_writesPending < _maxPendingWrites && immediate)
120127
{
121-
ScheduleWrite();
128+
scheduleWrite = true;
122129
_writesPending++;
123130
}
124131
}
125132

133+
if (scheduleWrite)
134+
{
135+
ScheduleWrite();
136+
}
137+
126138
// Return TaskCompletionSource's Task if set, otherwise completed Task
127139
return tcs?.Task ?? TaskUtilities.CompletedTask;
128140
}
@@ -164,6 +176,9 @@ public MemoryPoolIterator2 ProducingStart()
164176

165177
public void ProducingComplete(MemoryPoolIterator2 end, int count)
166178
{
179+
var decreasePreCompleted = false;
180+
MemoryPoolBlock2 blockToReturn = null;
181+
167182
lock (_returnLock)
168183
{
169184
Debug.Assert(_isProducing);
@@ -176,26 +191,40 @@ public void ProducingComplete(MemoryPoolIterator2 end, int count)
176191

177192
if (count != 0)
178193
{
179-
lock (_contextLock)
180-
{
181-
_numBytesPreCompleted += count;
182-
}
194+
decreasePreCompleted = true;
183195
}
184196
}
185197
else
186198
{
187-
var block = _returnFromOnProducingComplete;
188-
while (block != null)
189-
{
190-
var returnBlock = block;
191-
block = block.Next;
192-
193-
returnBlock.Pool?.Return(returnBlock);
194-
}
195-
199+
blockToReturn = _returnFromOnProducingComplete;
196200
_returnFromOnProducingComplete = null;
197201
}
198202
}
203+
204+
if (decreasePreCompleted)
205+
{
206+
lock (_contextLock)
207+
{
208+
_numBytesPreCompleted += count;
209+
}
210+
}
211+
212+
213+
if (blockToReturn != null)
214+
{
215+
ThreadPool.QueueUserWorkItem(_returnBlocks, blockToReturn);
216+
}
217+
}
218+
219+
private static void ReturnBlocks(MemoryPoolBlock2 block)
220+
{
221+
while(block != null)
222+
{
223+
var returningBlock = block;
224+
block = returningBlock.Next;
225+
226+
returningBlock.Pool?.Return(returningBlock);
227+
}
199228
}
200229

201230
private void ScheduleWrite()
@@ -252,11 +281,13 @@ private void OnWriteCompleted(int bytesWritten, int status, Exception error)
252281
_connection.Abort();
253282
}
254283

284+
bool scheduleWrite = false;
285+
255286
lock (_contextLock)
256287
{
257288
if (_nextWriteContext != null)
258289
{
259-
ScheduleWrite();
290+
scheduleWrite = true;
260291
}
261292
else
262293
{
@@ -279,21 +310,36 @@ private void OnWriteCompleted(int bytesWritten, int status, Exception error)
279310
_numBytesPreCompleted += bytesToWrite;
280311
bytesLeftToBuffer -= bytesToWrite;
281312

282-
if (_lastWriteError == null)
283-
{
284-
ThreadPool.QueueUserWorkItem(
285-
(o) => ((TaskCompletionSource<object>)o).SetResult(null),
286-
tcs);
287-
}
288-
else
289-
{
290-
// error is closure captured
291-
ThreadPool.QueueUserWorkItem(
292-
(o) => ((TaskCompletionSource<object>)o).SetException(_lastWriteError),
293-
tcs);
294-
}
313+
_tasksCompleted.Enqueue(tcs);
314+
}
315+
}
316+
317+
while (_tasksCompleted.Count > 0)
318+
{
319+
var tcs = _tasksCompleted.Dequeue();
320+
if (_lastWriteError == null)
321+
{
322+
ThreadPool.QueueUserWorkItem(
323+
(o) => ((TaskCompletionSource<object>)o).SetResult(null),
324+
tcs);
325+
}
326+
else
327+
{
328+
// error is closure captured
329+
ThreadPool.QueueUserWorkItem(
330+
(o) => ((TaskCompletionSource<object>)o).SetException(_lastWriteError),
331+
tcs);
295332
}
296333
}
334+
335+
if (scheduleWrite)
336+
{
337+
// ScheduleWrite();
338+
// on right thread, fairness issues?
339+
WriteAllPending();
340+
}
341+
342+
_tasksCompleted.Clear();
297343
}
298344

299345
// This is called on the libuv event loop
@@ -345,6 +391,8 @@ Task ISocketOutput.WriteAsync(ArraySegment<byte> buffer, bool immediate, Cancell
345391

346392
private class WriteContext
347393
{
394+
private static WaitCallback _returnWrittenBlocks = (state) => ReturnWrittenBlocks((MemoryPoolBlock2)state);
395+
348396
private MemoryPoolIterator2 _lockedStart;
349397
private MemoryPoolIterator2 _lockedEnd;
350398
private int _bufferCount;
@@ -385,7 +433,7 @@ public void DoWriteIfNeeded()
385433
{
386434
_writeReq.Dispose();
387435
var _this = (WriteContext)state;
388-
_this.ReturnFullyWrittenBlocks();
436+
_this.ScheduleReturnFullyWrittenBlocks();
389437
_this.WriteStatus = status;
390438
_this.WriteError = error;
391439
_this.DoShutdownIfNeeded();
@@ -441,20 +489,37 @@ public void Complete()
441489
{
442490
Self.OnWriteCompleted(_byteCount, WriteStatus, WriteError);
443491
}
444-
445-
private void ReturnFullyWrittenBlocks()
492+
493+
private void ScheduleReturnFullyWrittenBlocks()
446494
{
447495
var block = _lockedStart.Block;
448-
while (block != _lockedEnd.Block)
496+
var end = _lockedEnd.Block;
497+
if (block == end)
498+
{
499+
end.Unpin();
500+
return;
501+
}
502+
503+
while (block.Next != end)
504+
{
505+
block = block.Next;
506+
block.Unpin();
507+
}
508+
block.Next = null;
509+
510+
ThreadPool.QueueUserWorkItem(_returnWrittenBlocks, _lockedStart.Block);
511+
}
512+
513+
private static void ReturnWrittenBlocks(MemoryPoolBlock2 block)
514+
{
515+
while (block != null)
449516
{
450517
var returnBlock = block;
451518
block = block.Next;
452519

453520
returnBlock.Unpin();
454521
returnBlock.Pool?.Return(returnBlock);
455522
}
456-
457-
_lockedEnd.Block.Unpin();
458523
}
459524

460525
private void LockWrite()

0 commit comments

Comments
 (0)