// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; namespace Microsoft.AspNet.Server.Kestrel.Networking { /// /// Summary description for UvWriteRequest /// public class UvWriteReq : UvRequest { private readonly static Libuv.uv_write_cb _uv_write_cb = UvWriteCb; IntPtr _bufs; Action _callback; object _state; const int BUFFER_COUNT = 4; List _pins = new List(); public void Init(UvLoopHandle loop) { var requestSize = loop.Libuv.req_size(Libuv.RequestType.WRITE); var bufferSize = Marshal.SizeOf() * BUFFER_COUNT; CreateMemory( loop.Libuv, loop.ThreadId, requestSize + bufferSize); _bufs = handle + requestSize; } public unsafe void Write( UvStreamHandle handle, ArraySegment> bufs, Action callback, object state) { try { // 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) { // 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 { _callback = null; _state = null; Unpin(this); throw; } } public unsafe void Write2( UvStreamHandle handle, ArraySegment> bufs, UvStreamHandle sendHandle, Action callback, object state) { try { // 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) { // 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.write2(this, handle, pBuffers, nBuffers, sendHandle, _uv_write_cb); } catch { _callback = null; _state = null; Unpin(this); throw; } } private static void Unpin(UvWriteReq req) { 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; var state = req._state; req._state = null; Exception error = null; if (status < 0) { req.Libuv.Check(status, out error); } try { callback(req, status, error, state); } catch (Exception ex) { Trace.WriteLine("UvWriteCb " + ex.ToString()); } } } }