Как узнать, когда ReadFileEx() завершил ввод-вывод с перекрытием?

HasOverlappedIoCompleted() не работает с асинхронным вводом / выводом, начатым с ReadFileEx() а также WriteFileEx(), Фрагмент кода внизу демонстрирует это. В этом примере ReadFileEx() читает из канала, который не имеет ввода, поэтому чтение не будет завершено. Но HasOverlappedIoCompleted() возвращает TRUE. Если я изменю вызов на перекрывающийся ReadFile() затем HasOverlappedIoCompleted() возвращает FALSE, как и ожидалось.

Мой вопрос: как я могу узнать, завершился ли перекрывающийся запрос ввода-вывода с обратным вызовом, не полагаясь на сам обратный вызов? В моем приложении APC, возможно, был поставлен в очередь, но не обязательно должен был еще запускаться, потому что приложение, возможно, еще не ожидало в состоянии оповещения.

Благодарю.

(Обратите внимание, что GetOverlappedResult() не помогает - он также возвращает TRUE.)

Немного больше предыстории: в примере я использую ReadFileEx() потому что это легко продемонстрировать проблему. В моем заявлении я звоню WriteFileEx() неоднократно на экземпляре трубы. Если предыдущий WriteFileEx() еще не завершено, я должен отбросить сообщение, а не отправить его (у меня не должно быть более одной ожидающей записи в одном экземпляре канала), но если предыдущая WriteFileEx() После завершения я должен начать следующий, даже если обратный вызов завершения еще не запущен.

Изменить: описание сценария проблемы

  1. Поток переходит в состояние оповещения (с одним прочитанным APC в очереди).
  2. Начинается чтение APC: он ставит в очередь функцию WriteFileEx() и устанавливает флаг ожидания записи. Затем он ставит в очередь ReadFileEx().
  3. Основной поток начинает работу (без предупреждения).
  4. Очередное чтение завершено.
  5. Запись в очереди завершается (после чтения).
  6. Основной поток переходит в состояние оповещения.
  7. APC для чтения находится первым в очереди, поэтому сначала выполняется: он просматривает флаг 'pending pending' и, поскольку он все еще установлен, сбрасывает запись. На самом деле, хотя WriteFileEx() завершена, он еще не вызвал свой APC, потому что ReadFileEx () завершился первым.

Вместо того, чтобы тестировать мой пользовательский флаг 'ожидающих записи', я хочу узнать в ОС, действительно ли WriteFileEx() завершена, даже если APC еще не запущен.


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

VOID CALLBACK readComplete(DWORD err, DWORD bytes, LPOVERLAPPED ovlp)
{
}

int main(int argc, char *argv[])
{
  HANDLE     hServer;
  OVERLAPPED serverOvlp = { 0 };
  HANDLE     hClient;
  DWORD      bytes;
  BYTE       buffer[16];
  BOOL       result;

  hServer = CreateNamedPipe("\\\\.\\pipe\\testpipe", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
                            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 
                            PIPE_UNLIMITED_INSTANCES, 0, 0, 5000, NULL);

  serverOvlp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

  ConnectNamedPipe(hServer, &serverOvlp);
  assert(GetLastError() == ERROR_IO_PENDING);

  hClient = CreateFile("\\\\.\\pipe\\testpipe", GENERIC_READ | GENERIC_WRITE,
                       0, NULL, OPEN_EXISTING, 0, NULL);

  GetOverlappedResult(hServer, &serverOvlp, &bytes, TRUE);

  /* Server starts an overlapped read */
//  result = ReadFile(hServer, buffer, sizeof(buffer), &bytes, &serverOvlp);
  result = ReadFileEx(hServer, buffer, sizeof(buffer), &serverOvlp, readComplete);

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


  return EXIT_SUCCESS;
}

2 ответа

Решение

Поведение, о котором вы просите, кажется мне неправильным, потому что оно связано с расой. Эта проблема возникает только в том случае, если вы получаете сообщение A, когда вы все еще отправляете сообщение B. В настоящее время A всегда игнорируется, т. Е. Дополнительное сообщение не отправляется. Поведение, которое вы пытаетесь получить, приведет к тому, что дополнительное сообщение будет отправлено, если и только если сервер был занят обработкой работы в течение интервала между прибытием A и завершением B. Я думаю, что вы должны либо всегда игнорировать A, либо всегда отправлять ответ после завершения B.

Тем не менее, вы можете получить такое поведение, если вы уверены, что это то, что вы хотите. Одним из решений было бы вызвать QueueUserAPC из подпрограммы завершения ReadFileEx, чтобы поставить в очередь вызов функции, которая отправляет ответ. Поскольку APC запускаются в порядке FIFO, новый APC будет определенно запускаться после того APC, который WriteFileEx уже поставил в очередь.

В зависимости от деталей, для обеих подпрограмм завершения может быть удобнее установить флаги или добавить элементы в очередь, и ваш основной цикл будет выполнять реальную работу.

Если вам нужно опросить APC (например, из-за того, что в вашем главном цикле нет никаких естественных операций ожидания), вы можете использовать WaitForSingleObjectEx для фиктивного события с тайм-аутом, равным 0. Неважно, будет ли событие сигнализировано или нет, все APC в очереди все равно будут вызываться.

Я немного изменил ваш код, чтобы получить 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;
}
Другие вопросы по тегам