LibUv Networking (#65)
This commit is contained in:
parent
ec6629fbe9
commit
5e2be2d7b6
69 changed files with 5612 additions and 586 deletions
612
Projects/Server/LibUv/Internal/LibuvFunctions.cs
Normal file
612
Projects/Server/LibUv/Internal/LibuvFunctions.cs
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Server;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class LibuvFunctions
|
||||
{
|
||||
public LibuvFunctions()
|
||||
{
|
||||
_uv_loop_init = NativeMethods.uv_loop_init;
|
||||
_uv_loop_close = NativeMethods.uv_loop_close;
|
||||
_uv_run = NativeMethods.uv_run;
|
||||
_uv_stop = NativeMethods.uv_stop;
|
||||
_uv_ref = NativeMethods.uv_ref;
|
||||
_uv_unref = NativeMethods.uv_unref;
|
||||
_uv_fileno = NativeMethods.uv_fileno;
|
||||
_uv_close = NativeMethods.uv_close;
|
||||
_uv_async_init = NativeMethods.uv_async_init;
|
||||
_uv_async_send = NativeMethods.uv_async_send;
|
||||
_uv_unsafe_async_send = NativeMethods.uv_unsafe_async_send;
|
||||
_uv_tcp_init = NativeMethods.uv_tcp_init;
|
||||
_uv_tcp_bind = NativeMethods.uv_tcp_bind;
|
||||
_uv_tcp_open = NativeMethods.uv_tcp_open;
|
||||
_uv_tcp_nodelay = NativeMethods.uv_tcp_nodelay;
|
||||
_uv_pipe_init = NativeMethods.uv_pipe_init;
|
||||
_uv_pipe_bind = NativeMethods.uv_pipe_bind;
|
||||
_uv_pipe_open = NativeMethods.uv_pipe_open;
|
||||
_uv_listen = NativeMethods.uv_listen;
|
||||
_uv_accept = NativeMethods.uv_accept;
|
||||
_uv_pipe_connect = NativeMethods.uv_pipe_connect;
|
||||
_uv_pipe_pending_count = NativeMethods.uv_pipe_pending_count;
|
||||
_uv_read_start = NativeMethods.uv_read_start;
|
||||
_uv_read_stop = NativeMethods.uv_read_stop;
|
||||
_uv_try_write = NativeMethods.uv_try_write;
|
||||
unsafe
|
||||
{
|
||||
_uv_write = NativeMethods.uv_write;
|
||||
_uv_write2 = NativeMethods.uv_write2;
|
||||
}
|
||||
_uv_err_name = NativeMethods.uv_err_name;
|
||||
_uv_strerror = NativeMethods.uv_strerror;
|
||||
_uv_loop_size = NativeMethods.uv_loop_size;
|
||||
_uv_handle_size = NativeMethods.uv_handle_size;
|
||||
_uv_req_size = NativeMethods.uv_req_size;
|
||||
_uv_ip4_addr = NativeMethods.uv_ip4_addr;
|
||||
_uv_ip6_addr = NativeMethods.uv_ip6_addr;
|
||||
_uv_tcp_getpeername = NativeMethods.uv_tcp_getpeername;
|
||||
_uv_tcp_getsockname = NativeMethods.uv_tcp_getsockname;
|
||||
_uv_walk = NativeMethods.uv_walk;
|
||||
_uv_timer_init = NativeMethods.uv_timer_init;
|
||||
_uv_timer_start = NativeMethods.uv_timer_start;
|
||||
_uv_timer_stop = NativeMethods.uv_timer_stop;
|
||||
_uv_now = NativeMethods.uv_now;
|
||||
}
|
||||
|
||||
// Second ctor that doesn't set any fields only to be used by MockLibuv
|
||||
public LibuvFunctions(bool onlyForTesting)
|
||||
{
|
||||
}
|
||||
|
||||
public void ThrowIfErrored(int statusCode)
|
||||
{
|
||||
// Note: method is explicitly small so the success case is easily inlined
|
||||
if (statusCode < 0) ThrowError(statusCode);
|
||||
}
|
||||
|
||||
private void ThrowError(int statusCode)
|
||||
{
|
||||
// Note: only has one throw block so it will marked as "Does not return" by the jit
|
||||
// and not inlined into previous function, while also marking as a function
|
||||
// that does not need cpu register prep to call (see: https://github.com/dotnet/coreclr/pull/6103)
|
||||
throw GetError(statusCode);
|
||||
}
|
||||
|
||||
public void Check(int statusCode, out UvException error)
|
||||
{
|
||||
// Note: method is explicitly small so the success case is easily inlined
|
||||
error = statusCode < 0 ? GetError(statusCode) : null;
|
||||
}
|
||||
|
||||
// Note: method marked as NoInlining so it doesn't bloat either of the two preceding functions
|
||||
// Check and ThrowError and alter their jit heuristics.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private UvException GetError(int statusCode) =>
|
||||
new UvException($"Error {statusCode} {err_name(statusCode)} {strerror(statusCode)}", statusCode);
|
||||
|
||||
public Func<UvLoopHandle, int> _uv_loop_init;
|
||||
public void loop_init(UvLoopHandle handle)
|
||||
{
|
||||
ThrowIfErrored(_uv_loop_init(handle));
|
||||
}
|
||||
|
||||
public Func<IntPtr, int> _uv_loop_close;
|
||||
public void loop_close(UvLoopHandle handle)
|
||||
{
|
||||
handle.Validate(true);
|
||||
ThrowIfErrored(_uv_loop_close(handle.InternalGetHandle()));
|
||||
}
|
||||
|
||||
public Func<UvLoopHandle, int, int> _uv_run;
|
||||
public void run(UvLoopHandle handle, int mode)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_run(handle, mode));
|
||||
}
|
||||
|
||||
public Action<UvLoopHandle> _uv_stop;
|
||||
public void stop(UvLoopHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
_uv_stop(handle);
|
||||
}
|
||||
|
||||
public Action<UvHandle> _uv_ref;
|
||||
public void @ref(UvHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
_uv_ref(handle);
|
||||
}
|
||||
|
||||
public Action<UvHandle> _uv_unref;
|
||||
public void unref(UvHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
_uv_unref(handle);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket);
|
||||
public uv_fileno_func _uv_fileno;
|
||||
public void uv_fileno(UvHandle handle, ref IntPtr socket)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_fileno(handle, ref socket));
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_close_cb(IntPtr handle);
|
||||
public Action<IntPtr, uv_close_cb> _uv_close;
|
||||
public void close(UvHandle handle, uv_close_cb close_cb)
|
||||
{
|
||||
handle.Validate(true);
|
||||
_uv_close(handle.InternalGetHandle(), close_cb);
|
||||
}
|
||||
|
||||
public void close(IntPtr handle, uv_close_cb close_cb)
|
||||
{
|
||||
_uv_close(handle, close_cb);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_async_cb(IntPtr handle);
|
||||
public Func<UvLoopHandle, UvAsyncHandle, uv_async_cb, int> _uv_async_init;
|
||||
public void async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb)
|
||||
{
|
||||
loop.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_async_init(loop, handle, cb));
|
||||
}
|
||||
|
||||
public Func<UvAsyncHandle, int> _uv_async_send;
|
||||
public void async_send(UvAsyncHandle handle)
|
||||
{
|
||||
ThrowIfErrored(_uv_async_send(handle));
|
||||
}
|
||||
|
||||
public Func<IntPtr, int> _uv_unsafe_async_send;
|
||||
public void unsafe_async_send(IntPtr handle)
|
||||
{
|
||||
ThrowIfErrored(_uv_unsafe_async_send(handle));
|
||||
}
|
||||
|
||||
public Func<UvLoopHandle, UvTcpHandle, int> _uv_tcp_init;
|
||||
public void tcp_init(UvLoopHandle loop, UvTcpHandle handle)
|
||||
{
|
||||
loop.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_init(loop, handle));
|
||||
}
|
||||
|
||||
public delegate int uv_tcp_bind_func(UvTcpHandle handle, ref SockAddr addr, int flags);
|
||||
public uv_tcp_bind_func _uv_tcp_bind;
|
||||
public void tcp_bind(UvTcpHandle handle, ref SockAddr addr, int flags)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_bind(handle, ref addr, flags));
|
||||
}
|
||||
|
||||
public Func<UvTcpHandle, IntPtr, int> _uv_tcp_open;
|
||||
public void tcp_open(UvTcpHandle handle, IntPtr hSocket)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_open(handle, hSocket));
|
||||
}
|
||||
|
||||
public Func<UvTcpHandle, int, int> _uv_tcp_nodelay;
|
||||
public void tcp_nodelay(UvTcpHandle handle, bool enable)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_nodelay(handle, enable ? 1 : 0));
|
||||
}
|
||||
|
||||
public Func<UvLoopHandle, UvPipeHandle, int, int> _uv_pipe_init;
|
||||
public void pipe_init(UvLoopHandle loop, UvPipeHandle handle, bool ipc)
|
||||
{
|
||||
loop.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_pipe_init(loop, handle, ipc ? -1 : 0));
|
||||
}
|
||||
|
||||
public Func<UvPipeHandle, string, int> _uv_pipe_bind;
|
||||
public void pipe_bind(UvPipeHandle handle, string name)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_pipe_bind(handle, name));
|
||||
}
|
||||
|
||||
public Func<UvPipeHandle, IntPtr, int> _uv_pipe_open;
|
||||
public void pipe_open(UvPipeHandle handle, IntPtr hSocket)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_pipe_open(handle, hSocket));
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_connection_cb(IntPtr server, int status);
|
||||
public Func<UvStreamHandle, int, uv_connection_cb, int> _uv_listen;
|
||||
public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_listen(handle, backlog, cb));
|
||||
}
|
||||
|
||||
public Func<UvStreamHandle, UvStreamHandle, int> _uv_accept;
|
||||
public void accept(UvStreamHandle server, UvStreamHandle client)
|
||||
{
|
||||
server.Validate();
|
||||
client.Validate();
|
||||
ThrowIfErrored(_uv_accept(server, client));
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_connect_cb(IntPtr req, int status);
|
||||
public Action<UvConnectRequest, UvPipeHandle, string, uv_connect_cb> _uv_pipe_connect;
|
||||
public void pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb)
|
||||
{
|
||||
req.Validate();
|
||||
handle.Validate();
|
||||
_uv_pipe_connect(req, handle, name, cb);
|
||||
}
|
||||
|
||||
public Func<UvPipeHandle, int> _uv_pipe_pending_count;
|
||||
public int pipe_pending_count(UvPipeHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
return _uv_pipe_pending_count(handle);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_alloc_cb(IntPtr server, int suggested_size, out uv_buf_t buf);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_read_cb(IntPtr server, int nread, ref uv_buf_t buf);
|
||||
public Func<UvStreamHandle, uv_alloc_cb, uv_read_cb, int> _uv_read_start;
|
||||
public void read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_read_start(handle, alloc_cb, read_cb));
|
||||
}
|
||||
|
||||
public Func<UvStreamHandle, int> _uv_read_stop;
|
||||
public void read_stop(UvStreamHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_read_stop(handle));
|
||||
}
|
||||
|
||||
public Func<UvStreamHandle, uv_buf_t[], int, int> _uv_try_write;
|
||||
public int try_write(UvStreamHandle handle, uv_buf_t[] bufs, int nbufs)
|
||||
{
|
||||
handle.Validate();
|
||||
var count = _uv_try_write(handle, bufs, nbufs);
|
||||
ThrowIfErrored(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_write_cb(IntPtr req, int status);
|
||||
|
||||
public unsafe delegate int uv_write_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb);
|
||||
public uv_write_func _uv_write;
|
||||
public unsafe void write(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb)
|
||||
{
|
||||
req.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_write(req, handle, bufs, nbufs, cb));
|
||||
}
|
||||
|
||||
public unsafe delegate int uv_write2_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb);
|
||||
public uv_write2_func _uv_write2;
|
||||
public unsafe void write2(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb)
|
||||
{
|
||||
req.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_write2(req, handle, bufs, nbufs, sendHandle, cb));
|
||||
}
|
||||
|
||||
public Func<int, IntPtr> _uv_err_name;
|
||||
public string err_name(int err)
|
||||
{
|
||||
IntPtr ptr = _uv_err_name(err);
|
||||
return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr);
|
||||
}
|
||||
|
||||
public Func<int, IntPtr> _uv_strerror;
|
||||
public string strerror(int err)
|
||||
{
|
||||
IntPtr ptr = _uv_strerror(err);
|
||||
return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr);
|
||||
}
|
||||
|
||||
public Func<int> _uv_loop_size;
|
||||
public int loop_size() => _uv_loop_size();
|
||||
|
||||
public Func<HandleType, int> _uv_handle_size;
|
||||
public int handle_size(HandleType handleType) => _uv_handle_size(handleType);
|
||||
|
||||
public Func<RequestType, int> _uv_req_size;
|
||||
public int req_size(RequestType reqType) => _uv_req_size(reqType);
|
||||
|
||||
public delegate int uv_ip4_addr_func(string ip, int port, out SockAddr addr);
|
||||
public uv_ip4_addr_func _uv_ip4_addr;
|
||||
public void ip4_addr(string ip, int port, out SockAddr addr, out UvException error)
|
||||
{
|
||||
Check(_uv_ip4_addr(ip, port, out addr), out error);
|
||||
}
|
||||
|
||||
public delegate int uv_ip6_addr_func(string ip, int port, out SockAddr addr);
|
||||
public uv_ip6_addr_func _uv_ip6_addr;
|
||||
public void ip6_addr(string ip, int port, out SockAddr addr, out UvException error)
|
||||
{
|
||||
Check(_uv_ip6_addr(ip, port, out addr), out error);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_walk_cb(IntPtr handle, IntPtr arg);
|
||||
public Func<UvLoopHandle, uv_walk_cb, IntPtr, int> _uv_walk;
|
||||
public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg)
|
||||
{
|
||||
loop.Validate();
|
||||
_uv_walk(loop, walk_cb, arg);
|
||||
}
|
||||
|
||||
public Func<UvLoopHandle, UvTimerHandle, int> _uv_timer_init;
|
||||
public void timer_init(UvLoopHandle loop, UvTimerHandle handle)
|
||||
{
|
||||
loop.Validate();
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_timer_init(loop, handle));
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_timer_cb(IntPtr handle);
|
||||
public Func<UvTimerHandle, uv_timer_cb, long, long, int> _uv_timer_start;
|
||||
public void timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_timer_start(handle, cb, timeout, repeat));
|
||||
}
|
||||
|
||||
public Func<UvTimerHandle, int> _uv_timer_stop;
|
||||
public void timer_stop(UvTimerHandle handle)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_timer_stop(handle));
|
||||
}
|
||||
|
||||
public Func<UvLoopHandle, long> _uv_now;
|
||||
public long now(UvLoopHandle loop)
|
||||
{
|
||||
loop.Validate();
|
||||
return _uv_now(loop);
|
||||
}
|
||||
|
||||
public delegate int uv_tcp_getsockname_func(UvTcpHandle handle, out SockAddr addr, ref int namelen);
|
||||
public uv_tcp_getsockname_func _uv_tcp_getsockname;
|
||||
public void tcp_getsockname(UvTcpHandle handle, out SockAddr addr, ref int namelen)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_getsockname(handle, out addr, ref namelen));
|
||||
}
|
||||
|
||||
public delegate int uv_tcp_getpeername_func(UvTcpHandle handle, out SockAddr addr, ref int namelen);
|
||||
public uv_tcp_getpeername_func _uv_tcp_getpeername;
|
||||
public void tcp_getpeername(UvTcpHandle handle, out SockAddr addr, ref int namelen)
|
||||
{
|
||||
handle.Validate();
|
||||
ThrowIfErrored(_uv_tcp_getpeername(handle, out addr, ref namelen));
|
||||
}
|
||||
|
||||
public uv_buf_t buf_init(IntPtr memory, int len) => new uv_buf_t(memory, len, Core.IsWindows);
|
||||
|
||||
public struct uv_buf_t
|
||||
{
|
||||
// this type represents a WSABUF struct on Windows
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms741542(v=vs.85).aspx
|
||||
// and an iovec struct on *nix
|
||||
// http://man7.org/linux/man-pages/man2/readv.2.html
|
||||
// because the order of the fields in these structs is different, the field
|
||||
// names in this type don't have meaningful symbolic names. instead, they are
|
||||
// assigned in the correct order by the constructor at runtime
|
||||
|
||||
private readonly IntPtr _field0;
|
||||
private readonly IntPtr _field1;
|
||||
|
||||
public uv_buf_t(IntPtr memory, int len, bool windows)
|
||||
{
|
||||
if (windows)
|
||||
{
|
||||
_field0 = (IntPtr)len;
|
||||
_field1 = memory;
|
||||
}
|
||||
else
|
||||
{
|
||||
_field0 = memory;
|
||||
_field1 = (IntPtr)len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
private static class NativeMethods
|
||||
{
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_loop_init(UvLoopHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_loop_close(IntPtr a0);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_run(UvLoopHandle handle, int mode);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_stop(UvLoopHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_ref(UvHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_unref(UvHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_fileno(UvHandle handle, ref IntPtr socket);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_async_send(UvAsyncHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl, EntryPoint = "uv_async_send")]
|
||||
public static extern int uv_unsafe_async_send(IntPtr handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_init(UvLoopHandle loop, UvTcpHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_bind(UvTcpHandle handle, ref SockAddr addr, int flags);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_open(UvTcpHandle handle, IntPtr hSocket);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_nodelay(UvTcpHandle handle, int enable);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_pipe_init(UvLoopHandle loop, UvPipeHandle handle, int ipc);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_pipe_bind(UvPipeHandle loop, string name);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_pipe_open(UvPipeHandle handle, IntPtr hSocket);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_listen(UvStreamHandle handle, int backlog, uv_connection_cb cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_accept(UvStreamHandle server, UvStreamHandle client);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
public static extern void uv_pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_pipe_pending_count(UvPipeHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_read_stop(UvStreamHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_try_write(UvStreamHandle handle, uv_buf_t[] bufs, int nbufs);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern unsafe int uv_write(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern unsafe int uv_write2(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr uv_err_name(int err);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr uv_strerror(int err);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_loop_size();
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_handle_size(HandleType handleType);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_req_size(RequestType reqType);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_ip4_addr(string ip, int port, out SockAddr addr);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_ip6_addr(string ip, int port, out SockAddr addr);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_getsockname(UvTcpHandle handle, out SockAddr name, ref int namelen);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_tcp_getpeername(UvTcpHandle handle, out SockAddr name, ref int namelen);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_timer_init(UvLoopHandle loop, UvTimerHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_timer_stop(UvTimerHandle handle);
|
||||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern long uv_now(UvLoopHandle loop);
|
||||
|
||||
[DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||
public static extern unsafe int WSAIoctl(
|
||||
IntPtr socket,
|
||||
int dwIoControlCode,
|
||||
int* lpvInBuffer,
|
||||
uint cbInBuffer,
|
||||
int* lpvOutBuffer,
|
||||
int cbOutBuffer,
|
||||
out uint lpcbBytesReturned,
|
||||
IntPtr lpOverlapped,
|
||||
IntPtr lpCompletionRoutine
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||
public static extern int WSAGetLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Projects/Server/LibUv/Internal/SockAddr.cs
Normal file
107
Projects/Server/LibUv/Internal/SockAddr.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// 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.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using Server;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SockAddr
|
||||
{
|
||||
// this type represents native memory occupied by sockaddr struct
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx
|
||||
// although the c/c++ header defines it as a 2-byte short followed by a 14-byte array,
|
||||
// the simplest way to reserve the same size in c# is with four nameless long values
|
||||
private long _field0;
|
||||
private long _field1;
|
||||
private long _field2;
|
||||
private long _field3;
|
||||
|
||||
public SockAddr(long ignored) => _field0 = _field1 = _field2 = _field3 = 0;
|
||||
|
||||
public unsafe IPEndPoint GetIPEndPoint()
|
||||
{
|
||||
// The bytes are represented in network byte order.
|
||||
//
|
||||
// Example 1: [2001:4898:e0:391:b9ef:1124:9d3e:a354]:39179
|
||||
//
|
||||
// 0000 0000 0b99 0017 => The third and fourth bytes 990B is the actual port
|
||||
// 9103 e000 9848 0120 => IPv6 address is represented in the 128bit field1 and field2.
|
||||
// 54a3 3e9d 2411 efb9 Read these two 64-bit long from right to left byte by byte.
|
||||
// 0000 0000 0000 0010 => Scope ID 0x10 (eg [::1%16]) the first 4 bytes of field3 in host byte order.
|
||||
//
|
||||
// Example 2: 10.135.34.141:39178 when adopt dual-stack sockets, IPv4 is mapped to IPv6
|
||||
//
|
||||
// 0000 0000 0a99 0017 => The port representation are the same
|
||||
// 0000 0000 0000 0000
|
||||
// 8d22 870a ffff 0000 => IPv4 occupies the last 32 bit: 0A.87.22.8d is the actual address.
|
||||
// 0000 0000 0000 0000
|
||||
//
|
||||
// Example 3: 10.135.34.141:12804, not dual-stack sockets
|
||||
//
|
||||
// 8d22 870a fd31 0002 => sa_family == AF_INET (02)
|
||||
// 0000 0000 0000 0000
|
||||
// 0000 0000 0000 0000
|
||||
// 0000 0000 0000 0000
|
||||
//
|
||||
// Example 4: 127.0.0.1:52798, on a Mac OS
|
||||
//
|
||||
// 0100 007F 3ECE 0210 => sa_family == AF_INET (02) Note that struct sockaddr on mac use
|
||||
// 0000 0000 0000 0000 the second unint8 field for sa family type
|
||||
// 0000 0000 0000 0000 http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h
|
||||
// 0000 0000 0000 0000
|
||||
//
|
||||
// Reference:
|
||||
// - Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
|
||||
// - Linux: https://github.com/torvalds/linux/blob/6a13feb9c82803e2b815eca72fa7a9f5561d7861/include/linux/socket.h
|
||||
// - Linux (sin6_scope_id): https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/net/sunrpc/addr.c#L82
|
||||
// - Apple: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h
|
||||
|
||||
// Quick calculate the port by mask the field and locate the byte 3 and byte 4
|
||||
// and then shift them to correct place to form a int.
|
||||
var port = ((int)(_field0 & 0x00FF0000) >> 8) | (int)((_field0 & 0xFF000000) >> 24);
|
||||
|
||||
int family = (int)_field0;
|
||||
if (Core.IsDarwin)
|
||||
// see explanation in example 4
|
||||
family >>= 8;
|
||||
family &= 0xFF;
|
||||
|
||||
if (family == 2)
|
||||
// AF_INET => IPv4
|
||||
return new IPEndPoint(new IPAddress((_field0 >> 32) & 0xFFFFFFFF), port);
|
||||
|
||||
if (IsIPv4MappedToIPv6())
|
||||
{
|
||||
var ipv4bits = (_field2 >> 32) & 0x00000000FFFFFFFF;
|
||||
return new IPEndPoint(new IPAddress(ipv4bits), port);
|
||||
}
|
||||
|
||||
// otherwise IPv6
|
||||
var bytes = new byte[16];
|
||||
fixed (byte* b = bytes)
|
||||
{
|
||||
*(long*)b = _field1;
|
||||
*(long*)(b + 8) = _field2;
|
||||
}
|
||||
|
||||
return new IPEndPoint(new IPAddress(bytes, ScopeId), port);
|
||||
}
|
||||
|
||||
public uint ScopeId
|
||||
{
|
||||
get => (uint)_field3;
|
||||
set
|
||||
{
|
||||
_field3 &= unchecked ((long)0xFFFFFFFF00000000);
|
||||
_field3 |= value;
|
||||
}
|
||||
}
|
||||
|
||||
// If the IPAddress is an IPv4 mapped to IPv6, return the IPv4 representation instead.
|
||||
// For example [::FFFF:127.0.0.1] will be transform to IPAddress of 127.0.0.1
|
||||
private bool IsIPv4MappedToIPv6() => _field1 == 0 && (_field2 & 0xFFFFFFFF) == 0xFFFF0000;
|
||||
}
|
||||
}
|
||||
72
Projects/Server/LibUv/Internal/UvAsyncHandle.cs
Normal file
72
Projects/Server/LibUv/Internal/UvAsyncHandle.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvAsyncHandle : UvHandle
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_close_cb _destroyMemory = handle => DestroyMemory(handle);
|
||||
|
||||
private static readonly LibuvFunctions.uv_async_cb _uv_async_cb = handle => AsyncCb(handle);
|
||||
private Action _callback;
|
||||
private Action<Action<IntPtr>, IntPtr> _queueCloseHandle;
|
||||
|
||||
public UvAsyncHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(UvLoopHandle loop, Action callback, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||
{
|
||||
CreateMemory(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
loop.Libuv.handle_size(LibuvFunctions.HandleType.ASYNC));
|
||||
|
||||
_callback = callback;
|
||||
_queueCloseHandle = queueCloseHandle;
|
||||
_uv.async_init(loop, this, _uv_async_cb);
|
||||
}
|
||||
|
||||
public void Send()
|
||||
{
|
||||
_uv.async_send(this);
|
||||
}
|
||||
|
||||
private static void AsyncCb(IntPtr handle)
|
||||
{
|
||||
FromIntPtr<UvAsyncHandle>(handle)._callback.Invoke();
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
var memory = handle;
|
||||
if (memory != IntPtr.Zero)
|
||||
{
|
||||
handle = IntPtr.Zero;
|
||||
|
||||
if (Thread.CurrentThread.ManagedThreadId == ThreadId)
|
||||
{
|
||||
_uv.close(memory, _destroyMemory);
|
||||
}
|
||||
else if (_queueCloseHandle != null)
|
||||
{
|
||||
// This can be called from the finalizer.
|
||||
// Ensure the closure doesn't reference "this".
|
||||
LibuvFunctions uv = _uv;
|
||||
_queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory);
|
||||
uv.unsafe_async_send(memory);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "UvAsyncHandle not initialized with queueCloseHandle action");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Projects/Server/LibUv/Internal/UvConnectRequest.cs
Normal file
75
Projects/Server/LibUv/Internal/UvConnectRequest.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// 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 Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for UvWriteRequest
|
||||
/// </summary>
|
||||
public class UvConnectRequest : UvRequest
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = UvConnectCb;
|
||||
|
||||
private Action<UvConnectRequest, int, UvException, object> _callback;
|
||||
private object _state;
|
||||
|
||||
public UvConnectRequest(ILibuvTrace logger) : base (logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init(LibuvThread thread)
|
||||
{
|
||||
DangerousInit(thread.Loop);
|
||||
|
||||
base.Init(thread);
|
||||
}
|
||||
|
||||
public void DangerousInit(UvLoopHandle loop)
|
||||
{
|
||||
var requestSize = loop.Libuv.req_size(LibuvFunctions.RequestType.CONNECT);
|
||||
CreateMemory(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
requestSize);
|
||||
}
|
||||
|
||||
public void Connect(
|
||||
UvPipeHandle pipe,
|
||||
string name,
|
||||
Action<UvConnectRequest, int, UvException, object> callback,
|
||||
object state)
|
||||
{
|
||||
_callback = callback;
|
||||
_state = state;
|
||||
|
||||
Libuv.pipe_connect(this, pipe, name, _uv_connect_cb);
|
||||
}
|
||||
|
||||
private static void UvConnectCb(IntPtr ptr, int status)
|
||||
{
|
||||
var req = FromIntPtr<UvConnectRequest>(ptr);
|
||||
|
||||
var callback = req._callback;
|
||||
req._callback = null;
|
||||
|
||||
var state = req._state;
|
||||
req._state = null;
|
||||
|
||||
UvException error = null;
|
||||
if (status < 0) req.Libuv.Check(status, out error);
|
||||
|
||||
try
|
||||
{
|
||||
callback(req, status, error, state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
req._log.LogError(0, ex, "UvConnectRequest");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Projects/Server/LibUv/Internal/UvException.cs
Normal file
14
Projects/Server/LibUv/Internal/UvException.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// 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;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvException : Exception
|
||||
{
|
||||
public UvException(string message, int statusCode) : base(message) => StatusCode = statusCode;
|
||||
|
||||
public int StatusCode { get; }
|
||||
}
|
||||
}
|
||||
66
Projects/Server/LibUv/Internal/UvHandle.cs
Normal file
66
Projects/Server/LibUv/Internal/UvHandle.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public abstract class UvHandle : UvMemory
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_close_cb _destroyMemory = DestroyMemory;
|
||||
private Action<Action<IntPtr>, IntPtr> _queueCloseHandle;
|
||||
|
||||
protected UvHandle(ILibuvTrace logger) : base (logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected void CreateHandle(
|
||||
LibuvFunctions uv,
|
||||
int threadId,
|
||||
int size,
|
||||
Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||
{
|
||||
_queueCloseHandle = queueCloseHandle;
|
||||
CreateMemory(uv, threadId, size);
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
IntPtr memory = handle;
|
||||
if (memory != IntPtr.Zero)
|
||||
{
|
||||
handle = IntPtr.Zero;
|
||||
|
||||
if (Thread.CurrentThread.ManagedThreadId == ThreadId)
|
||||
{
|
||||
_uv.close(memory, _destroyMemory);
|
||||
}
|
||||
else if (_queueCloseHandle != null)
|
||||
{
|
||||
// This can be called from the finalizer.
|
||||
// Ensure the closure doesn't reference "this".
|
||||
LibuvFunctions uv = _uv;
|
||||
_queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "UvHandle not initialized with queueCloseHandle action");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reference()
|
||||
{
|
||||
_uv.@ref(this);
|
||||
}
|
||||
|
||||
public void Unreference()
|
||||
{
|
||||
_uv.unref(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Projects/Server/LibUv/Internal/UvLoopHandle.cs
Normal file
54
Projects/Server/LibUv/Internal/UvLoopHandle.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// 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.Threading;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvLoopHandle : UvMemory
|
||||
{
|
||||
public UvLoopHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(LibuvFunctions uv)
|
||||
{
|
||||
CreateMemory(
|
||||
uv,
|
||||
Thread.CurrentThread.ManagedThreadId,
|
||||
uv.loop_size());
|
||||
|
||||
_uv.loop_init(this);
|
||||
}
|
||||
|
||||
public void Run(int mode = 0)
|
||||
{
|
||||
_uv.run(this, mode);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_uv.stop(this);
|
||||
}
|
||||
|
||||
public long Now() => _uv.now(this);
|
||||
|
||||
protected override unsafe bool ReleaseHandle()
|
||||
{
|
||||
var memory = handle;
|
||||
if (memory != IntPtr.Zero)
|
||||
{
|
||||
// loop_close clears the gcHandlePtr
|
||||
var gcHandlePtr = *(IntPtr*)memory;
|
||||
|
||||
_uv.loop_close(this);
|
||||
handle = IntPtr.Zero;
|
||||
|
||||
DestroyMemory(memory, gcHandlePtr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Projects/Server/LibUv/Internal/UvMemory.cs
Normal file
79
Projects/Server/LibUv/Internal/UvMemory.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// 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.
|
||||
#define TRACE
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for UvMemory
|
||||
/// </summary>
|
||||
public abstract class UvMemory : SafeHandle
|
||||
{
|
||||
protected LibuvFunctions _uv;
|
||||
protected int _threadId;
|
||||
protected readonly ILibuvTrace _log;
|
||||
private readonly GCHandleType _handleType;
|
||||
|
||||
protected UvMemory(ILibuvTrace logger, GCHandleType handleType = GCHandleType.Weak) : base(IntPtr.Zero, true)
|
||||
{
|
||||
_log = logger;
|
||||
_handleType = handleType;
|
||||
}
|
||||
|
||||
public LibuvFunctions Libuv => _uv;
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
public int ThreadId
|
||||
{
|
||||
get => _threadId;
|
||||
private set => _threadId = value;
|
||||
}
|
||||
|
||||
protected unsafe void CreateMemory(LibuvFunctions uv, int threadId, int size)
|
||||
{
|
||||
_uv = uv;
|
||||
ThreadId = threadId;
|
||||
|
||||
handle = Marshal.AllocCoTaskMem(size);
|
||||
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this, _handleType));
|
||||
}
|
||||
|
||||
protected static unsafe void DestroyMemory(IntPtr memory)
|
||||
{
|
||||
var gcHandlePtr = *(IntPtr*)memory;
|
||||
DestroyMemory(memory, gcHandlePtr);
|
||||
}
|
||||
|
||||
protected static void DestroyMemory(IntPtr memory, IntPtr gcHandlePtr)
|
||||
{
|
||||
if (gcHandlePtr != IntPtr.Zero)
|
||||
{
|
||||
var gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
|
||||
gcHandle.Free();
|
||||
}
|
||||
Marshal.FreeCoTaskMem(memory);
|
||||
}
|
||||
|
||||
public IntPtr InternalGetHandle() => handle;
|
||||
|
||||
public void Validate(bool closed = false)
|
||||
{
|
||||
Debug.Assert(closed || !IsClosed, "Handle is closed");
|
||||
Debug.Assert(!IsInvalid, "Handle is invalid");
|
||||
|
||||
Debug.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect");
|
||||
}
|
||||
|
||||
public static unsafe THandle FromIntPtr<THandle>(IntPtr handle)
|
||||
{
|
||||
GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle);
|
||||
return (THandle)gcHandle.Target;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Projects/Server/LibUv/Internal/UvPipeHandle.cs
Normal file
36
Projects/Server/LibUv/Internal/UvPipeHandle.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// 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;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvPipeHandle : UvStreamHandle
|
||||
{
|
||||
public UvPipeHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle, bool ipc = false)
|
||||
{
|
||||
CreateHandle(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
loop.Libuv.handle_size(LibuvFunctions.HandleType.NAMED_PIPE), queueCloseHandle);
|
||||
|
||||
_uv.pipe_init(loop, this, ipc);
|
||||
}
|
||||
|
||||
public void Open(IntPtr fileDescriptor)
|
||||
{
|
||||
_uv.pipe_open(this, fileDescriptor);
|
||||
}
|
||||
|
||||
public void Bind(string name)
|
||||
{
|
||||
_uv.pipe_bind(this, name);
|
||||
}
|
||||
|
||||
public int PendingCount() => _uv.pipe_pending_count(this);
|
||||
}
|
||||
}
|
||||
31
Projects/Server/LibUv/Internal/UvRequest.cs
Normal file
31
Projects/Server/LibUv/Internal/UvRequest.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvRequest : UvMemory
|
||||
{
|
||||
protected UvRequest(ILibuvTrace logger) : base(logger, GCHandleType.Normal)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Init(LibuvThread thread)
|
||||
{
|
||||
#if DEBUG
|
||||
// Store weak handles to all UvRequest objects so we can do leak detection
|
||||
// while running tests
|
||||
thread.Requests.Add(new WeakReference(this));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
DestroyMemory(handle);
|
||||
handle = IntPtr.Zero;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
151
Projects/Server/LibUv/Internal/UvStreamHandle.cs
Normal file
151
Projects/Server/LibUv/Internal/UvStreamHandle.cs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public abstract class UvStreamHandle : UvHandle
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_connection_cb _uv_connection_cb = UvConnectionCb;
|
||||
// Ref and out lamda params must be explicitly typed
|
||||
private static readonly LibuvFunctions.uv_alloc_cb _uv_alloc_cb =
|
||||
(IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf) => UvAllocCb(handle, suggested_size, out buf);
|
||||
private static readonly LibuvFunctions.uv_read_cb _uv_read_cb =
|
||||
(IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf) => UvReadCb(handle, status, ref buf);
|
||||
|
||||
private Action<UvStreamHandle, int, UvException, object> _listenCallback;
|
||||
private object _listenState;
|
||||
private GCHandle _listenVitality;
|
||||
|
||||
private Func<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> _allocCallback;
|
||||
private Action<UvStreamHandle, int, object> _readCallback;
|
||||
private object _readState;
|
||||
private GCHandle _readVitality;
|
||||
|
||||
protected UvStreamHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (_listenVitality.IsAllocated) _listenVitality.Free();
|
||||
if (_readVitality.IsAllocated) _readVitality.Free();
|
||||
return base.ReleaseHandle();
|
||||
}
|
||||
|
||||
public void Listen(int backlog, Action<UvStreamHandle, int, UvException, object> callback, object state)
|
||||
{
|
||||
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, backlog, _uv_connection_cb);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_listenCallback = null;
|
||||
_listenState = null;
|
||||
if (_listenVitality.IsAllocated) _listenVitality.Free();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(UvStreamHandle handle)
|
||||
{
|
||||
_uv.accept(this, handle);
|
||||
}
|
||||
|
||||
public void ReadStart(
|
||||
Func<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> allocCallback,
|
||||
Action<UvStreamHandle, int, object> readCallback,
|
||||
object state)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// UvStreamHandle.ReadStop() should be idempotent to match uv_read_stop()
|
||||
public void ReadStop()
|
||||
{
|
||||
if (_readVitality.IsAllocated) _readVitality.Free();
|
||||
_allocCallback = null;
|
||||
_readCallback = null;
|
||||
_readState = null;
|
||||
_uv.read_stop(this);
|
||||
}
|
||||
|
||||
public int TryWrite(LibuvFunctions.uv_buf_t buf)
|
||||
{
|
||||
return _uv.try_write(this, new[] { buf }, 1);
|
||||
}
|
||||
|
||||
private static void UvConnectionCb(IntPtr handle, int status)
|
||||
{
|
||||
var stream = FromIntPtr<UvStreamHandle>(handle);
|
||||
|
||||
stream.Libuv.Check(status, out var error);
|
||||
|
||||
try
|
||||
{
|
||||
stream._listenCallback(stream, status, error, stream._listenState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stream._log.LogError(0, ex, "UvConnectionCb");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UvAllocCb(IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf)
|
||||
{
|
||||
var stream = FromIntPtr<UvStreamHandle>(handle);
|
||||
try
|
||||
{
|
||||
buf = stream._allocCallback(stream, suggested_size, stream._readState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stream._log.LogError(0, ex, "UvAllocCb");
|
||||
buf = stream.Libuv.buf_init(IntPtr.Zero, 0);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UvReadCb(IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf)
|
||||
{
|
||||
var stream = FromIntPtr<UvStreamHandle>(handle);
|
||||
|
||||
try
|
||||
{
|
||||
stream._readCallback(stream, status, stream._readState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stream._log.LogError(0, ex, "UbReadCb");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
72
Projects/Server/LibUv/Internal/UvTcpHandle.cs
Normal file
72
Projects/Server/LibUv/Internal/UvTcpHandle.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// 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.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvTcpHandle : UvStreamHandle
|
||||
{
|
||||
public UvTcpHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||
{
|
||||
CreateHandle(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
loop.Libuv.handle_size(LibuvFunctions.HandleType.TCP), queueCloseHandle);
|
||||
|
||||
_uv.tcp_init(loop, this);
|
||||
}
|
||||
|
||||
public void Open(IntPtr fileDescriptor)
|
||||
{
|
||||
_uv.tcp_open(this, fileDescriptor);
|
||||
}
|
||||
|
||||
public void Bind(IPEndPoint endPoint)
|
||||
{
|
||||
var addressText = endPoint.Address.ToString();
|
||||
|
||||
_uv.ip4_addr(addressText, endPoint.Port, out var addr, out var error1);
|
||||
|
||||
if (error1 != null)
|
||||
{
|
||||
_uv.ip6_addr(addressText, endPoint.Port, out addr, out var error2);
|
||||
if (error2 != null) throw error1;
|
||||
|
||||
if (endPoint.Address.ScopeId != addr.ScopeId)
|
||||
// IPAddress.ScopeId cannot be less than 0 or greater than 0xFFFFFFFF
|
||||
// https://msdn.microsoft.com/en-us/library/system.net.ipaddress.scopeid(v=vs.110).aspx
|
||||
addr.ScopeId = (uint)endPoint.Address.ScopeId;
|
||||
}
|
||||
|
||||
_uv.tcp_bind(this, ref addr, 0);
|
||||
}
|
||||
|
||||
public IPEndPoint GetPeerIPEndPoint()
|
||||
{
|
||||
int namelen = Marshal.SizeOf<SockAddr>();
|
||||
_uv.tcp_getpeername(this, out var socketAddress, ref namelen);
|
||||
|
||||
return socketAddress.GetIPEndPoint();
|
||||
}
|
||||
|
||||
public IPEndPoint GetSockIPEndPoint()
|
||||
{
|
||||
int namelen = Marshal.SizeOf<SockAddr>();
|
||||
_uv.tcp_getsockname(this, out var socketAddress, ref namelen);
|
||||
|
||||
return socketAddress.GetIPEndPoint();
|
||||
}
|
||||
|
||||
public void NoDelay(bool enable)
|
||||
{
|
||||
_uv.tcp_nodelay(this, enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Projects/Server/LibUv/Internal/UvTimerHandle.cs
Normal file
56
Projects/Server/LibUv/Internal/UvTimerHandle.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// 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 Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
public class UvTimerHandle : UvHandle
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_timer_cb _uv_timer_cb = UvTimerCb;
|
||||
|
||||
private Action<UvTimerHandle> _callback;
|
||||
|
||||
public UvTimerHandle(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
|
||||
{
|
||||
CreateHandle(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
loop.Libuv.handle_size(LibuvFunctions.HandleType.TIMER),
|
||||
queueCloseHandle);
|
||||
|
||||
_uv.timer_init(loop, this);
|
||||
}
|
||||
|
||||
public void Start(Action<UvTimerHandle> callback, long timeout, long repeat)
|
||||
{
|
||||
_callback = callback;
|
||||
_uv.timer_start(this, _uv_timer_cb, timeout, repeat);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_uv.timer_stop(this);
|
||||
}
|
||||
|
||||
private static void UvTimerCb(IntPtr handle)
|
||||
{
|
||||
var timer = FromIntPtr<UvTimerHandle>(handle);
|
||||
|
||||
try
|
||||
{
|
||||
timer._callback(timer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
timer._log.LogError(0, ex, nameof(UvTimerCb));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
237
Projects/Server/LibUv/Internal/UvWriteReq.cs
Normal file
237
Projects/Server/LibUv/Internal/UvWriteReq.cs
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
// 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.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Libuv.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for UvWriteRequest
|
||||
/// </summary>
|
||||
public class UvWriteReq : UvRequest
|
||||
{
|
||||
private static readonly LibuvFunctions.uv_write_cb _uv_write_cb = (ptr, status) => UvWriteCb(ptr, status);
|
||||
|
||||
private IntPtr _bufs;
|
||||
|
||||
private Action<UvWriteReq, int, UvException, object> _callback;
|
||||
private object _state;
|
||||
private const int BUFFER_COUNT = 4;
|
||||
|
||||
private LibuvAwaitable<UvWriteReq> _awaitable = new LibuvAwaitable<UvWriteReq>();
|
||||
private List<GCHandle> _pins = new List<GCHandle>(BUFFER_COUNT + 1);
|
||||
private List<MemoryHandle> _handles = new List<MemoryHandle>(BUFFER_COUNT + 1);
|
||||
|
||||
public UvWriteReq(ILibuvTrace logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init(LibuvThread thread)
|
||||
{
|
||||
DangerousInit(thread.Loop);
|
||||
|
||||
base.Init(thread);
|
||||
}
|
||||
|
||||
public void DangerousInit(UvLoopHandle loop)
|
||||
{
|
||||
var requestSize = loop.Libuv.req_size(LibuvFunctions.RequestType.WRITE);
|
||||
var bufferSize = Marshal.SizeOf<LibuvFunctions.uv_buf_t>() * BUFFER_COUNT;
|
||||
CreateMemory(
|
||||
loop.Libuv,
|
||||
loop.ThreadId,
|
||||
requestSize + bufferSize);
|
||||
_bufs = handle + requestSize;
|
||||
}
|
||||
|
||||
public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, in ReadOnlySequence<byte> buffer)
|
||||
{
|
||||
Write(handle, buffer, LibuvAwaitable<UvWriteReq>.Callback, _awaitable);
|
||||
return _awaitable;
|
||||
}
|
||||
|
||||
public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, ArraySegment<ArraySegment<byte>> bufs)
|
||||
{
|
||||
Write(handle, bufs, LibuvAwaitable<UvWriteReq>.Callback, _awaitable);
|
||||
return _awaitable;
|
||||
}
|
||||
|
||||
private unsafe void Write(
|
||||
UvStreamHandle handle,
|
||||
in ReadOnlySequence<byte> buffer,
|
||||
Action<UvWriteReq, int, UvException, object> callback,
|
||||
object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var nBuffers = 0;
|
||||
if (buffer.IsSingleSegment)
|
||||
nBuffers = 1;
|
||||
else
|
||||
foreach (var _ in buffer)
|
||||
nBuffers++;
|
||||
|
||||
var pBuffers = (LibuvFunctions.uv_buf_t*)_bufs;
|
||||
if (nBuffers > BUFFER_COUNT)
|
||||
{
|
||||
// create and pin buffer array when it's larger than the pre-allocated one
|
||||
var bufArray = new LibuvFunctions.uv_buf_t[nBuffers];
|
||||
var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned);
|
||||
_pins.Add(gcHandle);
|
||||
pBuffers = (LibuvFunctions.uv_buf_t*)gcHandle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
if (nBuffers == 1)
|
||||
{
|
||||
var memory = buffer.First;
|
||||
var memoryHandle = memory.Pin();
|
||||
_handles.Add(memoryHandle);
|
||||
|
||||
// Fast path for single buffer
|
||||
pBuffers[0] = Libuv.buf_init(
|
||||
(IntPtr)memoryHandle.Pointer,
|
||||
memory.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var memory in buffer)
|
||||
{
|
||||
// This won't actually pin the buffer since we're already using pinned memory
|
||||
var memoryHandle = memory.Pin();
|
||||
_handles.Add(memoryHandle);
|
||||
|
||||
// create and pin each segment being written
|
||||
pBuffers[index] = Libuv.buf_init(
|
||||
(IntPtr)memoryHandle.Pointer,
|
||||
memory.Length);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
_callback = callback;
|
||||
_state = state;
|
||||
_uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_callback = null;
|
||||
_state = null;
|
||||
UnpinGcHandles();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void Write(
|
||||
UvStreamHandle handle,
|
||||
ArraySegment<ArraySegment<byte>> bufs,
|
||||
Action<UvWriteReq, int, UvException, object> callback,
|
||||
object state)
|
||||
{
|
||||
WriteArraySegmentInternal(handle, bufs, null, callback, state);
|
||||
}
|
||||
|
||||
public void Write2(
|
||||
UvStreamHandle handle,
|
||||
ArraySegment<ArraySegment<byte>> bufs,
|
||||
UvStreamHandle sendHandle,
|
||||
Action<UvWriteReq, int, UvException, object> callback,
|
||||
object state)
|
||||
{
|
||||
WriteArraySegmentInternal(handle, bufs, sendHandle, callback, state);
|
||||
}
|
||||
|
||||
private unsafe void WriteArraySegmentInternal(
|
||||
UvStreamHandle handle,
|
||||
ArraySegment<ArraySegment<byte>> bufs,
|
||||
UvStreamHandle sendHandle,
|
||||
Action<UvWriteReq, int, UvException, object> callback,
|
||||
object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pBuffers = (LibuvFunctions.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 LibuvFunctions.uv_buf_t[nBuffers];
|
||||
var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned);
|
||||
_pins.Add(gcHandle);
|
||||
pBuffers = (LibuvFunctions.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;
|
||||
|
||||
if (sendHandle == null)
|
||||
_uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb);
|
||||
else
|
||||
_uv.write2(this, handle, pBuffers, nBuffers, sendHandle, _uv_write_cb);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_callback = null;
|
||||
_state = null;
|
||||
UnpinGcHandles();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Safe handle has instance method called Unpin
|
||||
// so using UnpinGcHandles to avoid conflict
|
||||
private void UnpinGcHandles()
|
||||
{
|
||||
var pinList = _pins;
|
||||
var count = pinList.Count;
|
||||
for (var i = 0; i < count; i++) pinList[i].Free();
|
||||
pinList.Clear();
|
||||
|
||||
var handleList = _handles;
|
||||
count = handleList.Count;
|
||||
for (var i = 0; i < count; i++) handleList[i].Dispose();
|
||||
handleList.Clear();
|
||||
}
|
||||
|
||||
private static void UvWriteCb(IntPtr ptr, int status)
|
||||
{
|
||||
var req = FromIntPtr<UvWriteReq>(ptr);
|
||||
req.UnpinGcHandles();
|
||||
|
||||
var callback = req._callback;
|
||||
req._callback = null;
|
||||
|
||||
var state = req._state;
|
||||
req._state = null;
|
||||
|
||||
UvException error = null;
|
||||
if (status < 0) req.Libuv.Check(status, out error);
|
||||
|
||||
try
|
||||
{
|
||||
callback(req, status, error, state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
req._log.LogError(0, ex, "UvWriteCb");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue