Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
dotNET Platform
runtime
提交
6bcc3e09
R
runtime
项目概览
dotNET Platform
/
runtime
12 个月 前同步成功
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
runtime
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
6bcc3e09
编写于
11月 08, 2022
作者:
A
Adam Sitnik
提交者:
GitHub
11月 08, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
unify error handling for FileStream and Pipe Streams (#77543)
上级
1b27edad
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
477 addition
and
106 deletion
+477
-106
src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
...raries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
+1
-0
src/libraries/System.IO.Pipes/tests/UnifiedErrorHandlingTests.cs
...raries/System.IO.Pipes/tests/UnifiedErrorHandlingTests.cs
+386
-0
src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs
...tem.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs
+72
-82
src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
...Lib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
+18
-24
未找到文件。
src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
浏览文件 @
6bcc3e09
...
...
@@ -22,6 +22,7 @@
<Compile Include="NamedPipeTests\NamedPipeTest.Specific.cs" />
<Compile Include="PipeStream.ProtectedMethods.Tests.cs" />
<Compile Include="PipeStreamConformanceTests.cs" />
<Compile Include="UnifiedErrorHandlingTests.cs" />
<Compile Include="$(CommonTestPath)System\Threading\Tasks\TaskTimeoutExtensions.cs" Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
...
...
src/libraries/System.IO.Pipes/tests/UnifiedErrorHandlingTests.cs
0 → 100644
浏览文件 @
6bcc3e09
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using
System.Threading.Tasks
;
using
Microsoft.Win32.SafeHandles
;
using
Xunit
;
namespace
System.IO.Pipes.Tests
{
public
static
class
UnifiedErrorHandlingTests
{
[
Fact
]
public
static
void
When_AnonymousPipeServer_IsClosed_AnonymousPipeClient_ReadReturnsZero
()
=>
DiposeServerAndVerifyClientBehaviour
(
GetAnonymousPipeStreams
(
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteRead
);
[
Fact
]
public
static
void
When_AnonymousPipeServer_IsClosed_FileStreamClient_ReadReturnsZero
()
=>
DiposeServerAndVerifyClientBehaviour
(
GetAnonymousPipeServerAndFileStreamClient
(
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteRead
);
[
Fact
]
public
static
void
When_AnonymousPipeServer_IsClosed_AnonymousPipeClient_WriteThrows
()
=>
DiposeServerAndVerifyClientBehaviour
(
GetAnonymousPipeStreams
(
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteThrows
);
[
Fact
]
public
static
void
When_AnonymousPipeServer_IsClosed_FileStreamClient_WriteThrows
()
=>
DiposeServerAndVerifyClientBehaviour
(
GetAnonymousPipeServerAndFileStreamClient
(
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteThrows
);
[
Fact
]
public
static
Task
When_AnonymousPipeServer_IsClosed_AnonymousPipeClient_ReadAsyncReturnsZero
()
=>
DiposeServerAndVerifyClientBehaviourAsync
(
GetAnonymousPipeStreams
(
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteReadAsync
);
[
Fact
]
public
static
Task
When_AnonymousPipeServer_IsClosed_FileStreamClient_ReadAsyncReturnsZero
()
=>
DiposeServerAndVerifyClientBehaviourAsync
(
GetAnonymousPipeServerAndFileStreamClient
(
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteReadAsync
);
[
Fact
]
public
static
Task
When_AnonymousPipeServer_IsClosed_AnonymousPipeClient_WriteAsyncThrows
()
=>
DiposeServerAndVerifyClientBehaviourAsync
(
GetAnonymousPipeStreams
(
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteAsyncThrows
);
[
Fact
]
public
static
Task
When_AnonymousPipeServer_IsClosed_FileStreamClient_WriteAsyncThrows
()
=>
DiposeServerAndVerifyClientBehaviourAsync
(
GetAnonymousPipeServerAndFileStreamClient
(
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteAsyncThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_NamedPipeClient_ReadReturnsZero
(
bool
asyncHandles
)
=>
DiposeServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteRead
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_FileStreamClient_ReadReturnsZero
(
bool
asyncHandles
)
=>
DiposeServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteRead
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_NamedPipeClient_WriteThrows
(
bool
asyncHandles
)
=>
DiposeServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_FileStreamClient_WriteThrows
(
bool
asyncHandles
)
=>
DiposeServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_NamedPipeClient_ReadAsyncReturnsZero
(
bool
asyncHandles
)
=>
await
DiposeServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteReadAsync
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_FileStreamClient_ReadAsyncReturnsZero
(
bool
asyncHandles
)
=>
await
DiposeServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteReadAsync
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_NamedPipeClient_WriteAsyncThrows
(
bool
asyncHandles
)
=>
await
DiposeServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteAsyncThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServer_IsClosed_FileStreamClient_WriteAsyncThrows
(
bool
asyncHandles
)
=>
await
DiposeServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteAsyncThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsNamedPipeClient_ReadReturnsZero
(
bool
asyncHandles
)
=>
DisconnectServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteRead
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsFileStreamClient_ReadReturnsZero
(
bool
asyncHandles
)
=>
DisconnectServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteRead
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsNamedPipeClient_WriteThrows
(
bool
asyncHandles
)
=>
DisconnectServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsFileStreamClient_WriteThrows
(
bool
asyncHandles
)
=>
DisconnectServerAndVerifyClientBehaviour
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsNamedPipeClient_ReadAsyncReturnsZero
(
bool
asyncHandles
)
=>
await
DisconnectServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
Out
,
PipeDirection
.
In
),
AssertZeroByteReadAsync
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsFileStreamClient_ReadAsyncReturnsZero
(
bool
asyncHandles
)
=>
await
DisconnectServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
Out
,
FileAccess
.
Read
),
AssertZeroByteReadAsync
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsNamedPipeClient_WriteAsyncThrows
(
bool
asyncHandles
)
=>
await
DisconnectServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
PipeDirection
.
In
,
PipeDirection
.
Out
),
AssertWriteAsyncThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
SkipOnPlatform
(
TestPlatforms
.
LinuxBionic
,
"SElinux blocks UNIX sockets in our CI environment"
)]
[
SkipOnPlatform
(
TestPlatforms
.
iOS
|
TestPlatforms
.
tvOS
,
"iOS/tvOS blocks binding to UNIX sockets"
)]
public
static
async
Task
When_NamedPipeServerDisconnectsFileStreamClient_WriteAsyncThrows
(
bool
asyncHandles
)
=>
await
DisconnectServerAndVerifyClientBehaviourAsync
(
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
In
,
FileAccess
.
Write
),
AssertWriteAsyncThrows
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
PlatformSpecific
(
TestPlatforms
.
Windows
)]
public
static
Task
When_NamedPipeServer_IsClosed_CopyToAsyncJustFinishesWithoutThrowing
(
bool
asyncHandles
)
=>
TestCopyToAsync
(
asyncHandles
,
dispose
:
true
);
[
Theory
]
[
InlineData
(
true
)]
[
InlineData
(
false
)]
[
PlatformSpecific
(
TestPlatforms
.
Windows
)]
public
static
Task
When_NamedPipeServerDisconnectsCopyToAsyncJustFinishesWithoutThrowing
(
bool
asyncHandles
)
=>
TestCopyToAsync
(
asyncHandles
,
dispose
:
false
);
private
static
async
Task
TestCopyToAsync
(
bool
asyncHandles
,
bool
dispose
)
{
(
NamedPipeServerStream
server
,
FileStream
client
)
=
await
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
asyncHandles
,
PipeDirection
.
Out
,
FileAccess
.
Read
);
using
(
server
)
using
(
client
)
using
(
MemoryStream
destination
=
new
())
{
if
(
dispose
)
{
await
server
.
DisposeAsync
();
}
else
{
server
.
Disconnect
();
}
// At this moment, the client handle is opened, it does not know yet that server has disconnected/closed the handle
Assert
.
True
(
client
.
CanRead
);
await
client
.
CopyToAsync
(
destination
);
Assert
.
Equal
(
0
,
destination
.
Length
);
Assert
.
Equal
(
0
,
destination
.
Position
);
}
}
private
static
(
AnonymousPipeServerStream
server
,
AnonymousPipeClientStream
client
)
GetAnonymousPipeStreams
(
PipeDirection
serverDirection
,
PipeDirection
clientDirection
)
{
AnonymousPipeServerStream
server
=
new
(
serverDirection
);
AnonymousPipeClientStream
client
=
new
(
clientDirection
,
server
.
ClientSafePipeHandle
);
return
(
server
,
client
);
}
private
static
(
AnonymousPipeServerStream
server
,
FileStream
client
)
GetAnonymousPipeServerAndFileStreamClient
(
PipeDirection
serverDirection
,
FileAccess
clientAccess
)
{
AnonymousPipeServerStream
server
=
new
(
serverDirection
);
FileStream
client
=
new
(
new
SafeFileHandle
(
nint
.
Parse
(
server
.
GetClientHandleAsString
()),
ownsHandle
:
true
),
clientAccess
,
0
);
return
(
server
,
client
);
}
private
static
async
Task
<(
NamedPipeServerStream
server
,
NamedPipeClientStream
client
)>
GetConnectedNamedPipeStreams
(
bool
asyncHandles
,
PipeDirection
serverDirection
,
PipeDirection
clientDirection
)
{
PipeOptions
options
=
asyncHandles
?
PipeOptions
.
Asynchronous
:
PipeOptions
.
None
;
string
pipeName
=
PipeStreamConformanceTests
.
GetUniquePipeName
();
NamedPipeServerStream
server
=
new
(
pipeName
,
serverDirection
,
1
,
PipeTransmissionMode
.
Byte
,
options
);
NamedPipeClientStream
client
=
new
(
"."
,
pipeName
,
clientDirection
,
options
);
await
Task
.
WhenAll
(
client
.
ConnectAsync
(),
server
.
WaitForConnectionAsync
());
return
(
server
,
client
);
}
private
static
async
Task
<(
NamedPipeServerStream
server
,
FileStream
client
)>
GetConnectedNamedPipeServerAndFileStreamClientStreams
(
bool
asyncHandles
,
PipeDirection
serverDirection
,
FileAccess
clientAccess
)
{
if
(
OperatingSystem
.
IsWindows
())
{
PipeOptions
pipeOptions
=
asyncHandles
?
PipeOptions
.
Asynchronous
:
PipeOptions
.
None
;
FileOptions
fileOptions
=
asyncHandles
?
FileOptions
.
Asynchronous
:
FileOptions
.
None
;
string
pipeName
=
PipeStreamConformanceTests
.
GetUniquePipeName
();
NamedPipeServerStream
server
=
new
(
pipeName
,
serverDirection
,
1
,
PipeTransmissionMode
.
Byte
,
pipeOptions
);
FileStream
client
=
new
(
$@"\\.\pipe\
{
pipeName
}
"
,
FileMode
.
Open
,
clientAccess
,
FileShare
.
None
,
0
,
fileOptions
);
await
server
.
WaitForConnectionAsync
();
return
(
server
,
client
);
}
else
{
(
NamedPipeServerStream
server
,
NamedPipeClientStream
namedPipeClient
)
=
await
GetConnectedNamedPipeStreams
(
asyncHandles
,
serverDirection
,
clientAccess
==
FileAccess
.
Read
?
PipeDirection
.
In
:
PipeDirection
.
Out
);
FileStream
fileStreamClient
=
new
(
new
SafeFileHandle
(
namedPipeClient
.
SafePipeHandle
.
DangerousGetHandle
(),
ownsHandle
:
true
),
clientAccess
,
bufferSize
:
0
,
isAsync
:
false
);
namedPipeClient
.
SafePipeHandle
.
SetHandleAsInvalid
();
return
(
server
,
fileStreamClient
);
}
}
private
static
void
DiposeServerAndVerifyClientBehaviour
((
Stream
server
,
Stream
client
)
connectedStreams
,
Action
<
Stream
>
assertMethod
)
{
connectedStreams
.
server
.
Dispose
();
assertMethod
(
connectedStreams
.
client
);
}
private
static
void
DisconnectServerAndVerifyClientBehaviour
((
NamedPipeServerStream
server
,
Stream
client
)
connectedStreams
,
Action
<
Stream
>
assertMethod
)
{
connectedStreams
.
server
.
Disconnect
();
assertMethod
(
connectedStreams
.
client
);
}
private
static
async
Task
DiposeServerAndVerifyClientBehaviourAsync
((
Stream
server
,
Stream
client
)
connectedStreams
,
Func
<
Stream
,
Task
>
assertMethod
)
{
await
connectedStreams
.
server
.
DisposeAsync
();
await
assertMethod
(
connectedStreams
.
client
);
}
private
static
async
Task
DisconnectServerAndVerifyClientBehaviourAsync
((
NamedPipeServerStream
server
,
Stream
client
)
connectedStreams
,
Func
<
Stream
,
Task
>
assertMethod
)
{
connectedStreams
.
server
.
Disconnect
();
await
assertMethod
(
connectedStreams
.
client
);
}
private
static
void
AssertZeroByteRead
(
Stream
client
)
{
using
(
client
)
{
Assert
.
Equal
(
0
,
client
.
Read
(
new
byte
[
100
]));
Assert
.
Equal
(
0
,
client
.
Read
(
new
byte
[
100
],
0
,
100
));
}
}
private
static
void
AssertWriteThrows
(
Stream
client
)
{
using
(
client
)
{
Assert
.
Throws
<
IOException
>(()
=>
client
.
Write
(
new
byte
[
100
],
0
,
100
));
Assert
.
Throws
<
IOException
>(()
=>
client
.
Write
(
new
byte
[
100
]));
}
}
private
static
async
Task
AssertZeroByteReadAsync
(
Stream
client
)
{
using
(
client
)
{
Assert
.
Equal
(
0
,
await
client
.
ReadAsync
(
new
byte
[
100
]));
Assert
.
Equal
(
0
,
await
client
.
ReadAsync
(
new
byte
[
100
],
0
,
100
));
}
}
private
static
async
Task
AssertWriteAsyncThrows
(
Stream
client
)
{
using
(
client
)
{
await
Assert
.
ThrowsAsync
<
IOException
>(()
=>
client
.
WriteAsync
(
new
byte
[
100
],
0
,
100
));
await
Assert
.
ThrowsAsync
<
IOException
>(()
=>
client
.
WriteAsync
(
new
byte
[
100
]).
AsTask
());
}
}
}
}
src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs
浏览文件 @
6bcc3e09
...
...
@@ -55,19 +55,15 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span<byte> buffer
}
int
errorCode
=
FileStreamHelpers
.
GetLastWin32ErrorAndDisposeHandleIfInvalid
(
handle
);
switch
(
errorCode
)
return
errorCode
switch
{
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position :
// "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file,
// ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF"
return
numBytesRead
;
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
// For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
case
Interop
.
Errors
.
ERROR_INVALID_PARAMETER
when
IsEndOfFileForNoBuffering
(
handle
,
fileOffset
):
return
0
;
default
:
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
}
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position:
// "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file,
// ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF"
Interop
.
Errors
.
ERROR_HANDLE_EOF
=>
numBytesRead
,
_
when
IsEndOfFile
(
errorCode
,
handle
,
fileOffset
)
=>
0
,
_
=>
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
)
};
}
}
...
...
@@ -113,20 +109,16 @@ private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span<b
resetEvent
.
ReleaseRefCount
(
overlapped
);
}
switch
(
errorCode
)
if
(
IsEndOfFile
(
errorCode
,
handle
,
fileOffset
)
)
{
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// logically success with 0 bytes read (read at end of file)
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
case
Interop
.
Errors
.
ERROR_INVALID_PARAMETER
when
IsEndOfFileForNoBuffering
(
handle
,
fileOffset
):
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
overlapped
->
InternalLow
=
IntPtr
.
Zero
;
return
0
;
default
:
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
overlapped
->
InternalLow
=
IntPtr
.
Zero
;
return
0
;
}
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
}
}
finally
...
...
@@ -163,13 +155,7 @@ internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan<by
}
int
errorCode
=
FileStreamHelpers
.
GetLastWin32ErrorAndDisposeHandleIfInvalid
(
handle
);
switch
(
errorCode
)
{
case
Interop
.
Errors
.
ERROR_NO_DATA
:
// EOF on a pipe
return
;
default
:
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
}
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
}
}
...
...
@@ -222,10 +208,6 @@ private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, Read
switch
(
errorCode
)
{
case
Interop
.
Errors
.
ERROR_NO_DATA
:
// For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
return
;
case
Interop
.
Errors
.
ERROR_INVALID_PARAMETER
:
// ERROR_INVALID_PARAMETER may be returned for writes
// where the position is too large or for synchronous writes
...
...
@@ -288,28 +270,27 @@ private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, Read
{
// The operation failed, or it's pending.
errorCode
=
FileStreamHelpers
.
GetLastWin32ErrorAndDisposeHandleIfInvalid
(
handle
);
switch
(
errorCode
)
{
case
Interop
.
Errors
.
ERROR_IO_PENDING
:
// Common case: IO was initiated, completion will be handled by callback.
// Register for cancellation now that the operation has been initiated.
vts
.
RegisterForCancellation
(
cancellationToken
);
break
;
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// logically success with 0 bytes read (read at end of file)
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
case
Interop
.
Errors
.
ERROR_INVALID_PARAMETER
when
IsEndOfFileForNoBuffering
(
handle
,
fileOffset
):
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
nativeOverlapped
->
InternalLow
=
IntPtr
.
Zero
;
vts
.
Dispose
();
return
(
null
,
0
);
default
:
// Error. Callback will not be called.
vts
.
Dispose
();
return
(
null
,
errorCode
);
if
(
errorCode
==
Interop
.
Errors
.
ERROR_IO_PENDING
)
{
// Common case: IO was initiated, completion will be handled by callback.
// Register for cancellation now that the operation has been initiated.
vts
.
RegisterForCancellation
(
cancellationToken
);
}
else
if
(
IsEndOfFile
(
errorCode
,
handle
,
fileOffset
))
{
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
nativeOverlapped
->
InternalLow
=
IntPtr
.
Zero
;
vts
.
Dispose
();
return
(
null
,
0
);
}
else
{
// Error. Callback will not be called.
vts
.
Dispose
();
return
(
null
,
errorCode
);
}
}
}
...
...
@@ -378,9 +359,6 @@ private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, Read
// Register for cancellation now that the operation has been initiated.
vts
.
RegisterForCancellation
(
cancellationToken
);
break
;
case
Interop
.
Errors
.
ERROR_NO_DATA
:
// EOF on a pipe. IO callback will not be called.
vts
.
Dispose
();
return
(
null
,
0
);
default
:
// Error. Callback will not be invoked.
vts
.
Dispose
();
...
...
@@ -587,27 +565,27 @@ private static unsafe ValueTask<int> ReadFileScatterAsync(SafeFileHandle handle,
{
// The operation failed, or it's pending.
int
errorCode
=
FileStreamHelpers
.
GetLastWin32ErrorAndDisposeHandleIfInvalid
(
handle
);
switch
(
errorCode
)
{
case
Interop
.
Errors
.
ERROR_IO_PENDING
:
// Common case: IO was initiated, completion will be handled by callback.
// Register for cancellation now that the operation has been initiated.
vts
.
RegisterForCancellation
(
cancellationToken
);
break
;
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// logically success with 0 bytes read (read at end of file)
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
nativeOverlapped
->
InternalLow
=
IntPtr
.
Zero
;
vts
.
Dispose
();
return
ValueTask
.
FromResult
(
0
);
default
:
// Error. Callback will not be called.
vts
.
Dispose
();
return
ValueTask
.
FromException
<
int
>(
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
));
if
(
errorCode
==
Interop
.
Errors
.
ERROR_IO_PENDING
)
{
// Common case: IO was initiated, completion will be handled by callback.
// Register for cancellation now that the operation has been initiated.
vts
.
RegisterForCancellation
(
cancellationToken
);
}
else
if
(
IsEndOfFile
(
errorCode
,
handle
,
fileOffset
))
{
// EOF on a pipe. Callback will not be called.
// We clear the overlapped status bit for this special case (failure
// to do so looks like we are freeing a pending overlapped later).
nativeOverlapped
->
InternalLow
=
IntPtr
.
Zero
;
vts
.
Dispose
();
return
ValueTask
.
FromResult
(
0
);
}
else
{
// Error. Callback will not be called.
vts
.
Dispose
();
return
ValueTask
.
FromException
<
int
>(
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
));
}
}
}
...
...
@@ -695,9 +673,7 @@ private static unsafe ValueTask WriteFileGatherAsync(SafeFileHandle handle, IntP
{
// Error. Callback will not be invoked.
vts
.
Dispose
();
return
errorCode
==
Interop
.
Errors
.
ERROR_NO_DATA
// EOF on a pipe. IO callback will not be called.
?
ValueTask
.
CompletedTask
:
ValueTask
.
FromException
(
SafeFileHandle
.
OverlappedValueTaskSource
.
GetIOError
(
errorCode
,
path
:
null
));
return
ValueTask
.
FromException
(
SafeFileHandle
.
OverlappedValueTaskSource
.
GetIOError
(
errorCode
,
path
:
null
));
}
}
}
...
...
@@ -771,6 +747,20 @@ static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* pOv
}
}
internal
static
bool
IsEndOfFile
(
int
errorCode
,
SafeFileHandle
handle
,
long
fileOffset
)
{
switch
(
errorCode
)
{
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// logically success with 0 bytes read (read at end of file)
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
// For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
case
Interop
.
Errors
.
ERROR_PIPE_NOT_CONNECTED
:
// Named pipe server has disconnected, return 0 to match NamedPipeClientStream behaviour
case
Interop
.
Errors
.
ERROR_INVALID_PARAMETER
when
IsEndOfFileForNoBuffering
(
handle
,
fileOffset
):
return
true
;
default
:
return
false
;
}
}
// From https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering:
// "File access sizes, including the optional file offset in the OVERLAPPED structure,
// if specified, must be for a number of bytes that is an integer multiple of the volume sector size."
...
...
src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
浏览文件 @
6bcc3e09
...
...
@@ -225,40 +225,34 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, bool canS
}
// If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation.
if
(!
synchronousSuccess
)
if
(!
synchronousSuccess
&&
errorCode
!=
Interop
.
Errors
.
ERROR_IO_PENDING
)
{
switch
(
errorCode
)
if
(!
RandomAccess
.
IsEndOfFile
(
errorCode
,
handle
,
readAwaitable
.
_position
)
)
{
case
Interop
.
Errors
.
ERROR_IO_PENDING
:
// Async operation in progress.
break
;
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// We're at or past the end of the file, and the overlapped callback
// won't be raised in these cases. Mark it as completed so that the await
// below will see it as such.
readAwaitable
.
MarkCompleted
();
break
;
default
:
// Everything else is an error (and there won't be a callback).
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
throw
Win32Marshal
.
GetExceptionForWin32Error
(
errorCode
,
handle
.
Path
);
}
// We're at or past the end of the file, and the overlapped callback
// won't be raised in these cases. Mark it as completed so that the await
// below will see it as such.
readAwaitable
.
MarkCompleted
();
}
// Wait for the async operation (which may or may not have already completed), then throw if it failed.
await
readAwaitable
;
switch
(
readAwaitable
.
_errorCode
)
if
(
readAwaitable
.
_errorCode
!=
Interop
.
Errors
.
ERROR_SUCCESS
)
{
case
0
:
// success
break
;
case
Interop
.
Errors
.
ERROR_BROKEN_PIPE
:
// logically success with 0 bytes read (write end of pipe closed)
case
Interop
.
Errors
.
ERROR_HANDLE_EOF
:
// logically success with 0 bytes read (read at end of file)
Debug
.
Assert
(
readAwaitable
.
_numBytes
==
0
,
$"Expected 0 bytes read, got
{
readAwaitable
.
_numBytes
}
"
);
break
;
case
Interop
.
Errors
.
ERROR_OPERATION_ABORTED
:
// canceled
if
(
readAwaitable
.
_errorCode
==
Interop
.
Errors
.
ERROR_OPERATION_ABORTED
)
{
throw
new
OperationCanceledException
(
cancellationToken
.
IsCancellationRequested
?
cancellationToken
:
new
CancellationToken
(
true
));
default
:
// error
}
else
if
(!
RandomAccess
.
IsEndOfFile
((
int
)
readAwaitable
.
_errorCode
,
handle
,
readAwaitable
.
_position
))
{
throw
Win32Marshal
.
GetExceptionForWin32Error
((
int
)
readAwaitable
.
_errorCode
,
handle
.
Path
);
}
Debug
.
Assert
(
readAwaitable
.
_numBytes
==
0
,
$"Expected 0 bytes read, got
{
readAwaitable
.
_numBytes
}
"
);
}
// Successful operation. If we got zero bytes, we're done: exit the read/write loop.
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录