275 lines
8.1 KiB
C#
275 lines
8.1 KiB
C#
// 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.Diagnostics;
|
|
using System.Runtime.ExceptionServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Microsoft.AspNet.Server.Kestrel
|
|
{
|
|
/// <summary>
|
|
/// Summary description for KestrelThread
|
|
/// </summary>
|
|
public class KestrelThread
|
|
{
|
|
KestrelEngine _engine;
|
|
Thread _thread;
|
|
UvLoopHandle _loop;
|
|
UvAsyncHandle _post;
|
|
Queue<Work> _workAdding = new Queue<Work>();
|
|
Queue<Work> _workRunning = new Queue<Work>();
|
|
Queue<CloseHandle> _closeHandleAdding = new Queue<CloseHandle>();
|
|
Queue<CloseHandle> _closeHandleRunning = new Queue<CloseHandle>();
|
|
object _workSync = new Object();
|
|
bool _stopImmediate = false;
|
|
private ExceptionDispatchInfo _closeError;
|
|
|
|
public KestrelThread(KestrelEngine engine)
|
|
{
|
|
_engine = engine;
|
|
_loop = new UvLoopHandle();
|
|
_post = new UvAsyncHandle();
|
|
_thread = new Thread(ThreadStart);
|
|
QueueCloseHandle = PostCloseHandle;
|
|
}
|
|
|
|
public UvLoopHandle Loop { get { return _loop; } }
|
|
public ExceptionDispatchInfo FatalError { get { return _closeError; } }
|
|
|
|
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; internal set; }
|
|
|
|
public Task StartAsync()
|
|
{
|
|
var tcs = new TaskCompletionSource<int>();
|
|
_thread.Start(tcs);
|
|
return tcs.Task;
|
|
}
|
|
|
|
public void Stop(TimeSpan timeout)
|
|
{
|
|
Post(OnStop, null);
|
|
if (!_thread.Join((int)timeout.TotalMilliseconds))
|
|
{
|
|
Post(OnStopRude, null);
|
|
if (!_thread.Join((int)timeout.TotalMilliseconds))
|
|
{
|
|
Post(OnStopImmediate, null);
|
|
if (!_thread.Join((int)timeout.TotalMilliseconds))
|
|
{
|
|
#if DNX451
|
|
_thread.Abort();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (_closeError != null)
|
|
{
|
|
_closeError.Throw();
|
|
}
|
|
}
|
|
|
|
private void OnStop(object obj)
|
|
{
|
|
_post.Unreference();
|
|
}
|
|
|
|
private void OnStopRude(object obj)
|
|
{
|
|
_engine.Libuv.walk(
|
|
_loop,
|
|
(ptr, arg) =>
|
|
{
|
|
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
|
|
if (handle != _post)
|
|
{
|
|
handle.Dispose();
|
|
}
|
|
},
|
|
IntPtr.Zero);
|
|
}
|
|
|
|
private void OnStopImmediate(object obj)
|
|
{
|
|
_stopImmediate = true;
|
|
_loop.Stop();
|
|
}
|
|
|
|
public void Post(Action<object> callback, object state)
|
|
{
|
|
lock (_workSync)
|
|
{
|
|
_workAdding.Enqueue(new Work { Callback = callback, State = state });
|
|
}
|
|
_post.Send();
|
|
}
|
|
|
|
public Task PostAsync(Action<object> callback, object state)
|
|
{
|
|
var tcs = new TaskCompletionSource<int>();
|
|
lock (_workSync)
|
|
{
|
|
_workAdding.Enqueue(new Work { Callback = callback, State = state, Completion = tcs });
|
|
}
|
|
_post.Send();
|
|
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)
|
|
{
|
|
lock (_workSync)
|
|
{
|
|
_closeHandleAdding.Enqueue(new CloseHandle { Callback = callback, Handle = handle });
|
|
}
|
|
_post.Send();
|
|
}
|
|
|
|
private void ThreadStart(object parameter)
|
|
{
|
|
var tcs = (TaskCompletionSource<int>)parameter;
|
|
try
|
|
{
|
|
_loop.Init(_engine.Libuv);
|
|
_post.Init(_loop, OnPost);
|
|
tcs.SetResult(0);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
tcs.SetException(ex);
|
|
}
|
|
|
|
try
|
|
{
|
|
var ran1 = _loop.Run();
|
|
if (_stopImmediate)
|
|
{
|
|
// thread-abort form of exit, resources will be leaked
|
|
return;
|
|
}
|
|
|
|
// run the loop one more time to delete the open handles
|
|
_post.Reference();
|
|
_post.DangerousClose();
|
|
|
|
_engine.Libuv.walk(
|
|
_loop,
|
|
(ptr, arg) =>
|
|
{
|
|
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
|
|
if (handle != _post)
|
|
{
|
|
handle.Dispose();
|
|
}
|
|
},
|
|
IntPtr.Zero);
|
|
|
|
// Ensure the "DangerousClose" operation completes in the event loop.
|
|
var ran2 = _loop.Run();
|
|
|
|
_loop.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_closeError = ExceptionDispatchInfo.Capture(ex);
|
|
// Request shutdown so we can rethrow this exception
|
|
// in Stop which should be observable.
|
|
_engine.AppShutdown.RequestShutdown();
|
|
}
|
|
}
|
|
|
|
private void OnPost()
|
|
{
|
|
DoPostWork();
|
|
DoPostCloseHandle();
|
|
}
|
|
|
|
private void DoPostWork()
|
|
{
|
|
Queue<Work> queue;
|
|
lock (_workSync)
|
|
{
|
|
queue = _workAdding;
|
|
_workAdding = _workRunning;
|
|
_workRunning = queue;
|
|
}
|
|
while (queue.Count != 0)
|
|
{
|
|
var work = queue.Dequeue();
|
|
try
|
|
{
|
|
work.Callback(work.State);
|
|
if (work.Completion != null)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(
|
|
tcs =>
|
|
{
|
|
((TaskCompletionSource<int>)tcs).SetResult(0);
|
|
},
|
|
work.Completion);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (work.Completion != null)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(_ => work.Completion.SetException(ex), null);
|
|
}
|
|
else
|
|
{
|
|
Trace.WriteLine("KestrelThread.DoPostWork " + ex.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DoPostCloseHandle()
|
|
{
|
|
Queue<CloseHandle> queue;
|
|
lock (_workSync)
|
|
{
|
|
queue = _closeHandleAdding;
|
|
_closeHandleAdding = _closeHandleRunning;
|
|
_closeHandleRunning = queue;
|
|
}
|
|
while (queue.Count != 0)
|
|
{
|
|
var closeHandle = queue.Dequeue();
|
|
try
|
|
{
|
|
closeHandle.Callback(closeHandle.Handle);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Trace.WriteLine("KestrelThread.DoPostCloseHandle " + ex.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct Work
|
|
{
|
|
public Action<object> Callback;
|
|
public object State;
|
|
public TaskCompletionSource<int> Completion;
|
|
}
|
|
private struct CloseHandle
|
|
{
|
|
public Action<IntPtr> Callback;
|
|
public IntPtr Handle;
|
|
}
|
|
}
|
|
}
|