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

Commit

Permalink
Fix memory leak caused by closure allocations in KestrelThread (#1264).
Browse files Browse the repository at this point in the history
  • Loading branch information
Cesar Blum Silveira committed Jan 25, 2017
1 parent ba3976a commit e2379b1
Showing 1 changed file with 30 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -19,10 +20,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
/// </summary>
public class KestrelThread
{
public const long HeartbeatMilliseconds = 1000;
public const long HeartbeatMilliseconds = 10;

private static readonly Action<object, object> _postCallbackAdapter = (callback, state) => ((Action<object>)callback).Invoke(state);
private static readonly Action<object, object> _postAsyncCallbackAdapter = (callback, state) => ((Action<object>)callback).Invoke(state);
private static readonly Libuv.uv_walk_cb _heartbeatWalkCallback = (ptr, arg) =>
{
var streamHandle = UvMemory.FromIntPtr<UvHandle>(ptr) as UvStreamHandle;
var thisHandle = GCHandle.FromIntPtr(arg);
var kestrelThread = (KestrelThread)thisHandle.Target;
streamHandle?.Connection?.Tick(kestrelThread.Now);
};

// maximum times the work queues swapped and are processed in a single pass
// as completing a task may immediately have write data to put on the network
Expand All @@ -48,6 +56,7 @@ public class KestrelThread
private readonly IKestrelTrace _log;
private readonly IThreadPool _threadPool;
private readonly TimeSpan _shutdownTimeout;
private IntPtr _thisPtr;

public KestrelThread(KestrelEngine engine)
{
Expand Down Expand Up @@ -94,6 +103,9 @@ internal KestrelThread(KestrelEngine engine, int maxLoops)

private Action<Action<IntPtr>, IntPtr> QueueCloseAsyncHandle { get; }

// The cached result of Loop.Now() which is a timestamp in milliseconds
private long Now { get; set; }

public Task StartAsync()
{
var tcs = new TaskCompletionSource<int>();
Expand Down Expand Up @@ -245,14 +257,17 @@ public Task PostAsync(Action<object> callback, object state)
}

public void Walk(Action<IntPtr> callback)
{
Walk((ptr, arg) => callback(ptr), IntPtr.Zero);
}

private void Walk(Libuv.uv_walk_cb callback, IntPtr arg)
{
_engine.Libuv.walk(
_loop,
(ptr, arg) =>
{
callback(ptr);
},
IntPtr.Zero);
callback,
arg
);
}

private void PostCloseHandle(Action<IntPtr> callback, IntPtr handle)
Expand All @@ -273,7 +288,7 @@ private void ThreadStart(object parameter)
{
lock (_startSync)
{
var tcs = (TaskCompletionSource<int>) parameter;
var tcs = (TaskCompletionSource<int>)parameter;
try
{
_loop.Init(_engine.Libuv);
Expand All @@ -290,8 +305,13 @@ private void ThreadStart(object parameter)
}
}

// This is used to access a 64-bit timestamp (this.Now) using a potentially 32-bit IntPtr.
var thisHandle = GCHandle.Alloc(this, GCHandleType.Weak);

try
{
_thisPtr = GCHandle.ToIntPtr(thisHandle);

_loop.Run();
if (_stopImmediate)
{
Expand All @@ -318,6 +338,7 @@ private void ThreadStart(object parameter)
}
finally
{
thisHandle.Free();
_threadTcs.SetResult(null);
}
}
Expand All @@ -336,13 +357,8 @@ private void OnPost()

private void OnHeartbeat(UvTimerHandle timer)
{
var now = Loop.Now();

Walk(ptr =>
{
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
(handle as UvStreamHandle)?.Connection?.Tick(now);
});
Now = Loop.Now();
Walk(_heartbeatWalkCallback, _thisPtr);
}

private bool DoPostWork()
Expand Down

0 comments on commit e2379b1

Please sign in to comment.