如何判断ReadFileEx()重叠I / O的时间已经完成?

时间:2011-12-05 14:47:01

标签: windows winapi overlapped-io

HasOverlappedIoCompleted()不适用于以ReadFileEx()WriteFileEx()开头的异步I / O.底部的代码片段演示了这一点。在此示例中,ReadFileEx()从没有输入的管道读取,因此读取将无法完成。但HasOverlappedIoCompleted()返回TRUE。如果我将调用更改为重叠的ReadFile(),则HasOverlappedIoCompleted()会按预期返回FALSE。

我的问题是:如何在不依赖回调本身的情况下找出带回调的重叠I / O请求是否已完成?在我的应用程序中,APC可能已经排队,但不一定必须已经运行,因为应用程序可能还没有等待处于警报状态。

感谢。

(注意GetOverlappedResult()没有帮助 - 它也返回TRUE。)

更多背景:在示例中我使用ReadFileEx(),因为很容易证明问题​​。在我的应用程序中,我在管道实例上反复调用WriteFileEx()。如果之前的WriteFileEx()尚未完成,则必须删除消息而不是发送消息(我必须在同一个管道实例上没有多个挂起写入),但是如果是前一个{ {1}} 已经完成,然后我必须启动下一个,即使完成回调尚未运行

编辑:问题情景说明

  1. 线程进入可警告状态(一个读取APC排队)。
  2. 读取APC开始:它将WriteFileEx()排队并设置'write pending'标志。然后它将ReadFileEx()排队。
  3. 主线程开始工作(不可警告)。
  4. 排队的阅读完成。
  5. 排队的写入完成(读取后)。
  6. 主线程进入可警告状态。
  7. 读取APC首先在队列中,因此首先运行:它查看“写入挂起”标志,因为它仍然设置,所以它会丢弃写入。实际上虽然WriteFileEx()已经完成,但它还没有调用它的APC,因为ReadFileEx()首先完成了。
  8. 我想从操作系统中找出WriteFileEx()是否已实际完成,而不是测试我的自定义'write pending'标志,即使APC尚未运行。


    WriteFileEx()

2 个答案:

答案 0 :(得分:1)

你要求的行为对我来说似乎不对,因为它涉及竞争条件。只有在您仍在发送消息B时收到消息A时才会出现此问题。目前,A始终被忽略,即不会发送其他消息。当且仅当服务器在A到达和B完成之间的间隔期间忙于处理工作时,您尝试获取的行为将导致发送附加消息。我认为你应该总是忽略A或者在B完成后总是发送响应。

但是,如果您确定它是您想要的,则可以获得此行为。一种解决方案是从ReadFileEx完成例程调用QueueUserAPC,将调用排队到发送回复的函数。由于APC以FIFO顺序运行,因此新的APC肯定会在WriteFileEx已经排队的APC之后运行。

根据细节的不同,完成例程设置标志或将项目添加到队列,让主循环完成实际工作可能更清晰。

如果您需要轮询APC(例如,因为您的主循环没有任何自然发生的等待操作),您可以在超时为0的虚拟事件上使用WaitForSingleObjectEx。事件是否无关紧要是否发出信号,仍会调用所有排队的APC。

答案 1 :(得分:1)

我已经改变了你的代码以实现CALLBACK。 要使此示例起作用,管道缓冲区长度不应为0。 另外,我认为管道的两端都应该设置OVERLAPPED标志。

#include <Windows.h>
#include <stdio.h>
#include <assert.h>

#define BUFFSIZE 100
#define MYPIPE   "\\\\.\\pipe\\testpipe"

typedef struct {
  OVERLAPPED serverOvlp;        // Overlapped should always be first in structure
  CHAR       buffer[20];
} OVLP;


VOID CALLBACK readComplete(DWORD err, DWORD bytes, LPOVERLAPPED ovlp)
{
  OVLP *temp = (OVLP *) ovlp;
  printf("readComplete  err=%d  bytes=%d  buffer=%s\n", err, bytes, temp->buffer);
}

int main(void)
{
  HANDLE     hServer;
  HANDLE     hClient;

  OVLP       oServer;

  DWORD      bytes;
  CHAR       ClientBuffer[20] = "Test message";

  hServer = CreateNamedPipe(MYPIPE, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
                            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                            PIPE_UNLIMITED_INSTANCES, BUFFSIZE, BUFFSIZE, 5000, NULL);

//-------------------------------------- CLIENT 
  hClient = CreateFile(MYPIPE, GENERIC_READ | GENERIC_WRITE,
                       0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

  WriteFile(hClient,ClientBuffer,strlen(ClientBuffer)+1,&bytes,NULL);

//-------------------------------------- SERVER
  ConnectNamedPipe(hServer, &oServer.serverOvlp);
  if (HasOverlappedIoCompleted(&oServer.serverOvlp)) assert(GetLastError() != 0 );
  puts("Client Pipe connected\n");

  ReadFileEx(hServer, oServer.buffer, sizeof(oServer.buffer), (LPOVERLAPPED)&oServer, readComplete);

  SleepEx(INFINITE,TRUE);       // Creates an alertable event so CALLBACK is triggered

  if (HasOverlappedIoCompleted(&oServer.serverOvlp)) {
    puts("Completed");
  } else {
    puts("Not completed");
  }

  return EXIT_SUCCESS;
}