Apache sendfile         OVERLAPPED overlapped;
        memset(&overlapped,'\0', sizeof(overlapped));
#ifdef WAIT_FOR_EVENT
        wait_event = overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#else
        wait_event = (HANDLE) sock->socketdes;
#endif
        overlapped.Offset = (DWORD)(curoff);
        overlapped.OffsetHigh = (DWORD)(curoff >> 32);        /* XXX BoundsChecker claims dwFlags must not be zero. */
        rv = TransmitFile(sock->socketdes,  /* socket */
                          file->filehand, /* open file descriptor of the file to be sent */
                          nbytes,         /* number of bytes to send. 0=send all */
                          0,              /* Number of bytes per send. 0=use default */
                          &overlapped,    /* OVERLAPPED structure */
                          ptfb,           /* header and trailer buffers */
                          dwFlags);       /* flags to control various aspects of TransmitFile */
        if (!rv)
        {
            status = apr_get_netos_error();
            if ((status == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) ||
                (status == APR_FROM_OS_ERROR(WSA_IO_PENDING))) 
            {
                rv = WaitForSingleObject(wait_event, 
                                         (DWORD)(sock->timeout >= 0 
                                                 ? sock->timeout_ms : INFINITE));
                if (rv == WAIT_OBJECT_0)
                    status = APR_SUCCESS;
                else if (rv == WAIT_TIMEOUT)
                    status = APR_FROM_OS_ERROR(WAIT_TIMEOUT);
                else if (rv == WAIT_ABANDONED)
                    status = APR_FROM_OS_ERROR(WAIT_TIMEOUT);
                else
                    status = apr_get_os_error();
            }
        }Atl ZEvtSyncSocket::Write WSAOVERLAPPED o;
o.hEvent = m_hEventWrite;
WSAResetEvent(o.hEvent);
if (WSASend(m_socket, pBuffers, nCount, pdwSize, 0, &o, 0))
{
DWORD dwLastError = WSAGetLastError();
if (dwLastError != WSA_IO_PENDING)
{
m_dwLastError = dwLastError;
bRet = false;
}
} // wait for write to complete
if (bRet)
{
if (WaitForSingleObject((HANDLE)m_hEventWrite, m_dwSocketTimeout) == WAIT_OBJECT_0)
{
DWORD dwFlags = 0;
if (WSAGetOverlappedResult(m_socket, &o, pdwSize, FALSE, &dwFlags))
bRet = true;
else
{
m_dwLastError = ::GetLastError();
bRet = false;
}
}
else
bRet = false;
}
return bRet;这两个片段都使用了重叠IO,并利用Event等待操作完成.
它们的OVERLAPPED变量都存放在栈上,它们都等待指定的时间,如果没有完成返回超时错误.它们在超时错误发生后都未进行CancelIo操作,也就是说重叠操作仍在进行,完成后不管是否有错,系统都会对OVERLAPPED变量做修改(错误值和传输字节数).而OVERLAPPED变量早已被销毁,也许已做后来进行操作的变量使用,这样修改后就造成了不确定的错误.如果以上理解正确的话,它们都不约而同的出现同样的错误,事实上还有许多代码也这样使用过.是设计者没有考虑到,还是有其它的原因使这个错误不会发生.我认为没考虑到的可能性较大些,希望有编写经验的或也写过类似代码的朋友来做个合理的解答.

解决方案 »

  1.   

    问题是这样的代码,如果出现超时后不在函数本体内进行CancelIo操作,那么OVERLAPPED始终被系统认为有效,原OVERLAPPED的内存地址将被后来的操作包括CancelIo所使用,即使以后(退出函数后)进行了CancelIo操作,也无法确认CancelIo的结果是正确的,它的过程中的栈变量也可能被强制修改过了.即使按照 nuaawenlin(飘人) 的说法,不再进行操作,比如关闭socket,问题照旧,系统会立即修改原OVERLAPPED.话说了那么多,好象只能在该函数体内调用CancelIo才可能保证绝对的正确性.