Domanda

HasOverlappedIoCompleted() non funziona sugli I/O asincroni iniziati con ReadFileEx() E WriteFileEx().Lo snippet di codice in basso lo dimostra.In questo esempio, ReadFileEx() legge da una pipe che non ha input, quindi la lettura non verrà completata.Ma HasOverlappedIoCompleted() restituisce VERO.Se cambio la chiamata in sovrapposta ReadFile() Poi HasOverlappedIoCompleted() restituisce FALSE come previsto.

La mia domanda è:Come posso scoprire se una richiesta di I/O sovrapposta con callback è stata completata, senza fare affidamento sulla callback stessa?Nella mia applicazione, l'APC potrebbe essere stato messo in coda ma non è necessariamente necessario che sia già stato eseguito perché l'applicazione potrebbe non essere ancora in attesa in uno stato di avviso.

Grazie.

(Nota GetOverlappedResult() non aiuta: restituisce anche TRUE.)

Un po' più di background:Nell'esempio che sto usando ReadFileEx() perché è facile dimostrare il problema.Nella mia domanda sto chiamando WriteFileEx() ripetutamente su un'istanza di pipe.Se il precedente WriteFileEx() non ha ancora completato I dovere rilasciare il messaggio anziché inviarlo (non devo avere più di una scrittura in sospeso sulla stessa istanza della pipe), ma se il precedente WriteFileEx() ha completato poi I dovere inizia il prossimo, anche se la richiamata di completamento non è ancora stata eseguita.

Modificare:Una descrizione dello scenario problematico

  1. Il thread entra in uno stato di avviso (con un APC di lettura in coda).
  2. Inizia la lettura dell'APC:Mette in coda un WriteFileEx() e imposta un flag di "scrittura in sospeso".Quindi mette in coda un ReadFileEx().
  3. Il thread principale inizia a funzionare (non segnalabile).
  4. La lettura in coda viene completata.
  5. La scrittura in coda viene completata (dopo la lettura).
  6. Il thread principale entra in uno stato di avviso.
  7. L'APC di lettura è il primo nella coda, quindi viene eseguito per primo:Controlla il flag "scrittura in sospeso" e poiché è ancora impostato elimina la scrittura.In effetti, sebbene WriteFileEx() ha completato, semplicemente non ha ancora chiamato il suo APC perché ReadFileEx() è stato completato per primo.

Invece di testare il mio flag personalizzato di "scrittura in sospeso", voglio scoprire dal sistema operativo se WriteFileEx() è stato effettivamente completato, anche se l'APC non è ancora stato eseguito.


#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;
}
È stato utile?

Soluzione

Il comportamento che chiedi mi sembra sbagliato, perché implica una race condition.Il problema si verifica solo se ricevi un messaggio A mentre stai ancora inviando un messaggio B.Attualmente A viene sempre ignorato, cioè non viene inviato alcun messaggio aggiuntivo.Il comportamento che stai tentando di ottenere comporterebbe l'invio di un messaggio aggiuntivo se e solo se il server era occupato nell'elaborazione del lavoro durante l'intervallo tra l'arrivo di A e il completamento di B.Penso che dovresti ignorare sempre A o inviare sempre una risposta una volta completato B.

Tuttavia, puoi ottenere questo comportamento se sei sicuro che sia quello che desideri.Una soluzione potrebbe essere quella di chiamare QueueUserAPC dalla routine di completamento ReadFileEx, per accodare una chiamata a una funzione che invia la risposta.Poiché gli APC vengono eseguiti in ordine FIFO, il nuovo APC verrà sicuramente eseguito dopo l'APC che WriteFileEx ha già messo in coda.

A seconda dei dettagli, potrebbe essere più pulito per entrambe le routine di completamento impostare flag o aggiungere elementi a una coda e fare in modo che il ciclo principale svolga il lavoro vero e proprio.

Se è necessario eseguire il polling per gli APC (ad esempio perché il ciclo principale non prevede operazioni di attesa naturali) è possibile utilizzare WaitForSingleObjectEx su un evento fittizio con un timeout pari a 0.Non importa se l'evento viene segnalato o meno, verranno comunque chiamati tutti gli APC in coda.

Altri suggerimenti

Ho modificato leggermente il tuo codice per ottenere il CALLBACK.Affinché questo esempio funzioni, la lunghezza del buffer della pipe non deve essere 0.Inoltre, penso che entrambe le estremità della pipe dovrebbero avere il flag OVERLAPPED impostato.

#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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top