diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index c43d13dcba..9b6e9fa52c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -53,11 +53,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http SocketOutput _self; ArraySegment _buffer; - Action _drained; UvStreamHandle _socket; Action _callback; object _state; - GCHandle _pin; internal void Contextualize( SocketOutput socketOutput, @@ -87,8 +85,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { KestrelTrace.Log.ConnectionWriteCallback(0, status); //NOTE: pool this? + + var callback = _callback; + _callback = null; + var state = _state; + _state = null; + Dispose(); - _callback(error, _state); + callback(error, state); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvStreamHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvStreamHandle.cs index ecde6e08e3..e9f9279f16 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvStreamHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvStreamHandle.cs @@ -37,10 +37,27 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public void Listen(int backlog, Action callback, object state) { - _listenCallback = callback; - _listenState = state; - _listenVitality = GCHandle.Alloc(this, GCHandleType.Normal); - _uv.listen(this, 10, _uv_connection_cb); + if (_listenVitality.IsAllocated) + { + throw new InvalidOperationException("TODO: Listen may not be called more than once"); + } + try + { + _listenCallback = callback; + _listenState = state; + _listenVitality = GCHandle.Alloc(this, GCHandleType.Normal); + _uv.listen(this, 10, _uv_connection_cb); + } + catch + { + _listenCallback = null; + _listenState = null; + if (_listenVitality.IsAllocated) + { + _listenVitality.Free(); + } + throw; + } } public void Accept(UvStreamHandle handle) @@ -53,15 +70,37 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking Action readCallback, object state) { - _allocCallback = allocCallback; - _readCallback = readCallback; - _readState = state; - _readVitality = GCHandle.Alloc(this, GCHandleType.Normal); - _uv.read_start(this, _uv_alloc_cb, _uv_read_cb); + if (_readVitality.IsAllocated) + { + throw new InvalidOperationException("TODO: ReadStop must be called before ReadStart may be called again"); + } + try + { + _allocCallback = allocCallback; + _readCallback = readCallback; + _readState = state; + _readVitality = GCHandle.Alloc(this, GCHandleType.Normal); + _uv.read_start(this, _uv_alloc_cb, _uv_read_cb); + } + catch + { + _allocCallback = null; + _readCallback = null; + _readState = null; + if (_readVitality.IsAllocated) + { + _readVitality.Free(); + } + throw; + } } public void ReadStop() { + if (!_readVitality.IsAllocated) + { + throw new InvalidOperationException("TODO: ReadStart must be called before ReadStop may be called"); + } _allocCallback = null; _readCallback = null; _readState = null; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs index 60228c4eca..dc58e5688f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs @@ -40,45 +40,60 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking Action callback, 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 nBuffers = bufs.Count; - if (nBuffers > BUFFER_COUNT) + try { - // create and pin buffer array when it's larger than the pre-allocated one - var bufArray = new Libuv.uv_buf_t[nBuffers]; - var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); - _pins.Add(gcHandle); - pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject(); - } + // add GCHandle to keeps this SafeHandle alive while request processing + _pins.Add(GCHandle.Alloc(this, GCHandleType.Normal)); - for (var index = 0; index != nBuffers; ++index) + var pBuffers = (Libuv.uv_buf_t*)_bufs; + var nBuffers = bufs.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 gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); + _pins.Add(gcHandle); + pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject(); + } + + for (var index = 0; index != nBuffers; ++index) + { + // create and pin each segment being written + var buf = bufs.Array[bufs.Offset + index]; + + var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned); + _pins.Add(gcHandle); + pBuffers[index] = Libuv.buf_init( + gcHandle.AddrOfPinnedObject() + buf.Offset, + buf.Count); + } + + _callback = callback; + _state = state; + _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); + } + catch { - // create and pin each segment being written - var buf = bufs.Array[bufs.Offset + index]; - - var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned); - _pins.Add(gcHandle); - pBuffers[index] = Libuv.buf_init( - gcHandle.AddrOfPinnedObject() + buf.Offset, - buf.Count); + _callback = null; + _state = null; + Unpin(this); + throw; } - - _callback = callback; - _state = state; - _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); } - private static void UvWriteCb(IntPtr ptr, int status) + private static void Unpin(UvWriteReq req) { - var req = FromIntPtr(ptr); foreach (var pin in req._pins) { pin.Free(); } req._pins.Clear(); + } + + private static void UvWriteCb(IntPtr ptr, int status) + { + var req = FromIntPtr(ptr); + Unpin(req); var callback = req._callback; req._callback = null;