-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
DiagnosticsServerRouterRunner.cs
166 lines (140 loc) · 7.36 KB
/
DiagnosticsServerRouterRunner.cs
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Microsoft.Diagnostics.NETCore.Client
{
/// <summary>
/// Class used to run different flavours of Diagnostics Server routers.
/// </summary>
internal class DiagnosticsServerRouterRunner
{
internal interface Callbacks
{
void OnRouterStarted(string tcpAddress);
void OnRouterStopped();
}
public static async Task<int> runIpcClientTcpServerRouter(CancellationToken token, string ipcClient, string tcpServer, int runtimeTimeoutMs, TcpServerRouterFactory.CreateInstanceDelegate tcpServerRouterFactory, ILogger logger, Callbacks callbacks)
{
return await runRouter(token, new IpcClientTcpServerRouterFactory(ipcClient, tcpServer, runtimeTimeoutMs, tcpServerRouterFactory, logger), callbacks).ConfigureAwait(false);
}
public static async Task<int> runIpcServerTcpServerRouter(CancellationToken token, string ipcServer, string tcpServer, int runtimeTimeoutMs, TcpServerRouterFactory.CreateInstanceDelegate tcpServerRouterFactory, ILogger logger, Callbacks callbacks)
{
return await runRouter(token, new IpcServerTcpServerRouterFactory(ipcServer, tcpServer, runtimeTimeoutMs, tcpServerRouterFactory, logger), callbacks).ConfigureAwait(false);
}
public static async Task<int> runIpcServerTcpClientRouter(CancellationToken token, string ipcServer, string tcpClient, int runtimeTimeoutMs, TcpClientRouterFactory.CreateInstanceDelegate tcpClientRouterFactory, ILogger logger, Callbacks callbacks)
{
return await runRouter(token, new IpcServerTcpClientRouterFactory(ipcServer, tcpClient, runtimeTimeoutMs, tcpClientRouterFactory, logger), callbacks).ConfigureAwait(false);
}
public static async Task<int> runIpcClientTcpClientRouter(CancellationToken token, string ipcClient, string tcpClient, int runtimeTimeoutMs, TcpClientRouterFactory.CreateInstanceDelegate tcpClientRouterFactory, ILogger logger, Callbacks callbacks)
{
return await runRouter(token, new IpcClientTcpClientRouterFactory(ipcClient, tcpClient, runtimeTimeoutMs, tcpClientRouterFactory, logger), callbacks).ConfigureAwait(false);
}
public static bool isLoopbackOnly(string address)
{
bool isLooback = false;
try
{
var value = new IpcTcpSocketEndPoint(address);
isLooback = IPAddress.IsLoopback(value.EndPoint.Address);
}
catch { }
return isLooback;
}
async static Task<int> runRouter(CancellationToken token, DiagnosticsServerRouterFactory routerFactory, Callbacks callbacks)
{
List<Task> runningTasks = new List<Task>();
List<Router> runningRouters = new List<Router>();
try
{
await routerFactory.Start(token);
if (!token.IsCancellationRequested)
callbacks?.OnRouterStarted(routerFactory.TcpAddress);
while (!token.IsCancellationRequested)
{
Task<Router> routerTask = null;
Router router = null;
try
{
routerTask = routerFactory.CreateRouterAsync(token);
do
{
// Search list and clean up dead router instances before continue waiting on new instances.
runningRouters.RemoveAll(IsRouterDead);
runningTasks.Clear();
foreach (var runningRouter in runningRouters)
runningTasks.Add(runningRouter.RouterTaskCompleted.Task);
runningTasks.Add(routerTask);
}
while (await Task.WhenAny(runningTasks.ToArray()).ConfigureAwait(false) != routerTask);
if (routerTask.IsFaulted || routerTask.IsCanceled)
{
//Throw original exception.
routerTask.GetAwaiter().GetResult();
}
if (routerTask.IsCompleted)
{
router = routerTask.Result;
router.Start();
// Add to list of running router instances.
runningRouters.Add(router);
router = null;
}
routerTask.Dispose();
routerTask = null;
}
catch (Exception ex)
{
router?.Dispose();
router = null;
routerTask?.Dispose();
routerTask = null;
// Timing out on accepting new streams could mean that either the frontend holds an open connection
// alive (but currently not using it), or we have a dead backend. If there are no running
// routers we assume a dead backend. Reset current backend endpoint and see if we get
// reconnect using same or different runtime instance.
if (ex is BackendStreamTimeoutException && runningRouters.Count == 0)
{
routerFactory.Logger?.LogDebug("No backend stream available before timeout.");
routerFactory.Reset();
}
// Timing out on accepting a new runtime connection means there is no runtime alive.
// Shutdown router to prevent instances to outlive runtime process (if auto shutdown is enabled).
if (ex is RuntimeTimeoutException)
{
routerFactory.Logger?.LogInformation("No runtime connected before timeout.");
routerFactory.Logger?.LogInformation("Starting automatic shutdown.");
throw;
}
}
}
}
catch (Exception ex)
{
routerFactory.Logger?.LogInformation($"Shutting down due to error: {ex.Message}");
}
finally
{
if (token.IsCancellationRequested)
routerFactory.Logger?.LogInformation("Shutting down due to cancelation request.");
runningRouters.RemoveAll(IsRouterDead);
runningRouters.Clear();
await routerFactory?.Stop();
callbacks?.OnRouterStopped();
routerFactory.Logger?.LogInformation("Router stopped.");
}
return 0;
}
static bool IsRouterDead(Router router)
{
bool isRunning = router.IsRunning && !router.RouterTaskCompleted.Task.IsCompleted;
if (!isRunning)
router.Dispose();
return !isRunning;
}
}
}