Cross-platform LoadLibrary, adjusting libuv apis

This commit is contained in:
Louis DeJardin 2014-06-27 19:45:31 -07:00
parent 08a3685f53
commit 9c29ccdd32
8 changed files with 237 additions and 40 deletions

View File

@ -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<string, IntPtr> LoadLibrary;
public Func<IntPtr, bool> FreeLibrary;
public Func<IntPtr, string, IntPtr> 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;

View File

@ -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;
}
}
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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<UvShutdownReq, int, object> callback, object state)

View File

@ -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);
}

View File

@ -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<UvWriteReq, int, object> _callback;
object _state;
const int BUFFER_COUNT = 4;
List<GCHandle> _pins = new List<GCHandle>();
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<UvWriteReq, int, object> callback, object state)
public unsafe void Write(UvStreamHandle handle, ArraySegment<ArraySegment<byte>> bufs, Action<UvWriteReq, int, object> 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<UvWriteReq>(ptr);
foreach(var pin in req._pins)
{
pin.Free();
}
req._pins.Clear();
req._callback(req, status, req._state);
req._callback = null;
req._state = null;