winapi - CancelIo 并不代表实际取消的 I/O
问题描述
我写了一个小程序。它向文件发出大量异步读取,这些读取将被 I/O 完成端口捕获。然后我通过 CancelIo 随机取消文件的所有 I/O(每个重叠结构单独而不是一次用于整个文件句柄)。因此,我计算了成功取消的次数以及由于 I/O 操作已完成而获得 GetLastError() 的 ERROR_NOT_FOUND 的取消次数。然后我对所有已发布的 I/O 执行 GetQueuedCompletionStatus()。我计算所有成功的 I/O 和所有取消的 I/O (GetLastError() == ERROR_OPERATION_ABORTED)。有趣的是,完成和取消的 I/O 的数量与我在发出 CancelIo 时计算的不同。那么为什么会有区别呢?这是我的测试程序:
#include <Windows.h>
#include <exception>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdint>
#include <cstdlib>
#include <system_error>
#include <utility>
#include <random>
using namespace std;
int main( int argc, char **argv )
{
auto throwSysErr = []( char const *str )
{
throw system_error( error_code( (int)GetLastError(), system_category() ), str ? str : "" );
};
try
{
if( argc < 3 )
throw exception( "parameters !" );
size_t blocks = atoi( argv[2] );
if( blocks == 0 )
throw exception( "blocks == 0" );
HANDLE hFile = CreateFileA( argv[1], GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
if( !hFile )
throwSysErr( "cf failed" );
size_t const BLOCKSIZE = 4096;
void *pMem = VirtualAlloc( nullptr, BLOCKSIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
if( !pMem )
throwSysErr( "va failed" );
OVERLAPPED ol;
if( !(ol.hEvent = CreateEvent( nullptr, FALSE, FALSE, nullptr )) )
throwSysErr( "ce failed" );
uint64_t offset = (uint64_t)(blocks - 1) * BLOCKSIZE;
ol.Offset = (DWORD) offset;
ol.OffsetHigh = (DWORD)(offset >> 32);
if( !WriteFile( hFile, pMem, BLOCKSIZE, nullptr, &ol ) &&
GetLastError() != ERROR_IO_PENDING )
throwSysErr( "wf failed " );
DWORD dwTransferred;
if( !GetOverlappedResult( hFile, &ol, &dwTransferred, TRUE ) ||
dwTransferred != BLOCKSIZE )
throwSysErr( "gor failed " );
HANDLE hIocp = CreateIoCompletionPort( hFile, NULL, 0x12345678, 1 );
if( !hIocp )
throwSysErr( "ciocp failed" );
vector<OVERLAPPED> vol( blocks );
for( size_t b = 0; b != blocks; ++b )
{
uint64_t offset = (uint64_t)b * BLOCKSIZE;
vol[b].Offset = (DWORD) offset;
vol[b].OffsetHigh = (DWORD)(offset >> 32);
vol[b].hEvent = NULL;
if( !ReadFile( hFile, pMem, BLOCKSIZE, nullptr, &vol[b] ) && GetLastError() != ERROR_IO_PENDING )
throwSysErr( "wf failed " );
}
vector<size_t> blockReorderList( blocks );
for( size_t b = 0; b != blocks; ++b )
blockReorderList[b] = b;
mt19937_64 mt;
uniform_int_distribution<size_t> uidBlock( 0, blocks - 1 );
for( size_t b = 0; b != blocks; ++b )
swap( blockReorderList[b], blockReorderList[uidBlock( mt )] );
unsigned cancelSucceeded = 0,
cancelCompleted = 0;
for( size_t b = 0; b != blocks; ++b )
if( CancelIoEx( hFile, &vol[blockReorderList[b]] ) )
++cancelSucceeded;
else
if( GetLastError() == ERROR_NOT_FOUND )
++cancelCompleted;
else
throwSysErr( "cio failed " );
cout << "cancel succeeded: " << cancelSucceeded << endl;
cout << "cancel already completed: " << cancelCompleted << endl;
uint32_t completedCounter = 0,
cancelledCounter = 0;
for( size_t c = 0; c != blocks; ++c )
{
DWORD dwBytesTransferred = 0;
ULONG_PTR ulpCompletionKey;
OVERLAPPED *pol;
if( GetQueuedCompletionStatus( hIocp, &dwBytesTransferred, &ulpCompletionKey, &pol, INFINITE ) )
++completedCounter;
else
if( GetLastError() == ERROR_OPERATION_ABORTED )
++cancelledCounter;
else
throwSysErr( "gqcs failed" );
}
cout << "completed: " << completedCounter << endl
<< "cancelled: " << cancelledCounter << endl;
cout << endl;
}
catch( exception &exc )
{
cout << exc.what() << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
解决方案
推荐阅读
- ubuntu - FT232H 用户空间 GPIO 设备
- android - 如何使自定义视图在Android中继承其父样式
- pytorch - 未找到从源 libavcodec/avcodec.h 安装 torchvision
- html - 为什么移动网站右侧有过多的空白?
- javascript - 嵌套列表中的jQuery目标数据元素
- .net-core - 在自定义插件中构建的 NopCommerce 插件
- reactjs - 如何在 Undux Side Effect 中使用 React Hooks (useRouter)?
- c++ - 具有移动语义的 C++ 可变参数模板函数
- mysql - MySQL SELECT JOIN 与空行返回
- ionic-framework - Leaflet - 如何将点击事件添加到离子应用程序中的标记弹出内的按钮?