diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs index b326d101ad..18e9478f86 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs @@ -10,20 +10,20 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking { public class Libuv { - private IntPtr _module = IntPtr.Zero; - - [DllImport("kernel32")] - public static extern IntPtr LoadLibrary(string dllToLoad); - - [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); - - [DllImport("kernel32")] - public static extern bool FreeLibrary(IntPtr hModule); + public Libuv() + { + IsWindows = PlatformApis.IsWindows(); + } + public bool IsWindows; + public Func LoadLibrary; + public Func FreeLibrary; + public Func GetProcAddress; public void Load(string dllToLoad) { + PlatformApis.Apply(this); + var module = LoadLibrary(dllToLoad); foreach (var field in GetType().GetTypeInfo().DeclaredFields) { @@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public void loop_close(UvLoopHandle handle) { handle.Validate(closed: true); - Check(_uv_loop_close(handle.DangerousGetHandle())); + Check(_uv_loop_close(handle.InternalGetHandle())); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -126,7 +126,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public void close(UvHandle handle, uv_close_cb close_cb) { handle.Validate(closed: true); - _uv_close(handle.DangerousGetHandle(), close_cb); + _uv_close(handle.InternalGetHandle(), close_cb); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -223,9 +223,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_write_cb(IntPtr req, int status); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate int uv_write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, uv_write_cb cb); + unsafe delegate int uv_write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb); uv_write _uv_write; - public void write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, uv_write_cb cb) + unsafe public void write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb) { req.Validate(); handle.Validate(); @@ -244,14 +244,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking Check(_uv_shutdown(req, handle, cb)); } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate int uv_handle_size(int handleType); - uv_handle_size _uv_handle_size; - public int handle_size(int handleType) - { - return _uv_handle_size(handleType); - } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate IntPtr uv_err_name(int err); uv_err_name _uv_err_name; @@ -270,13 +262,28 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate int uv_loop_size(); + uv_loop_size _uv_loop_size; + public int loop_size() + { + return _uv_loop_size(); + } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate int uv_req_size(int handleType); - uv_req_size _uv_req_size; - public int req_size(int handleType) + delegate int uv_handle_size(HandleType handleType); + uv_handle_size _uv_handle_size; + public int handle_size(HandleType handleType) { - return _uv_req_size(handleType); + return _uv_handle_size(handleType); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate int uv_req_size(RequestType reqType); + uv_req_size _uv_req_size; + public int req_size(RequestType reqType) + { + return _uv_req_size(reqType); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -297,20 +304,84 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking return Check(_uv_ip6_addr(ip, port, out addr), out error); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_walk_cb(IntPtr handle, IntPtr arg); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + unsafe delegate int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg); + uv_walk _uv_walk; + unsafe public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg) + { + loop.Validate(); + _uv_walk(loop, walk_cb, arg); + } + + public uv_buf_t buf_init(IntPtr memory, int len) + { + return new uv_buf_t(memory, len, IsWindows); + } + public struct sockaddr { - long w; - long x; - long y; - long z; + long x0; + long x1; + long x2; + long x3; } public struct uv_buf_t { - public uint len; - public IntPtr memory; + public uv_buf_t(IntPtr memory, int len, bool IsWindows) + { + if (IsWindows) + { + x0 = (IntPtr)len; + x1 = memory; + } + else + { + x0 = memory; + x1 = (IntPtr)len; + } + } + + public IntPtr x0; + public IntPtr x1; } + public enum HandleType + { + Unknown = 0, + ASYNC, + CHECK, + FS_EVENT, + FS_POLL, + HANDLE, + IDLE, + NAMED_PIPE, + POLL, + PREPARE, + PROCESS, + STREAM, + TCP, + TIMER, + TTY, + UDP, + SIGNAL, + } + + public enum RequestType + { + Unknown = 0, + REQ, + CONNECT, + WRITE, + SHUTDOWN, + UDP_SEND, + FS, + WORK, + GETADDRINFO, + GETNAMEINFO, + } //int handle_size_async; //int handle_size_tcp; //int req_size_write; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/PlatformApis.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/PlatformApis.cs new file mode 100644 index 0000000000..e55e16577e --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/PlatformApis.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.AspNet.Server.Kestrel.Networking +{ + public static class PlatformApis + { + public static bool IsWindows() + { + var p = (int)Environment.OSVersion.Platform; + return (p != 4) && (p != 6) && (p != 128); + } + + public static void Apply(Libuv libuv) + { + if (libuv.IsWindows) + { + WindowsApis.Apply(libuv); + } + else + { + LinuxApis.Apply(libuv); + } + } + + public static class WindowsApis + { + [DllImport("kernel32")] + public static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32")] + public static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); + + public static void Apply(Libuv libuv) + { + libuv.LoadLibrary = LoadLibrary; + libuv.FreeLibrary = FreeLibrary; + libuv.GetProcAddress = GetProcAddress; + } + } + + public static class LinuxApis + { + [DllImport("libdl")] + public static extern IntPtr dlopen(String fileName, int flags); + + [DllImport("libdl")] + public static extern IntPtr dlsym(IntPtr handle, String symbol); + + [DllImport("libdl")] + public static extern int dlclose(IntPtr handle); + + [DllImport("libdl")] + public static extern IntPtr dlerror(); + + public static IntPtr LoadLibrary(string dllToLoad) + { + return dlopen(dllToLoad, 2); + } + + public static bool FreeLibrary(IntPtr hModule) + { + return dlclose(hModule) == 0; + } + + public static IntPtr GetProcAddress(IntPtr hModule, string procedureName) + { + dlerror(); + var res = dlsym(hModule, procedureName); + var errPtr = dlerror(); + return errPtr == IntPtr.Zero ? res : IntPtr.Zero; + } + + public static void Apply(Libuv libuv) + { + libuv.LoadLibrary = LoadLibrary; + libuv.FreeLibrary = FreeLibrary; + libuv.GetProcAddress = GetProcAddress; + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UcAsyncHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UcAsyncHandle.cs index e30cbe63ba..7709ebd50c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UcAsyncHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UcAsyncHandle.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public void Init(UvLoopHandle loop, Action callback) { - CreateHandle(loop, 256); + CreateHandle(loop, loop.Libuv.handle_size(Libuv.HandleType.ASYNC)); _callback = callback; _uv.async_init(loop, this, _uv_async_cb); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvLoopHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvLoopHandle.cs index ec87e7db56..9ea5cb3d8a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvLoopHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvLoopHandle.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking { public void Init(Libuv uv) { - CreateHandle(uv, 256); + CreateHandle(uv, uv.loop_size()); _uv.loop_init(this); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvMemory.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvMemory.cs index 661445c2e3..6000c82ebb 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvMemory.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvMemory.cs @@ -45,6 +45,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking _threadId = loop._threadId; } + internal IntPtr InternalGetHandle() + { + return handle; + } public void Validate(bool closed = false) { @@ -53,7 +57,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking Trace.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is correct"); } - unsafe protected static void DestroyHandle(IntPtr memory) { var gcHandlePtr = *(IntPtr*)memory; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvShutdownReq.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvShutdownReq.cs index 05496b3a06..2606285aa0 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvShutdownReq.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvShutdownReq.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public void Init(UvLoopHandle loop) { - CreateHandle(loop, loop.Libuv.req_size(3)); + CreateHandle(loop, loop.Libuv.req_size(Libuv.RequestType.SHUTDOWN)); } public void Shutdown(UvStreamHandle handle, Action callback, object state) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs index 187656647f..96d5ddbbc9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking { public void Init(UvLoopHandle loop) { - CreateHandle(loop, 256); + CreateHandle(loop, loop.Libuv.handle_size(Libuv.HandleType.TCP)); _uv.tcp_init(loop, this); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs index 54d69a3580..f44865e121 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteRequest.cs @@ -2,6 +2,7 @@ // 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.Runtime.InteropServices; namespace Microsoft.AspNet.Server.Kestrel.Networking @@ -13,24 +14,56 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking { 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) { - CreateHandle(loop, loop.Libuv.req_size(2)); + var requestSize = loop.Libuv.req_size(Libuv.RequestType.WRITE); + var bufferSize = Marshal.SizeOf(typeof(Libuv.uv_buf_t)) * BUFFER_COUNT; + CreateHandle(loop, requestSize + bufferSize); + _bufs = handle + requestSize; } - public void Write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, Action callback, object state) + public unsafe void Write(UvStreamHandle handle, ArraySegment> bufs, Action callback, object state) { + var pBuffers = (Libuv.uv_buf_t*)_bufs; + var nBuffers = bufs.Count; + if (nBuffers > BUFFER_COUNT) + { + 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) + { + 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, bufs, nbufs, _uv_write_cb); + _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); } private static void UvWriteCb(IntPtr ptr, int status) { var req = FromIntPtr(ptr); + foreach(var pin in req._pins) + { + pin.Free(); + } + req._pins.Clear(); req._callback(req, status, req._state); req._callback = null; req._state = null;