Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
58ed8f6c
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
58ed8f6c
编写于
5月 22, 2015
作者:
J
Jared Parsons
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #3022 from jaredpar/fix-2866
Fix connection issues in server
上级
0a13911f
89818226
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
80 addition
and
69 deletion
+80
-69
src/Compilers/Core/VBCSCompiler/ServerDispatcher.Connection.cs
...ompilers/Core/VBCSCompiler/ServerDispatcher.Connection.cs
+18
-11
src/Compilers/Core/VBCSCompiler/ServerDispatcher.cs
src/Compilers/Core/VBCSCompiler/ServerDispatcher.cs
+45
-54
src/Compilers/Core/VBCSCompilerTests/CompilerServerApiTest.cs
...Compilers/Core/VBCSCompilerTests/CompilerServerApiTest.cs
+17
-4
未找到文件。
src/Compilers/Core/VBCSCompiler/ServerDispatcher.Connection.cs
浏览文件 @
58ed8f6c
...
...
@@ -14,6 +14,18 @@ namespace Microsoft.CodeAnalysis.CompilerServer
{
internal
partial
class
ServerDispatcher
{
internal
struct
ConnectionData
{
public
readonly
CompletionReason
CompletionReason
;
public
readonly
TimeSpan
?
KeepAlive
;
public
ConnectionData
(
CompletionReason
completionReason
,
TimeSpan
?
keepAlive
=
null
)
{
CompletionReason
=
completionReason
;
KeepAlive
=
keepAlive
;
}
}
internal
enum
CompletionReason
{
/// <summary>
...
...
@@ -52,9 +64,8 @@ public Connection(IClientConnection clientConnection, IRequestHandler handler)
_handler
=
handler
;
}
public
async
Task
<
Co
mpletionReason
>
ServeConnection
(
TaskCompletionSource
<
TimeSpan
?>
timeoutCompletionSource
=
null
,
CancellationToken
cancellationToken
=
default
(
CancellationToken
))
public
async
Task
<
Co
nnectionData
>
ServeConnection
(
CancellationToken
cancellationToken
=
default
(
CancellationToken
))
{
timeoutCompletionSource
=
timeoutCompletionSource
??
new
TaskCompletionSource
<
TimeSpan
?>();
try
{
BuildRequest
request
;
...
...
@@ -67,10 +78,10 @@ public async Task<CompletionReason> ServeConnection(TaskCompletionSource<TimeSpa
catch
(
Exception
e
)
{
LogException
(
e
,
"Error reading build request."
);
return
CompletionReason
.
CompilationNotStarted
;
return
new
ConnectionData
(
CompletionReason
.
CompilationNotStarted
)
;
}
CheckForNewKeepAlive
(
request
,
timeoutCompletionSource
);
var
keepAlive
=
CheckForNewKeepAlive
(
request
);
// Kick off both the compilation and a task to monitor the pipe for closing.
var
buildCts
=
CancellationTokenSource
.
CreateLinkedTokenSource
(
cancellationToken
);
...
...
@@ -105,15 +116,11 @@ public async Task<CompletionReason> ServeConnection(TaskCompletionSource<TimeSpa
// Begin the tear down of the Task which didn't complete.
buildCts
.
Cancel
();
return
reason
;
return
new
ConnectionData
(
reason
,
keepAlive
)
;
}
finally
{
_clientConnection
.
Close
();
// Ensure that the task is completed. If the code previously added a real result this will
// simply be a nop.
timeoutCompletionSource
.
TrySetResult
(
null
);
}
}
...
...
@@ -121,7 +128,7 @@ public async Task<CompletionReason> ServeConnection(TaskCompletionSource<TimeSpa
/// Check the request arguments for a new keep alive time. If one is present,
/// set the server timer to the new time.
/// </summary>
private
void
CheckForNewKeepAlive
(
BuildRequest
request
,
TaskCompletionSource
<
TimeSpan
?>
timeoutCompletionSource
)
private
TimeSpan
?
CheckForNewKeepAlive
(
BuildRequest
request
)
{
TimeSpan
?
timeout
=
null
;
foreach
(
var
arg
in
request
.
Arguments
)
...
...
@@ -140,7 +147,7 @@ private void CheckForNewKeepAlive(BuildRequest request, TaskCompletionSource<Tim
}
}
timeoutCompletionSource
.
SetResult
(
timeout
)
;
return
timeout
;
}
private
Task
<
BuildResponse
>
ServeBuildRequest
(
BuildRequest
request
,
CancellationToken
cancellationToken
)
...
...
src/Compilers/Core/VBCSCompiler/ServerDispatcher.cs
浏览文件 @
58ed8f6c
...
...
@@ -34,18 +34,6 @@ internal interface IRequestHandler
/// </remarks>
internal
partial
class
ServerDispatcher
{
private
class
ConnectionData
{
public
readonly
Task
<
CompletionReason
>
ConnectionTask
;
public
Task
<
TimeSpan
?>
ChangeKeepAliveTask
;
internal
ConnectionData
(
Task
<
CompletionReason
>
connectionTask
,
Task
<
TimeSpan
?>
changeKeepAliveTask
)
{
ConnectionTask
=
connectionTask
;
ChangeKeepAliveTask
=
changeKeepAliveTask
;
}
}
/// <summary>
/// Default time the server will stay alive after the last request disconnects.
/// </summary>
...
...
@@ -154,15 +142,15 @@ public ServerDispatcher(IRequestHandler handler, IDiagnosticListener diagnosticL
/// test framework. The code hooks <see cref="AppDomain.AssemblyResolve"/> in a way
/// that prevents xUnit from running correctly and hence must be disabled.
/// </remarks>
[
System
.
Diagnostics
.
CodeAnalysis
.
SuppressMessage
(
"Microsoft.Reliability"
,
"CA2001:AvoidCallingProblematicMethods"
,
MessageId
=
"System.GC.Collect"
,
Justification
=
"We intentionally call GC.Collect when anticipate long period on inactivity."
)]
[
System
.
Diagnostics
.
CodeAnalysis
.
SuppressMessage
(
"Microsoft.Reliability"
,
"CA2001:AvoidCallingProblematicMethods"
,
MessageId
=
"System.GC.Collect"
,
Justification
=
"We intentionally call GC.Collect when anticipate long period on inactivity."
)]
public
void
ListenAndDispatchConnections
(
string
pipeName
,
TimeSpan
?
keepAlive
,
CancellationToken
cancellationToken
=
default
(
CancellationToken
))
{
Debug
.
Assert
(
SynchronizationContext
.
Current
==
null
);
var
isKeepAliveDefault
=
true
;
var
connectionList
=
new
List
<
ConnectionData
>();
var
connectionList
=
new
List
<
Task
<
ConnectionData
>
>();
Task
gcTask
=
null
;
Task
timeoutTask
=
null
;
Task
<
NamedPipeServerStream
>
listenTask
=
null
;
...
...
@@ -192,9 +180,8 @@ public void ListenAndDispatchConnections(string pipeName, TimeSpan? keepAlive, C
// If there is a connection event that has highest priority.
if
(
listenTask
.
IsCompleted
&&
!
cancellationToken
.
IsCancellationRequested
)
{
var
changeKeepAliveSource
=
new
TaskCompletionSource
<
TimeSpan
?>();
var
connectionTask
=
CreateHandleConnectionTask
(
listenTask
,
changeKeepAliveSource
,
cancellationToken
);
connectionList
.
Add
(
new
ConnectionData
(
connectionTask
,
changeKeepAliveSource
.
Task
));
var
connectionTask
=
CreateHandleConnectionTask
(
listenTask
,
_handler
,
cancellationToken
);
connectionList
.
Add
(
connectionTask
);
listenTask
=
null
;
listenCancellationTokenSource
=
null
;
timeoutTask
=
null
;
...
...
@@ -234,7 +221,7 @@ public void ListenAndDispatchConnections(string pipeName, TimeSpan? keepAlive, C
try
{
Task
.
WaitAll
(
connectionList
.
Select
(
x
=>
x
.
ConnectionTask
).
ToArray
());
Task
.
WaitAll
(
connectionList
.
ToArray
());
}
catch
{
...
...
@@ -248,11 +235,10 @@ public void ListenAndDispatchConnections(string pipeName, TimeSpan? keepAlive, C
/// The server farms out work to Task values and this method needs to wait until at least one of them
/// has completed.
/// </summary>
private
void
WaitForAnyCompletion
(
IEnumerable
<
ConnectionData
>
e
,
Task
[]
other
,
CancellationToken
cancellationToken
)
private
void
WaitForAnyCompletion
(
IEnumerable
<
Task
<
ConnectionData
>
>
e
,
Task
[]
other
,
CancellationToken
cancellationToken
)
{
var
all
=
new
List
<
Task
>();
all
.
AddRange
(
e
.
Select
(
x
=>
x
.
ConnectionTask
));
all
.
AddRange
(
e
.
Select
(
x
=>
x
.
ChangeKeepAliveTask
).
Where
(
x
=>
x
!=
null
));
all
.
AddRange
(
e
);
all
.
AddRange
(
other
.
Where
(
x
=>
x
!=
null
));
try
...
...
@@ -270,32 +256,31 @@ private void WaitForAnyCompletion(IEnumerable<ConnectionData> e, Task[] other, C
/// Checks the completed connection objects.
/// </summary>
/// <returns>True if everything completed normally and false if there were any client disconnections.</returns>
private
bool
CheckConnectionTask
(
List
<
ConnectionData
>
connectionList
,
ref
TimeSpan
?
keepAlive
,
ref
bool
isKeepAliveDefault
)
private
bool
CheckConnectionTask
(
List
<
Task
<
ConnectionData
>
>
connectionList
,
ref
TimeSpan
?
keepAlive
,
ref
bool
isKeepAliveDefault
)
{
var
allFine
=
true
;
foreach
(
var
current
in
connectionList
)
var
processedCount
=
0
;
var
i
=
0
;
while
(
i
<
connectionList
.
Count
)
{
if
(
current
.
ChangeKeepAliveTask
!=
null
&&
current
.
ChangeKeepAliveTask
.
IsCompleted
)
var
current
=
connectionList
[
i
];
if
(!
current
.
IsCompleted
)
{
ChangeKeepAlive
(
current
.
ChangeKeepAliveTask
,
ref
keepAlive
,
ref
isKeepAliveDefault
)
;
c
urrent
.
ChangeKeepAliveTask
=
null
;
i
++
;
c
ontinue
;
}
if
(
current
.
ConnectionTask
.
IsCompleted
)
{
// https://github.com/dotnet/roslyn/issues/2866
// Debug.Assert(current.ChangeKeepAliveTask == null);
connectionList
.
RemoveAt
(
i
);
processedCount
++;
if
(
current
.
ConnectionTask
.
Result
==
CompletionReason
.
ClientDisconnect
)
{
allFine
=
false
;
}
var
connectionData
=
current
.
Result
;
ChangeKeepAlive
(
connectionData
.
KeepAlive
,
ref
keepAlive
,
ref
isKeepAliveDefault
);
if
(
connectionData
.
CompletionReason
==
CompletionReason
.
ClientDisconnect
)
{
allFine
=
false
;
}
}
// Finally remove any ConnectionData for connections which are no longer active.
int
processedCount
=
connectionList
.
RemoveAll
(
x
=>
x
.
ConnectionTask
.
IsCompleted
);
if
(
processedCount
>
0
)
{
_diagnosticListener
.
ConnectionProcessed
(
processedCount
);
...
...
@@ -304,15 +289,8 @@ private bool CheckConnectionTask(List<ConnectionData> connectionList, ref TimeSp
return
allFine
;
}
private
void
ChangeKeepAlive
(
T
ask
<
TimeSpan
?>
task
,
ref
TimeSpan
?
keepAlive
,
ref
bool
isKeepAliveDefault
)
private
void
ChangeKeepAlive
(
T
imeSpan
?
value
,
ref
TimeSpan
?
keepAlive
,
ref
bool
isKeepAliveDefault
)
{
Debug
.
Assert
(
task
.
IsCompleted
);
if
(
task
.
Status
!=
TaskStatus
.
RanToCompletion
)
{
return
;
}
var
value
=
task
.
Result
;
if
(
value
.
HasValue
)
{
if
(
isKeepAliveDefault
||
!
keepAlive
.
HasValue
||
value
.
Value
>
keepAlive
.
Value
)
...
...
@@ -407,15 +385,28 @@ private async Task<NamedPipeServerStream> CreateListenTask(string pipeName, Canc
}
/// <summary>
/// Creates a Task representing the processing of the new connection. Returns null
/// if the server is unable to create a new Task object for the connection.
/// Creates a Task representing the processing of the new connection. This will return a task that
/// will never fail. It will always produce a <see cref="ConnectionData"/> value. Connection errors
/// will end up being represented as <see cref="CompletionReason.ClientDisconnect"/>
/// </summary>
private
async
Task
<
CompletionReason
>
CreateHandleConnectionTask
(
Task
<
NamedPipeServerStream
>
pipeStreamTask
,
TaskCompletionSource
<
TimeSpan
?>
changeKeepAliveSource
,
CancellationToken
cancellationToken
)
internal
static
async
Task
<
ConnectionData
>
CreateHandleConnectionTask
(
Task
<
NamedPipeServerStream
>
pipeStreamTask
,
IRequestHandler
handler
,
CancellationToken
cancellationToken
)
{
var
pipeStream
=
await
pipeStreamTask
.
ConfigureAwait
(
false
);
var
clientConnection
=
new
NamedPipeClientConnection
(
pipeStream
);
var
connection
=
new
Connection
(
clientConnection
,
_handler
);
return
await
connection
.
ServeConnection
(
changeKeepAliveSource
,
cancellationToken
).
ConfigureAwait
(
false
);
Connection
connection
;
try
{
var
pipeStream
=
await
pipeStreamTask
.
ConfigureAwait
(
false
);
var
clientConnection
=
new
NamedPipeClientConnection
(
pipeStream
);
connection
=
new
Connection
(
clientConnection
,
handler
);
}
catch
(
Exception
ex
)
{
// Unable to establish a connection with the client. The client is responsible for
// handling this case. Nothing else for us to do here.
CompilerServerLogger
.
LogException
(
ex
,
"Error creating client named pipe"
);
return
new
ConnectionData
(
CompletionReason
.
ClientDisconnect
);
}
return
await
connection
.
ServeConnection
(
cancellationToken
).
ConfigureAwait
(
false
);
}
/// <summary>
...
...
src/Compilers/Core/VBCSCompilerTests/CompilerServerApiTest.cs
浏览文件 @
58ed8f6c
...
...
@@ -194,7 +194,7 @@ public void ClientDisconnectCancelBuildAndReturnsFailure()
.
Returns
(
s_emptyBuildResponse
);
var
client
=
new
ServerDispatcher
.
Connection
(
clientConnection
,
handler
.
Object
);
var
serveTask
=
client
.
ServeConnection
(
new
TaskCompletionSource
<
TimeSpan
?>()
);
var
serveTask
=
client
.
ServeConnection
();
// Once this returns we know the Connection object has kicked off a compilation and
// started monitoring the disconnect task. Can now initiate a disconnect in a known
...
...
@@ -202,7 +202,7 @@ public void ClientDisconnectCancelBuildAndReturnsFailure()
var
cancellationToken
=
handlerTaskSource
.
Task
.
Result
;
monitorTaskSource
.
SetResult
(
true
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
ClientDisconnect
,
serveTask
.
Result
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
ClientDisconnect
,
serveTask
.
Result
.
CompletionReason
);
Assert
.
True
(
cancellationToken
.
IsCancellationRequested
);
// Now that the asserts are done unblock the "build" long running task. Have to do this
...
...
@@ -221,7 +221,7 @@ public void ReadError()
clientConnection
.
CloseAction
=
delegate
{
calledClose
=
true
;
};
var
client
=
new
ServerDispatcher
.
Connection
(
clientConnection
,
handler
.
Object
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
CompilationNotStarted
,
client
.
ServeConnection
().
Result
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
CompilationNotStarted
,
client
.
ServeConnection
().
Result
.
CompletionReason
);
Assert
.
True
(
calledClose
);
}
...
...
@@ -242,7 +242,7 @@ public void WriteError()
.
Returns
(
s_emptyBuildResponse
);
var
client
=
new
ServerDispatcher
.
Connection
(
clientConnection
,
handler
.
Object
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
ClientDisconnect
,
client
.
ServeConnection
().
Result
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
ClientDisconnect
,
client
.
ServeConnection
().
Result
.
CompletionReason
);
}
[
Fact
]
...
...
@@ -258,6 +258,19 @@ public void KeepAliveNoConnections()
Assert
.
True
((
DateTime
.
Now
-
startTime
)
>
keepAlive
);
}
[
Fact
]
public
async
Task
FailedConnectionShoudlCreateFailedConnectionData
()
{
var
tcs
=
new
TaskCompletionSource
<
NamedPipeServerStream
>();
var
handler
=
new
Mock
<
IRequestHandler
>(
MockBehavior
.
Strict
);
var
connectionDataTask
=
ServerDispatcher
.
CreateHandleConnectionTask
(
tcs
.
Task
,
handler
.
Object
,
CancellationToken
.
None
);
tcs
.
SetException
(
new
Exception
());
var
connectionData
=
await
connectionDataTask
.
ConfigureAwait
(
false
);
Assert
.
Equal
(
ServerDispatcher
.
CompletionReason
.
ClientDisconnect
,
connectionData
.
CompletionReason
);
Assert
.
Null
(
connectionData
.
KeepAlive
);
}
/// <summary>
/// Ensure server respects keep alive and shuts down after processing a single connection.
/// </summary>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录