Как узнать, когда ReadFileEx() завершил ввод-вывод с перекрытием?
HasOverlappedIoCompleted()
не работает с асинхронным вводом / выводом, начатым с ReadFileEx()
а также WriteFileEx()
, Фрагмент кода внизу демонстрирует это. В этом примере ReadFileEx()
читает из канала, который не имеет ввода, поэтому чтение не будет завершено. Но HasOverlappedIoCompleted()
возвращает TRUE. Если я изменю вызов на перекрывающийся ReadFile()
затем HasOverlappedIoCompleted()
возвращает FALSE, как и ожидалось.
Мой вопрос: как я могу узнать, завершился ли перекрывающийся запрос ввода-вывода с обратным вызовом, не полагаясь на сам обратный вызов? В моем приложении APC, возможно, был поставлен в очередь, но не обязательно должен был еще запускаться, потому что приложение, возможно, еще не ожидало в состоянии оповещения.
Благодарю.
(Обратите внимание, что GetOverlappedResult() не помогает - он также возвращает TRUE.)
Немного больше предыстории: в примере я использую ReadFileEx()
потому что это легко продемонстрировать проблему. В моем заявлении я звоню WriteFileEx()
неоднократно на экземпляре трубы. Если предыдущий WriteFileEx()
еще не завершено, я должен отбросить сообщение, а не отправить его (у меня не должно быть более одной ожидающей записи в одном экземпляре канала), но если предыдущая WriteFileEx()
После завершения я должен начать следующий, даже если обратный вызов завершения еще не запущен.
Изменить: описание сценария проблемы
- Поток переходит в состояние оповещения (с одним прочитанным APC в очереди).
- Начинается чтение APC: он ставит в очередь функцию WriteFileEx() и устанавливает флаг ожидания записи. Затем он ставит в очередь ReadFileEx().
- Основной поток начинает работу (без предупреждения).
- Очередное чтение завершено.
- Запись в очереди завершается (после чтения).
- Основной поток переходит в состояние оповещения.
- 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;
}