forked from ServiceStack/ServiceStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAppSelfHostBase.cs
More file actions
127 lines (106 loc) · 4.43 KB
/
AppSelfHostBase.cs
File metadata and controls
127 lines (106 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#if !NETSTANDARD2_0
using System;
using System.Net;
using System.Reflection;
using System.Threading;
using Amib.Threading;
using ServiceStack.Logging;
namespace ServiceStack.SmartThreadPool
{
public abstract class AppSelfHostBase
: AppHostHttpListenerBase
{
private readonly ILog log = LogManager.GetLogger(typeof(AppSelfHostBase));
private readonly AutoResetEvent listenForNextRequest = new AutoResetEvent(false);
private readonly Amib.Threading.SmartThreadPool threadPoolManager;
private const int IdleTimeout = 300;
protected AppSelfHostBase(string serviceName, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
threadPoolManager = new Amib.Threading.SmartThreadPool(IdleTimeout,
maxWorkerThreads: Math.Max(16, Environment.ProcessorCount * 2));
}
protected AppSelfHostBase(string serviceName, string handlerPath, params Assembly[] assembliesWithServices)
: base(serviceName, handlerPath, assembliesWithServices)
{
threadPoolManager = new Amib.Threading.SmartThreadPool(IdleTimeout,
maxWorkerThreads: Math.Max(16, Environment.ProcessorCount * 2));
}
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (disposed) return;
lock (this)
{
if (disposed) return;
if (disposing)
threadPoolManager.Dispose();
// new shared cleanup logic
disposed = true;
base.Dispose(disposing);
}
}
private bool IsListening => this.IsStarted && this.Listener != null && this.Listener.IsListening;
// Loop here to begin processing of new requests.
protected override void Listen(object state)
{
while (IsListening)
{
if (Listener == null) return;
try
{
Listener.BeginGetContext(ListenerCallback, Listener);
listenForNextRequest.WaitOne();
}
catch (Exception ex)
{
log.Error("Listen()", ex);
return;
}
if (Listener == null) return;
}
}
// Handle the processing of a request in here.
private void ListenerCallback(IAsyncResult asyncResult)
{
var listener = asyncResult.AsyncState as HttpListener;
HttpListenerContext context;
if (listener == null) return;
var isListening = listener.IsListening;
try
{
if (!isListening)
{
log.DebugFormat("Ignoring ListenerCallback() as HttpListener is no longer listening");
return;
}
// The EndGetContext() method, as with all Begin/End asynchronous methods in the .NET Framework,
// blocks until there is a request to be processed or some type of data is available.
context = listener.EndGetContext(asyncResult);
}
catch (Exception ex)
{
// You will get an exception when httpListener.Stop() is called
// because there will be a thread stopped waiting on the .EndGetContext()
// method, and again, that is just the way most Begin/End asynchronous
// methods of the .NET Framework work.
string errMsg = ex + ": " + isListening;
log.Warn(errMsg);
return;
}
finally
{
// Once we know we have a request (or exception), we signal the other thread
// so that it calls the BeginGetContext() (or possibly exits if we're not
// listening any more) method to start handling the next incoming request
// while we continue to process this request on a different thread.
listenForNextRequest.Set();
}
if (Config.DebugMode && log.IsDebugEnabled)
log.Debug($"{context.Request.UserHostAddress} Request : {context.Request.RawUrl}");
OnBeginRequest(context);
threadPoolManager.QueueWorkItem(ProcessRequestContext, context);
}
}
}
#endif