Using named pipes to dispatch connections to multiple threads
This commit is contained in:
parent
ceeb4edabd
commit
42246fd51b
|
|
@ -38,7 +38,7 @@ namespace Kestrel
|
||||||
var disposables = new List<IDisposable>();
|
var disposables = new List<IDisposable>();
|
||||||
var information = (ServerInformation)serverInformation;
|
var information = (ServerInformation)serverInformation;
|
||||||
var engine = new KestrelEngine(_libraryManager, _appShutdownService);
|
var engine = new KestrelEngine(_libraryManager, _appShutdownService);
|
||||||
engine.Start(1);
|
engine.Start(Environment.ProcessorCount);
|
||||||
foreach (var address in information.Addresses)
|
foreach (var address in information.Addresses)
|
||||||
{
|
{
|
||||||
disposables.Add(engine.CreateServer(
|
disposables.Add(engine.CreateServer(
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
ListenSocket = new UvTcpHandle();
|
ListenSocket = new UvTcpHandle();
|
||||||
ListenSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
ListenSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
|
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
|
||||||
ListenSocket.Listen(10, _connectionCallback, this);
|
ListenSocket.Listen(128, _connectionCallback, this);
|
||||||
tcs.SetResult(0);
|
tcs.SetResult(0);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -64,13 +64,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnection(UvStreamHandle listenSocket, int status)
|
protected void OnConnection(UvStreamHandle listenSocket, int status)
|
||||||
{
|
{
|
||||||
var acceptSocket = new UvTcpHandle();
|
var acceptSocket = new UvTcpHandle();
|
||||||
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
listenSocket.Accept(acceptSocket);
|
listenSocket.Accept(acceptSocket);
|
||||||
|
|
||||||
var connection = new Connection(this, acceptSocket);
|
DispatchConnection(acceptSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DispatchConnection(UvTcpHandle socket)
|
||||||
|
{
|
||||||
|
var connection = new Connection(this, socket);
|
||||||
connection.Start();
|
connection.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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 Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
{
|
||||||
|
public class ListenerPrimary : Listener
|
||||||
|
{
|
||||||
|
UvPipeHandle ListenPipe { get; set; }
|
||||||
|
|
||||||
|
List<UvPipeHandle> _dispatchPipes = new List<UvPipeHandle>();
|
||||||
|
int _dispatchIndex;
|
||||||
|
ArraySegment<ArraySegment<byte>> _1234 = new ArraySegment<ArraySegment<byte>>(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) });
|
||||||
|
|
||||||
|
public ListenerPrimary(IMemoryPool memory) : base(memory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(
|
||||||
|
string pipeName,
|
||||||
|
string scheme,
|
||||||
|
string host,
|
||||||
|
int port,
|
||||||
|
KestrelThread thread,
|
||||||
|
Func<Frame, Task> application)
|
||||||
|
{
|
||||||
|
await StartAsync(scheme, host, port, thread, application);
|
||||||
|
|
||||||
|
await Thread.PostAsync(_ =>
|
||||||
|
{
|
||||||
|
ListenPipe = new UvPipeHandle();
|
||||||
|
ListenPipe.Init(Thread.Loop, false);
|
||||||
|
ListenPipe.Bind(pipeName);
|
||||||
|
ListenPipe.Listen(128, OnListenPipe, null);
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnListenPipe(UvStreamHandle pipe, int status, Exception error, object state)
|
||||||
|
{
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dispatchPipe = new UvPipeHandle();
|
||||||
|
dispatchPipe.Init(Thread.Loop, true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pipe.Accept(dispatchPipe);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
dispatchPipe.Dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_dispatchPipes.Add(dispatchPipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DispatchConnection(UvTcpHandle socket)
|
||||||
|
{
|
||||||
|
var index = _dispatchIndex++ % (_dispatchPipes.Count + 1);
|
||||||
|
if (index == _dispatchPipes.Count)
|
||||||
|
{
|
||||||
|
base.DispatchConnection(socket);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dispatchPipe = _dispatchPipes[index];
|
||||||
|
var write = new UvWriteReq();
|
||||||
|
write.Init(Thread.Loop);
|
||||||
|
write.Write2(
|
||||||
|
dispatchPipe,
|
||||||
|
_1234,
|
||||||
|
socket,
|
||||||
|
(write2, status, error, state) =>
|
||||||
|
{
|
||||||
|
write.Dispose();
|
||||||
|
((UvTcpHandle)state).Dispose();
|
||||||
|
},
|
||||||
|
socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
// 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 Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
{
|
||||||
|
public class ListenerSecondary : ListenerContext, IDisposable
|
||||||
|
{
|
||||||
|
UvPipeHandle DispatchPipe { get; set; }
|
||||||
|
|
||||||
|
public ListenerSecondary(IMemoryPool memory)
|
||||||
|
{
|
||||||
|
Memory = memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(
|
||||||
|
string pipeName,
|
||||||
|
KestrelThread thread,
|
||||||
|
Func<Frame, Task> application)
|
||||||
|
{
|
||||||
|
Thread = thread;
|
||||||
|
Application = application;
|
||||||
|
|
||||||
|
DispatchPipe = new UvPipeHandle();
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
Thread.Post(_ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DispatchPipe.Init(Thread.Loop, true);
|
||||||
|
var connect = new UvConnectRequest();
|
||||||
|
connect.Init(Thread.Loop);
|
||||||
|
connect.Connect(
|
||||||
|
DispatchPipe,
|
||||||
|
pipeName,
|
||||||
|
(connect2, status, error, state) =>
|
||||||
|
{
|
||||||
|
connect.Dispose();
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
tcs.SetException(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ptr = Marshal.AllocHGlobal(16);
|
||||||
|
var buf = Thread.Loop.Libuv.buf_init(ptr, 16);
|
||||||
|
|
||||||
|
DispatchPipe.ReadStart(
|
||||||
|
(_1, _2, _3) => buf,
|
||||||
|
(_1, status2, error2, state2) =>
|
||||||
|
{
|
||||||
|
if (status2 <= 0)
|
||||||
|
{
|
||||||
|
DispatchPipe.Dispose();
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var acceptSocket = new UvTcpHandle();
|
||||||
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
|
DispatchPipe.Accept(acceptSocket);
|
||||||
|
|
||||||
|
var connection = new Connection(this, acceptSocket);
|
||||||
|
connection.Start();
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
|
||||||
|
tcs.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DispatchPipe.Dispose();
|
||||||
|
tcs.SetException(ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DispatchPipe.Dispose();
|
||||||
|
tcs.SetException(ex);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Ensure the event loop is still running.
|
||||||
|
// If the event loop isn't running and we try to wait on this Post
|
||||||
|
// to complete, then KestrelEngine will never be disposed and
|
||||||
|
// the exception that stopped the event loop will never be surfaced.
|
||||||
|
if (Thread.FatalError == null)
|
||||||
|
{
|
||||||
|
Thread.Send(_ => DispatchPipe.Dispose(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -118,6 +118,18 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Send(Action<object> callback, object state)
|
||||||
|
{
|
||||||
|
if (_loop.ThreadId == Thread.CurrentThread.ManagedThreadId)
|
||||||
|
{
|
||||||
|
callback.Invoke(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PostAsync(callback, state).Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void PostCloseHandle(Action<IntPtr> callback, IntPtr handle)
|
private void PostCloseHandle(Action<IntPtr> callback, IntPtr handle)
|
||||||
{
|
{
|
||||||
lock (_workSync)
|
lock (_workSync)
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
: "amd64";
|
: "amd64";
|
||||||
|
|
||||||
libraryPath = Path.Combine(
|
libraryPath = Path.Combine(
|
||||||
libraryPath,
|
libraryPath,
|
||||||
"native",
|
"native",
|
||||||
"windows",
|
"windows",
|
||||||
architecture,
|
architecture,
|
||||||
"libuv.dll");
|
"libuv.dll");
|
||||||
}
|
}
|
||||||
else if (Libuv.IsDarwin)
|
else if (Libuv.IsDarwin)
|
||||||
|
|
@ -91,16 +91,37 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
|
|
||||||
public IDisposable CreateServer(string scheme, string host, int port, Func<Frame, Task> application)
|
public IDisposable CreateServer(string scheme, string host, int port, Func<Frame, Task> application)
|
||||||
{
|
{
|
||||||
var listeners = new List<Listener>();
|
var listeners = new List<IDisposable>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var pipeName = (Libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n");
|
||||||
|
|
||||||
|
var single = Threads.Count == 1;
|
||||||
|
var first = true;
|
||||||
|
|
||||||
foreach (var thread in Threads)
|
foreach (var thread in Threads)
|
||||||
{
|
{
|
||||||
var listener = new Listener(Memory);
|
if (single)
|
||||||
|
{
|
||||||
|
var listener = new Listener(Memory);
|
||||||
|
listeners.Add(listener);
|
||||||
|
listener.StartAsync(scheme, host, port, thread, application).Wait();
|
||||||
|
}
|
||||||
|
else if (first)
|
||||||
|
{
|
||||||
|
var listener = new ListenerPrimary(Memory);
|
||||||
|
listeners.Add(listener);
|
||||||
|
listener.StartAsync(pipeName, scheme, host, port, thread, application).Wait();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var listener = new ListenerSecondary(Memory);
|
||||||
|
listeners.Add(listener);
|
||||||
|
listener.StartAsync(pipeName, thread, application).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
listeners.Add(listener);
|
first = false;
|
||||||
listener.StartAsync(scheme, host, port, thread, application).Wait();
|
|
||||||
}
|
}
|
||||||
return new Disposable(() =>
|
return new Disposable(() =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
_listenCallback = callback;
|
_listenCallback = callback;
|
||||||
_listenState = state;
|
_listenState = state;
|
||||||
_listenVitality = GCHandle.Alloc(this, GCHandleType.Normal);
|
_listenVitality = GCHandle.Alloc(this, GCHandleType.Normal);
|
||||||
_uv.listen(this, 10, _uv_connection_cb);
|
_uv.listen(this, backlog, _uv_connection_cb);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,54 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public unsafe void Write2(
|
||||||
|
UvStreamHandle handle,
|
||||||
|
ArraySegment<ArraySegment<byte>> bufs,
|
||||||
|
UvStreamHandle sendHandle,
|
||||||
|
Action<UvWriteReq, int, Exception, object> 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)
|
private static void Unpin(UvWriteReq req)
|
||||||
{
|
{
|
||||||
foreach (var pin in req._pins)
|
foreach (var pin in req._pins)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"url": "git://github.com/aspnet/kestrelhttpserver"
|
"url": "git://github.com/aspnet/kestrelhttpserver"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Framework.Runtime.Abstractions": "1.0.0-*"
|
"Microsoft.Framework.Runtime.Abstractions": "1.0.0-beta7-*"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnx451": { },
|
"dnx451": { },
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,251 @@
|
||||||
|
using Microsoft.AspNet.Server.Kestrel;
|
||||||
|
using Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
using Microsoft.Framework.Runtime.Infrastructure;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
{
|
||||||
|
public class MultipleLoopTests
|
||||||
|
{
|
||||||
|
Libuv _uv;
|
||||||
|
public MultipleLoopTests()
|
||||||
|
{
|
||||||
|
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
|
_uv = engine.Libuv;
|
||||||
|
}
|
||||||
|
|
||||||
|
ILibraryManager LibraryManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var locator = CallContextServiceLocator.Locator;
|
||||||
|
if (locator == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var services = locator.ServiceProvider;
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InitAndCloseServerPipe()
|
||||||
|
{
|
||||||
|
var loop = new UvLoopHandle();
|
||||||
|
var pipe = new UvPipeHandle();
|
||||||
|
|
||||||
|
loop.Init(_uv);
|
||||||
|
pipe.Init(loop, true);
|
||||||
|
pipe.Bind(@"\\.\pipe\InitAndCloseServerPipe");
|
||||||
|
pipe.Dispose();
|
||||||
|
|
||||||
|
loop.Run();
|
||||||
|
|
||||||
|
pipe.Dispose();
|
||||||
|
loop.Dispose();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ServerPipeListenForConnections()
|
||||||
|
{
|
||||||
|
var loop = new UvLoopHandle();
|
||||||
|
var serverListenPipe = new UvPipeHandle();
|
||||||
|
|
||||||
|
loop.Init(_uv);
|
||||||
|
serverListenPipe.Init(loop, false);
|
||||||
|
serverListenPipe.Bind(@"\\.\pipe\ServerPipeListenForConnections");
|
||||||
|
serverListenPipe.Listen(128, (_1, status, error, _2) =>
|
||||||
|
{
|
||||||
|
var serverConnectionPipe = new UvPipeHandle();
|
||||||
|
serverConnectionPipe.Init(loop, true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverListenPipe.Accept(serverConnectionPipe);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
serverConnectionPipe.Dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var writeRequest = new UvWriteReq();
|
||||||
|
writeRequest.Init(loop);
|
||||||
|
writeRequest.Write(
|
||||||
|
serverConnectionPipe,
|
||||||
|
new ArraySegment<ArraySegment<byte>>(new ArraySegment<byte>[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) }),
|
||||||
|
(_3, status2, error2, _4) =>
|
||||||
|
{
|
||||||
|
writeRequest.Dispose();
|
||||||
|
serverConnectionPipe.Dispose();
|
||||||
|
serverListenPipe.Dispose();
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
var worker = new Thread(() =>
|
||||||
|
{
|
||||||
|
var loop2 = new UvLoopHandle();
|
||||||
|
var clientConnectionPipe = new UvPipeHandle();
|
||||||
|
var connect = new UvConnectRequest();
|
||||||
|
|
||||||
|
loop2.Init(_uv);
|
||||||
|
clientConnectionPipe.Init(loop2, true);
|
||||||
|
connect.Init(loop2);
|
||||||
|
connect.Connect(clientConnectionPipe, @"\\.\pipe\ServerPipeListenForConnections", (_1, status, error, _2) =>
|
||||||
|
{
|
||||||
|
var buf = loop2.Libuv.buf_init(Marshal.AllocHGlobal(8192), 8192);
|
||||||
|
|
||||||
|
connect.Dispose();
|
||||||
|
clientConnectionPipe.ReadStart(
|
||||||
|
(_3, cb, _4) => buf,
|
||||||
|
(_3, status2, error2, _4) =>
|
||||||
|
{
|
||||||
|
if (status2 <= 0)
|
||||||
|
{
|
||||||
|
clientConnectionPipe.Dispose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
}, null);
|
||||||
|
loop2.Run();
|
||||||
|
loop2.Dispose();
|
||||||
|
});
|
||||||
|
worker.Start();
|
||||||
|
loop.Run();
|
||||||
|
loop.Dispose();
|
||||||
|
worker.Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ServerPipeDispatchConnections()
|
||||||
|
{
|
||||||
|
|
||||||
|
var loop = new UvLoopHandle();
|
||||||
|
loop.Init(_uv);
|
||||||
|
|
||||||
|
var serverConnectionPipe = default(UvPipeHandle);
|
||||||
|
|
||||||
|
var serverListenPipe = new UvPipeHandle();
|
||||||
|
serverListenPipe.Init(loop, false);
|
||||||
|
serverListenPipe.Bind(@"\\.\pipe\ServerPipeListenForConnections");
|
||||||
|
serverListenPipe.Listen(128, (_1, status, error, _2) =>
|
||||||
|
{
|
||||||
|
serverConnectionPipe = new UvPipeHandle();
|
||||||
|
serverConnectionPipe.Init(loop, true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverListenPipe.Accept(serverConnectionPipe);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
serverConnectionPipe.Dispose();
|
||||||
|
serverConnectionPipe = null;
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
var serverListenTcp = new UvTcpHandle();
|
||||||
|
serverListenTcp.Init(loop);
|
||||||
|
serverListenTcp.Bind(new IPEndPoint(0, 54321));
|
||||||
|
serverListenTcp.Listen(128, (_1, status, error, _2) =>
|
||||||
|
{
|
||||||
|
var serverConnectionTcp = new UvTcpHandle();
|
||||||
|
serverConnectionTcp.Init(loop);
|
||||||
|
serverListenTcp.Accept(serverConnectionTcp);
|
||||||
|
|
||||||
|
var writeRequest = new UvWriteReq();
|
||||||
|
writeRequest.Init(loop);
|
||||||
|
writeRequest.Write2(
|
||||||
|
serverConnectionPipe,
|
||||||
|
new ArraySegment<ArraySegment<byte>>(new ArraySegment<byte>[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) }),
|
||||||
|
serverConnectionTcp,
|
||||||
|
(_3, status2, error2, _4) =>
|
||||||
|
{
|
||||||
|
writeRequest.Dispose();
|
||||||
|
serverConnectionPipe.Dispose();
|
||||||
|
serverConnectionTcp.Dispose();
|
||||||
|
serverListenPipe.Dispose();
|
||||||
|
serverListenTcp.Dispose();
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
var worker = new Thread(() =>
|
||||||
|
{
|
||||||
|
var loop2 = new UvLoopHandle();
|
||||||
|
var clientConnectionPipe = new UvPipeHandle();
|
||||||
|
var connect = new UvConnectRequest();
|
||||||
|
|
||||||
|
loop2.Init(_uv);
|
||||||
|
clientConnectionPipe.Init(loop2, true);
|
||||||
|
connect.Init(loop2);
|
||||||
|
connect.Connect(clientConnectionPipe, @"\\.\pipe\ServerPipeListenForConnections", (_1, status, error, _2) =>
|
||||||
|
{
|
||||||
|
connect.Dispose();
|
||||||
|
|
||||||
|
var buf = loop2.Libuv.buf_init(Marshal.AllocHGlobal(64), 64);
|
||||||
|
clientConnectionPipe.ReadStart(
|
||||||
|
(_3, cb, _4) => buf,
|
||||||
|
(_3, status2, error2, _4) =>
|
||||||
|
{
|
||||||
|
if (status2 <= 0)
|
||||||
|
{
|
||||||
|
clientConnectionPipe.Dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var clientConnectionTcp = new UvTcpHandle();
|
||||||
|
clientConnectionTcp.Init(loop2);
|
||||||
|
clientConnectionPipe.Accept(clientConnectionTcp);
|
||||||
|
var buf2 = loop2.Libuv.buf_init(Marshal.AllocHGlobal(64), 64);
|
||||||
|
clientConnectionTcp.ReadStart(
|
||||||
|
(_5, cb, _6) => buf2,
|
||||||
|
(_5, status3, error3, _6) =>
|
||||||
|
{
|
||||||
|
if (status3 <= 0)
|
||||||
|
{
|
||||||
|
clientConnectionTcp.Dispose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
}, null);
|
||||||
|
loop2.Run();
|
||||||
|
loop2.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
var worker2 = new Thread(() =>
|
||||||
|
{
|
||||||
|
var socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
||||||
|
socket.Connect(IPAddress.Loopback, 54321);
|
||||||
|
socket.Send(new byte[] { 6, 7, 8, 9 });
|
||||||
|
socket.Shutdown(SocketShutdown.Send);
|
||||||
|
var cb = socket.Receive(new byte[64]);
|
||||||
|
socket.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.Start();
|
||||||
|
worker2.Start();
|
||||||
|
|
||||||
|
loop.Run();
|
||||||
|
loop.Dispose();
|
||||||
|
worker.Join();
|
||||||
|
worker2.Join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.KestrelTests
|
namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -8,9 +11,21 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public void Main()
|
private readonly IApplicationEnvironment env;
|
||||||
|
private readonly IServiceProvider sp;
|
||||||
|
|
||||||
|
public Program(IApplicationEnvironment env, IServiceProvider sp)
|
||||||
{
|
{
|
||||||
new EngineTests().DisconnectingClient().Wait();
|
this.env = env;
|
||||||
|
this.sp = sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Main()
|
||||||
|
{
|
||||||
|
return new Xunit.Runner.AspNet.Program(env, sp).Main(new string[] {
|
||||||
|
"-class",
|
||||||
|
typeof(MultipleLoopTests).FullName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
"allowUnsafe": true
|
"allowUnsafe": true
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"run": "xunit.runner.aspnet",
|
"run": "Microsoft.AspNet.Server.KestrelTests",
|
||||||
"test": "xunit.runner.aspnet"
|
"test": "xunit.runner.aspnet"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue