I have a loop that invokes ZwReadFile multiple times in a loop, each time
specifying an ApcRoutine for completion notification. After submitting all
of the reads, I wait on an event that is signalled by the last APC to
execute (the ApcContext contains a pointer to a shared variable used to
track outstanding requests).
What I’ve found is that my APC is sometimes not invoked when a read
completes, even though ZwReadFile returns STATUS_SUCCESS. In these cases,
the IO_STATUS_BLOCK will have been filled out correctly
(Status=STATUS_SUCCESS, Information=expected read size), but the APC has not
been (and never will be, apparently) invoked.
Are there situations in which ZwReadFile will not invoke an APC upon
completion? Maybe there’s some path through FastIo that skips the queueing
of the APC?
Or maybe I’m doing something else wrong. A chopped down version of my code
is below.
Thanks, as always, for any suggestions.
Myk
XxxReadFileApc( )
{
context->Invoked = TRUE;
if ( InterlockedDecrement( context->ReadsOutstanding ) == 0 )
{
KeSetEvent( context->Event, 0, FALSE );
}
return STATUS_SUCCESS;
}
DoMultipleReadsAtPassiveLevel()
{
LONG readsOutstanding = 1;
KEVENT event; KeInitializeEvent( &event, NotificationEvent,
FALSE );
for ( each range to read )
{
ApcContext = ExAllocatePoolWithTag( … );
ApcContext->ReadsOutstanding = &readsOutstanding;
ApcContext->Invoked = FALSE;
ApcContext->Event = &event;
InterlockedIncrement( &readsOutstanding );
status = ZwReadFile(
FileHandle,
NULL, // event
XxxReadFileApc,
ApcContext, // context
&ApcContext->IoStatusBlock, …
);
if ( !NT_SUCCESS(status) )
{
InterlockedDecrement( &readsOutstanding );
}
} // for (each section)
if ( InterlockedDecrement( &readsOutstanding ) != 0 )
{
// at this point, we will (sometimes) hang forever with
readsOutstanding stuck
// at a non-zero value. Inspection of the APC context structures
shows that
// sometimes one has not been invoked (Invoked==FALSE), even though
its
// associated IO_STATUS_BLOCK looks like it completed successfully.
KeWaitForSingleObject( &event, UserRequest, KernelMode, FALSE,
NULL );
}
// …
}