首页 > 解决方案 > 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;
}

标签: winapi

解决方案


推荐阅读