【问题标题】:Using the timeout in GetOverlappedResultEx to simulate a wait with timeout?使用 GetOverlappedResultEx 中的超时来模拟超时等待?
【发布时间】:2020-04-20 16:20:20
【问题描述】:

当使用GetOverlapedResult获取重叠(即异步)I/O操作的结果时,可以要求GetOverlappdResult“等待”

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

这里要注意的部分是GetOverlappedResult的最后一个参数:bWait

bWait

如果这个参数是TRUE,并且lpOverlapped结构的Internal成员是STATUS_PENDING,函数在操作完成之前不会返回。如果此参数为 FALSE 且操作仍处于挂起状态,则函数返回 FALSEGetLastError 函数返回 ERROR_IO_INCOMPLETE

对于我的代码,这意味着:

  • 如果 I/O 快速完成,ReadFile 返回 true
  • 如果 I/O 仍然待处理,那么我等待它完成
  • 如果还有其他错误,我会收到该错误。

这一切都很好。

大多数情况下,这一切都很好

我遇到了一个问题,实际的 ReadFile 操作需要 10 分钟 才能返回。当它返回时,它会返回:

The device is not ready (21)

这是one vendor's storage system. 的一个众所周知的问题。

我想做的是使用 Windows 的 异步 功能来等待 - 但有一个超时。

我注意到GetOverlappedResultEx,它有一种超时参数:

BOOL GetOverlappedResultEx(
  HANDLE       hFile,
  LPOVERLAPPED lpOverlapped,
  LPDWORD      lpNumberOfBytesTransferred,
  DWORD        dwMilliseconds, <----------
  BOOL         bAlertable
);

但是当我查看文档时,这是我了解我不理解的 Windows 编程细节的地方 - 排队的 APC,可变等待。但它仍然听起来像我想要的::

dwMilliseconds

超时间隔,以毫秒为单位。

如果 dwMilliseconds 为零并且操作仍在进行中,则 函数立即返回,GetLastError 函数返回 ERROR_IO_INCOMPLETE

如果 dwMilliseconds 为非零且操作仍在进行中, 函数等待直到对象发出信号,I/O 完成 例程或 APC 已排队,或者在返回之前经过了间隔。 使用 GetLastError 获取扩展错误信息。

如果 dwMillisecondsINFINITE,则函数仅在 对象已发出信号或 I/O 完成例程或 APC 已排队。

所以我尝试改变我的功能:

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      //if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

除了对 GetOverlappedResultEx 的调用不会在 5 秒(5,000 毫秒)内返回。相反,存储子系统需要 10 到 20 分钟才能恢复故障。

所以我随机尝试一下

我看到 GetOverlappedResultsEx 的另一个参数:

`bAlertable`

If this parameter is **TRUE** and the calling thread is in the waiting state, the function returns when the system queues an I/O completion routine or APC. The calling thread then runs the routine or function. Otherwise, the function does not return, and the completion routine or APC function is not executed.

A completion routine is queued when the [ReadFileEx][5] or [WriteFileEx][5] function in which it was specified has completed. The function returns and the completion routine is called only if *bAlertable* is **TRUE**, and the calling thread is the thread that initiated the read or write operation. An APC is queued when you call [QueueUserAPC][5].

这不像我的情况听起来

  • 我没有使用ReadFileEx
  • 我没有打电话给QueueUserAPC

但这并不一定会阻止我随意尝试并希望它们能奏效:

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      //if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, True) //wait 5000 ms, alertable

         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

但它不起作用。

所以我问 Stackoverflow

我可以使用GetOverlappedResultEx 模拟同步ReadFile 操作但 超时吗?

我敢肯定,我最终会得到一个涉及消息传递计时器(或者是线程计时器?或者是警报计时器?和事件?和我自己的事件?或者重叠中的事件?)。但我宁愿使用 good 解决方案,而不是 my 解决方案。

【问题讨论】:

  • WaitForSingleObject(overlapped.hEvent, 5000) ?
  • 如果您愿意将自己限制在 C++20,您可以使用协程来做到这一点。 Asynchronous timeouts made easy 解释了如何使用预建的wait_for 功能。该实现基本上按照 Jonathan Potter 的建议进行。
  • @JonathanPotter - 但GetOverlappedResultEx 并做内部WaitForSingleObjectEx(overlapped.hEvent, dwMilliseconds, bAlertable)
  • 除了对GetOverlappedResultEx 的调用不会在 5 秒(5,000 毫秒)内返回。相反,存储子系统需要 10-20 分钟才能返回故障。 - 这不可能。你在这里犯了一些错误
  • GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) 不会迟于 5 秒后返回,如果您说 而是继续需要 10-20 分钟 - 这是您的电话 @987654348 中的 100% 错误@ - 此 api 将仅等待来自重叠且独立于文件的事件。所以不能超过你的等待超时

标签: winapi overlapped-io getoverlappedresult


【解决方案1】:

我可以模拟一个同步的ReadFile 操作但是有一个超时, 使用GetOverlappedResultEx?

是的,你可以,就像你一样,已经尝试过了。这不是模拟。这将是完全同步的文件读取。因为同步读取 - 这是异步读取 + I/O 完成时就地等待。所以代码可以是下一个:

ULONG ReadFileTimeout(HANDLE hFile, 
               PVOID lpBuffer, 
               ULONG nNumberOfBytesToRead, 
               PULONG lpNumberOfBytesRead, 
               PLARGE_INTEGER ByteOffset, 
               ULONG dwMilliseconds)
{
    OVERLAPPED ov;
    ov.Offset = ByteOffset->LowPart;
    ov.OffsetHigh = ByteOffset->HighPart;

    ULONG dwError = NOERROR;

    if (ov.hEvent = CreateEvent(0, 0, 0, 0))
    {
        dwError = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &ov) ? NOERROR : GetLastError();

        if (dwError == ERROR_IO_PENDING)
        {
            dwError = GetOverlappedResultEx(0/*yes, not need hFile*/, 
                &ov, lpNumberOfBytesRead, dwMilliseconds, FALSE) ? NOERROR : GetLastError();
        }

        CloseHandle(ov.hEvent);
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}

请注意,您必须(在异步文件的情况下)始终显式使用字节偏移量,从中读取数据。

GetOverlappedResultEx 通常接下来执行 - 检查 OVERLAPPED - I/O 是否完整(只需将 InternalSTATUS_PENDING 进行比较),如果没有(仍然是 Internal == STATUS_PENDING),您需要等待 - 它从OVERLAPPED 调用WaitForSingleObjectExhEvent 并转移dwMillisecondsbAlertable。如果hEvent发出信号(WAIT_OBJECT_0WaitForSingleObjectEx返回)GetOverlappedResultEx决定I/O完成并从OVERLAPPED返回结果,否则返回适当的错误。

但如果ReadFile 本身不返回控制并等待 - 这里不可能做点什么。这意味着驱动程序开始等待您的线程。并且没有任何办法打破这个司机的等待。看起来这正是你的情况。当您使用异步文件时 - 驱动程序不能等待,但是.. 有时(写得不好的驱动程序)这可能是

【讨论】:

    猜你喜欢
    • 2015-01-18
    • 2019-10-28
    • 2021-08-31
    • 2016-11-16
    • 2012-04-19
    • 2012-09-20
    • 1970-01-01
    • 2015-02-02
    相关资源
    最近更新 更多