Garbage collecting safe handles need to queue the uv_close
All of the uv_* calls must be called on the original thread the finalizer thread cleaning up safehandle classes needs special handling see #16
This commit is contained in:
parent
836be5565a
commit
2da561cb7a
|
|
@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ListenSocket = new UvTcpHandle();
|
ListenSocket = new UvTcpHandle();
|
||||||
ListenSocket.Init(Thread.Loop);
|
ListenSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
|
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
|
||||||
ListenSocket.Listen(10, _connectionCallback, this);
|
ListenSocket.Listen(10, _connectionCallback, this);
|
||||||
tcs.SetResult(0);
|
tcs.SetResult(0);
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
private void OnConnection(UvStreamHandle listenSocket, int status)
|
private void OnConnection(UvStreamHandle listenSocket, int status)
|
||||||
{
|
{
|
||||||
var acceptSocket = new UvTcpHandle();
|
var acceptSocket = new UvTcpHandle();
|
||||||
acceptSocket.Init(Thread.Loop);
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
listenSocket.Accept(acceptSocket);
|
listenSocket.Accept(acceptSocket);
|
||||||
|
|
||||||
var connection = new Connection(this, acceptSocket);
|
var connection = new Connection(this, acceptSocket);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using Microsoft.AspNet.Server.Kestrel.Networking;
|
using Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -21,6 +22,8 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
UvAsyncHandle _post;
|
UvAsyncHandle _post;
|
||||||
Queue<Work> _workAdding = new Queue<Work>();
|
Queue<Work> _workAdding = new Queue<Work>();
|
||||||
Queue<Work> _workRunning = new Queue<Work>();
|
Queue<Work> _workRunning = new Queue<Work>();
|
||||||
|
Queue<CloseHandle> _closeHandleAdding = new Queue<CloseHandle>();
|
||||||
|
Queue<CloseHandle> _closeHandleRunning = new Queue<CloseHandle>();
|
||||||
object _workSync = new Object();
|
object _workSync = new Object();
|
||||||
bool _stopImmediate = false;
|
bool _stopImmediate = false;
|
||||||
private ExceptionDispatchInfo _closeError;
|
private ExceptionDispatchInfo _closeError;
|
||||||
|
|
@ -31,10 +34,13 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
_loop = new UvLoopHandle();
|
_loop = new UvLoopHandle();
|
||||||
_post = new UvAsyncHandle();
|
_post = new UvAsyncHandle();
|
||||||
_thread = new Thread(ThreadStart);
|
_thread = new Thread(ThreadStart);
|
||||||
|
QueueCloseHandle = PostCloseHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UvLoopHandle Loop { get { return _loop; } }
|
public UvLoopHandle Loop { get { return _loop; } }
|
||||||
|
|
||||||
|
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; internal set; }
|
||||||
|
|
||||||
public Task StartAsync()
|
public Task StartAsync()
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<int>();
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
|
@ -92,6 +98,15 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PostCloseHandle(Action<IntPtr> callback, IntPtr handle)
|
||||||
|
{
|
||||||
|
lock (_workSync)
|
||||||
|
{
|
||||||
|
_closeHandleAdding.Enqueue(new CloseHandle { Callback = callback, Handle = handle });
|
||||||
|
}
|
||||||
|
_post.Send();
|
||||||
|
}
|
||||||
|
|
||||||
private void ThreadStart(object parameter)
|
private void ThreadStart(object parameter)
|
||||||
{
|
{
|
||||||
var tcs = (TaskCompletionSource<int>)parameter;
|
var tcs = (TaskCompletionSource<int>)parameter;
|
||||||
|
|
@ -119,7 +134,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
_post.Reference();
|
_post.Reference();
|
||||||
_post.DangerousClose();
|
_post.DangerousClose();
|
||||||
_engine.Libuv.walk(
|
_engine.Libuv.walk(
|
||||||
_loop,
|
_loop,
|
||||||
(ptr, arg) =>
|
(ptr, arg) =>
|
||||||
{
|
{
|
||||||
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
|
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
|
||||||
|
|
@ -138,12 +153,19 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
|
|
||||||
private void OnPost()
|
private void OnPost()
|
||||||
{
|
{
|
||||||
var queue = _workAdding;
|
DoPostWork();
|
||||||
|
DoPostCloseHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoPostWork()
|
||||||
|
{
|
||||||
|
Queue<Work> queue;
|
||||||
lock (_workSync)
|
lock (_workSync)
|
||||||
{
|
{
|
||||||
|
queue = _workAdding;
|
||||||
_workAdding = _workRunning;
|
_workAdding = _workRunning;
|
||||||
|
_workRunning = queue;
|
||||||
}
|
}
|
||||||
_workRunning = queue;
|
|
||||||
while (queue.Count != 0)
|
while (queue.Count != 0)
|
||||||
{
|
{
|
||||||
var work = queue.Dequeue();
|
var work = queue.Dequeue();
|
||||||
|
|
@ -156,7 +178,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
tcs =>
|
tcs =>
|
||||||
{
|
{
|
||||||
((TaskCompletionSource<int>)tcs).SetResult(0);
|
((TaskCompletionSource<int>)tcs).SetResult(0);
|
||||||
},
|
},
|
||||||
work.Completion);
|
work.Completion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,11 +190,33 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: unobserved exception?
|
Trace.WriteLine("KestrelThread.DoPostWork " + ex.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void DoPostCloseHandle()
|
||||||
|
{
|
||||||
|
Queue<CloseHandle> queue;
|
||||||
|
lock (_workSync)
|
||||||
|
{
|
||||||
|
queue = _closeHandleAdding;
|
||||||
|
_closeHandleAdding = _closeHandleRunning;
|
||||||
|
_closeHandleRunning = queue;
|
||||||
|
}
|
||||||
|
while (queue.Count != 0)
|
||||||
|
{
|
||||||
|
var closeHandle = queue.Dequeue();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
closeHandle.Callback(closeHandle.Handle);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.WriteLine("KestrelThread.DoPostCloseHandle " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct Work
|
private struct Work
|
||||||
{
|
{
|
||||||
|
|
@ -180,5 +224,10 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
public object State;
|
public object State;
|
||||||
public TaskCompletionSource<int> Completion;
|
public TaskCompletionSource<int> Completion;
|
||||||
}
|
}
|
||||||
|
private struct CloseHandle
|
||||||
|
{
|
||||||
|
public Action<IntPtr> Callback;
|
||||||
|
public IntPtr Handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
handle.Validate(closed: true);
|
handle.Validate(closed: true);
|
||||||
_uv_close(handle.InternalGetHandle(), close_cb);
|
_uv_close(handle.InternalGetHandle(), close_cb);
|
||||||
}
|
}
|
||||||
|
public void close(IntPtr handle, uv_close_cb close_cb)
|
||||||
|
{
|
||||||
|
_uv_close(handle, close_cb);
|
||||||
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
public delegate void uv_async_cb(IntPtr handle);
|
public delegate void uv_async_cb(IntPtr handle);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
|
|
||||||
public void Init(UvLoopHandle loop, Action callback)
|
public void Init(UvLoopHandle loop, Action callback)
|
||||||
{
|
{
|
||||||
CreateHandle(loop, loop.Libuv.handle_size(Libuv.HandleType.ASYNC));
|
CreateMemory(
|
||||||
|
loop.Libuv,
|
||||||
|
loop.ThreadId,
|
||||||
|
loop.Libuv.handle_size(Libuv.HandleType.ASYNC));
|
||||||
|
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
_uv.async_init(loop, this, _uv_async_cb);
|
_uv.async_init(loop, this, _uv_async_cb);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,40 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Networking
|
namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
public abstract class UvHandle : UvMemory
|
public abstract class UvHandle : UvMemory
|
||||||
{
|
{
|
||||||
static Libuv.uv_close_cb _close_cb = DestroyHandle;
|
static Libuv.uv_close_cb _destroyMemory = DestroyMemory;
|
||||||
|
Action<Action<IntPtr>, IntPtr> _queueCloseHandle;
|
||||||
|
|
||||||
|
unsafe protected void CreateHandle(
|
||||||
|
Libuv uv,
|
||||||
|
int threadId,
|
||||||
|
int size,
|
||||||
|
Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||||
|
{
|
||||||
|
_queueCloseHandle = queueCloseHandle;
|
||||||
|
CreateMemory(uv, threadId, size);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool ReleaseHandle()
|
protected override bool ReleaseHandle()
|
||||||
{
|
{
|
||||||
var memory = handle;
|
var memory = handle;
|
||||||
if (memory != IntPtr.Zero)
|
if (memory != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
_uv.close(this, _close_cb);
|
|
||||||
handle = IntPtr.Zero;
|
handle = IntPtr.Zero;
|
||||||
|
|
||||||
|
if (Thread.CurrentThread.ManagedThreadId == ThreadId)
|
||||||
|
{
|
||||||
|
_uv.close(memory, _destroyMemory);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_queueCloseHandle(memory2 => _uv.close(memory2, _destroyMemory), memory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Networking
|
namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
|
|
@ -9,7 +10,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
public void Init(Libuv uv)
|
public void Init(Libuv uv)
|
||||||
{
|
{
|
||||||
CreateHandle(uv, uv.loop_size());
|
CreateMemory(
|
||||||
|
uv,
|
||||||
|
Thread.CurrentThread.ManagedThreadId,
|
||||||
|
uv.loop_size());
|
||||||
|
|
||||||
_uv.loop_init(this);
|
_uv.loop_init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +35,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
_uv.loop_close(this);
|
_uv.loop_close(this);
|
||||||
handle = IntPtr.Zero;
|
handle = IntPtr.Zero;
|
||||||
DestroyHandle(memory);
|
DestroyMemory(memory);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
public abstract class UvMemory : SafeHandle
|
public abstract class UvMemory : SafeHandle
|
||||||
{
|
{
|
||||||
protected Libuv _uv;
|
protected Libuv _uv;
|
||||||
int _threadId;
|
private int _threadId;
|
||||||
|
|
||||||
public UvMemory() : base(IntPtr.Zero, true)
|
public UvMemory() : base(IntPtr.Zero, true)
|
||||||
{
|
{
|
||||||
|
|
@ -30,19 +30,36 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe protected void CreateHandle(Libuv uv, int size)
|
public int ThreadId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _threadId;
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_threadId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe protected void CreateMemory(Libuv uv, int threadId, int size)
|
||||||
{
|
{
|
||||||
_uv = uv;
|
_uv = uv;
|
||||||
_threadId = Thread.CurrentThread.ManagedThreadId;
|
ThreadId = threadId;
|
||||||
|
|
||||||
handle = Marshal.AllocCoTaskMem(size);
|
handle = Marshal.AllocCoTaskMem(size);
|
||||||
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Weak));
|
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Weak));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void CreateHandle(UvLoopHandle loop, int size)
|
unsafe protected static void DestroyMemory(IntPtr memory)
|
||||||
{
|
{
|
||||||
CreateHandle(loop._uv, size);
|
var gcHandlePtr = *(IntPtr*)memory;
|
||||||
_threadId = loop._threadId;
|
if (gcHandlePtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
var gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
|
||||||
|
gcHandle.Free();
|
||||||
|
}
|
||||||
|
Marshal.FreeCoTaskMem(memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IntPtr InternalGetHandle()
|
internal IntPtr InternalGetHandle()
|
||||||
|
|
@ -57,16 +74,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
Trace.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect");
|
Trace.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe protected static void DestroyHandle(IntPtr memory)
|
|
||||||
{
|
|
||||||
var gcHandlePtr = *(IntPtr*)memory;
|
|
||||||
if (gcHandlePtr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
GCHandle.FromIntPtr(gcHandlePtr).Free();
|
|
||||||
}
|
|
||||||
Marshal.FreeCoTaskMem(memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe public static THandle FromIntPtr<THandle>(IntPtr handle)
|
unsafe public static THandle FromIntPtr<THandle>(IntPtr handle)
|
||||||
{
|
{
|
||||||
GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle);
|
GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
|
|
||||||
public void Init(UvLoopHandle loop)
|
public void Init(UvLoopHandle loop)
|
||||||
{
|
{
|
||||||
CreateHandle(loop, loop.Libuv.req_size(Libuv.RequestType.SHUTDOWN));
|
CreateMemory(
|
||||||
|
loop.Libuv,
|
||||||
|
loop.ThreadId,
|
||||||
|
loop.Libuv.req_size(Libuv.RequestType.SHUTDOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, object> callback, object state)
|
public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, object> callback, object state)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,21 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
public void Init(UvLoopHandle loop)
|
public void Init(UvLoopHandle loop)
|
||||||
{
|
{
|
||||||
CreateHandle(loop, loop.Libuv.handle_size(Libuv.HandleType.TCP));
|
CreateMemory(
|
||||||
|
loop.Libuv,
|
||||||
|
loop.ThreadId,
|
||||||
|
loop.Libuv.handle_size(Libuv.HandleType.TCP));
|
||||||
|
|
||||||
|
_uv.tcp_init(loop, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||||
|
{
|
||||||
|
CreateHandle(
|
||||||
|
loop.Libuv,
|
||||||
|
loop.ThreadId,
|
||||||
|
loop.Libuv.handle_size(Libuv.HandleType.TCP), queueCloseHandle);
|
||||||
|
|
||||||
_uv.tcp_init(loop, this);
|
_uv.tcp_init(loop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
var requestSize = loop.Libuv.req_size(Libuv.RequestType.WRITE);
|
var requestSize = loop.Libuv.req_size(Libuv.RequestType.WRITE);
|
||||||
var bufferSize = Marshal.SizeOf(typeof(Libuv.uv_buf_t)) * BUFFER_COUNT;
|
var bufferSize = Marshal.SizeOf(typeof(Libuv.uv_buf_t)) * BUFFER_COUNT;
|
||||||
CreateHandle(loop, requestSize + bufferSize);
|
CreateMemory(
|
||||||
|
loop.Libuv,
|
||||||
|
loop.ThreadId,
|
||||||
|
requestSize + bufferSize);
|
||||||
_bufs = handle + requestSize;
|
_bufs = handle + requestSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,17 +40,23 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
Action<UvWriteReq, int, Exception, object> callback,
|
Action<UvWriteReq, int, Exception, object> callback,
|
||||||
object state)
|
object state)
|
||||||
{
|
{
|
||||||
|
// add GCHandle to keeps this SafeHandle alive while request processing
|
||||||
|
_pins.Add(GCHandle.Alloc(this, GCHandleType.Normal));
|
||||||
|
|
||||||
var pBuffers = (Libuv.uv_buf_t*)_bufs;
|
var pBuffers = (Libuv.uv_buf_t*)_bufs;
|
||||||
var nBuffers = bufs.Count;
|
var nBuffers = bufs.Count;
|
||||||
if (nBuffers > BUFFER_COUNT)
|
if (nBuffers > BUFFER_COUNT)
|
||||||
{
|
{
|
||||||
|
// create and pin buffer array when it's larger than the pre-allocated one
|
||||||
var bufArray = new Libuv.uv_buf_t[nBuffers];
|
var bufArray = new Libuv.uv_buf_t[nBuffers];
|
||||||
var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned);
|
var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned);
|
||||||
_pins.Add(gcHandle);
|
_pins.Add(gcHandle);
|
||||||
pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject();
|
pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var index = 0; index != nBuffers; ++index)
|
for (var index = 0; index != nBuffers; ++index)
|
||||||
{
|
{
|
||||||
|
// create and pin each segment being written
|
||||||
var buf = bufs.Array[bufs.Offset + index];
|
var buf = bufs.Array[bufs.Offset + index];
|
||||||
|
|
||||||
var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned);
|
var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned);
|
||||||
|
|
@ -56,6 +65,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
gcHandle.AddrOfPinnedObject() + buf.Offset,
|
gcHandle.AddrOfPinnedObject() + buf.Offset,
|
||||||
buf.Count);
|
buf.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
_state = state;
|
_state = state;
|
||||||
_uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb);
|
_uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb);
|
||||||
|
|
@ -97,7 +107,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
protected override bool ReleaseHandle()
|
protected override bool ReleaseHandle()
|
||||||
{
|
{
|
||||||
DestroyHandle(handle);
|
DestroyMemory(handle);
|
||||||
handle = IntPtr.Zero;
|
handle = IntPtr.Zero;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue