Re: Weird Exception by Peter
Peter
Thu Jun 24 16:18:44 CDT 2004
I don't see the problem offhand ... but i've got some notes for future
designs:
You can wait on a timer using KeWaitForSingleObject(). You could wait on
the timer and the event both by calling KeWaitForMultipleObjects(). So you
don't need to test the state of the thread and then spin checking the timer
state.
As a general rule when you find yourself writing a spin loop like the one
you have below you've probably chosen a wrong design for Windows. Between
waiting on objects and proper use of DPCs and work items i find that
spinning is rarely necessary (except occasionally when i'm directly touching
hardware and then there are Ke routines to spin for me).
You can also clear the timer by calling KeCancelTimer() so you don't need to
spin after the terminate event is set in order to rundown the timer. It's
possible the problem you're seeing has to do with the timer remaining in the
timer lists even after it's become signalled for some time unless you call
KeCancelTimer() before you free it (i'm guessing)
-p
--
This posting is provided "AS IS" with no warranties, and confers no rights.
"Michael I. McCully" <Mike.McCully@pobox.com> wrote in message
news:I8ydnUw1H_ZIpEbd4p2dnA@texas.net...
> Calvin,
>
> Thanks for the post.
>
> >> I'm having a strange problem in my WDM driver. I'm using a kernel
> >>thread to check something at 100 mS intervals with a KTIMER. The timer
> >>is allocated from the nonpaged pool when the thread starts and freed
> >>before it is terminated, and the thread itself runs for a second or
more.
> >
> >
> > I don't quite understand why a timer would be necessary and how it's
> > related to the thread. If the thread is to poll every 100ms, I would do
> > something like this:
> >
> > VOID
> > ThreadMainRoutine(
> > IN PDEVICE_DATA pDevExt
> > )
> > {
> > LARGE_INTEGER time;
> > time.QuadPart = MILLISECONDS(100);
> > while (!pDevExt->kill) {
> >
> > KeDelayExecutionThread(KernelMode,FALSE,&time);
> > DoMyPollingHere(pDevExt)
> > }
> > PsTerminateSystemThread(STATUS_SUCCESS);
> > }
> You're right, I can use KeDelayExecutionThread() and was planning to
> change the driver to do that, but when I wrote the driver last year, the
> documentation I was reading pointed me to using a KTIMER. I didn't know
> about KeDelayExecutionThread(). Even at the time I thought using a
> KTIMER was too complicated, but that's sometimes the case when
> programming under Windows (as you probably already know).
> At this point, I'm just seeing if I can figure out why this crash is
> happening, and it isn't happening in most places where the driver is
> installed.
>
>
> >> For some unknown reason, I started getting a 0xC2 bugcheck under
> >>Windows 2K after about four executions of the thread. I'm only
> >>deallocating the KTIMER once at the end of the thread,
> >
> >
> > What do you mean by deallocating the KTIMER?
> See the thread routine below. I originally worked from some example
> code that allocated the KTIMER from the nonpaged pool.
>
> >>but since 0xC2
> >>can be generated by double deallocations, I decided to see if the
> >>bugcheck was occurring because I was allocating and deallocating the
> >>KTIMER every time the thread executed. I changed the driver so that the
> >>KTIMER was allocated when the device was created and deallocated when
> >>the device was removed, and when I did that the 0xC2 bugcheck turned
> >>into a 0x0A bugcheck.
> >
> >
> > Can you show the thread routine? I'm trying to understand its
interaction
> > with the timer.
> Here are the essentials of the thread routine. It's a little bit messy,
> so I hope you'll excuse me.
> VOID Thread(DEVICE_EXTENSION *pdx) {
> PKTIMER pkTimer;
> LARGE_INTEGER Timeout, WaitTime;
> int TimerEnabled = 0;
> NTSTATUS WaitStatus;
>
> // Set up a system timer. MustSucceed in case running under XP and low
> // on resources.
> pkTimer = ExAllocatePool(NonPagedPoolMustSucceed,sizeof(KTIMER));
> KeInitializeTimer(pkTimer);
> // Set Timeout to zero so that KeWaitForSingleObject will
> // return immediately
> Timeout.QuadPart = 0;
> // Set WaitTime to 100 mS (or whatever)
> WaitTime.QuadPart = -1000000;
> // If KeWaitForSingleObject() returns STATUS_SUCCESS,
> // then thread execution is done and it's time to exit
> // the loop
> WaitStatus =
>
KeWaitForSingleObject(&pdx->evKillThread,Executive,KernelMode,FALSE,&Timeout
);
> while (WaitStatus != STATUS_SUCCESS) {
> // Enable the KTIMER if necessary
> if (!TimerEnabled) {
> KeSetTimer(pkTimer,WaitTime,NULL);
> TimerEnabled = 1;
> }
> // Otherwise, if the timer has timed out, then do something
> else if (KeReadStateTimer(pkTimer) == TRUE) {
> .
> .
> .
> TimerEnabled = 0;
> }
> WaitStatus =
>
KeWaitForSingleObject(&pdx->evKillThread,Executive,KernelMode,FALSE,&Timeout
);
> }
> // Wait for timeout before freeing the KTIMER
> do
> ;
> while (KeReadStateTimer(pkTimer) != TRUE);
> ExFreePool(pkTimer);
> // Bye bye
> PsTerminateSystemThread(STATUS_SUCCESS);
> }
>
> Here's the code that starts the thread under Win2K or XP:
>
> NTSTATUS StartThread(DEVICE_EXTENSION *pdx) {
> NTSTATUS status;
> HANDLE hThread;
>
> KeInitializeEvent(&pdx->evKillThread,NotificationEvent,FALSE);
> status =
>
PsCreateSystemThread(&hThread,THREAD_ALL_ACCESS,NULL,NULL,NULL,(PKSTART_ROUT
INE)Thread,pdx);
> if (!NT_SUCCESS(status))
> return status;
>
>
ObReferenceObjectByHandle(hThread,THREAD_ALL_ACCESS,NULL,KernelMode,(PVOID)&
pdx->ktThread,NULL);
> ZwClose(hThread);
> return STATUS_SUCCESS;
> }
>
> ...and here's the code that stops it:
>
> VOID StopThread(DEVICE_EXTENSION *pdx) {
> KeSetEvent(&pdx->evKillThread,0,FALSE);
>
KeWaitForSingleObject(&pdx->ktThread,Executive,KernelMode,FALSE,NULL);
> ObDereferenceObject(pdx->ktThread);
> }
>
> The call in StopThread() to KeWaitForSingleObject() appears to be where
> the crash is occurring.
>
>
> >> The kicker is this: I changed the driver back to its original
> >>state except that I left a declaration for a KTIMER pointer in my device
> >>extension--that is, there is space for a KTIMER pointer in the device
> >>extension, but it is unused--and I still get the 0x0A bugcheck. If I
> >>take the declaration out, the bugcheck changes back to 0xC2.
> >
> >
> > Without a stack dump, it's hard to tell what went wrong.
> I don't have a good stack dump yet.
>
> > You mentioned space for "KTIMER pointer", I hope you meant space for the
"KTIMER structure".
> I tried it both ways--a KTIMER structure in the device extension and a
> pointer to one that gets allocated by AddDevice() and freed by
> RemoveDevice(). In both cases, I got the 0x0A exception.
>
>
> >> One final note is this. The 0x0A exception is occurring inside of
> >>KeWaitForSingleObject(), which is what I'm using to wait for the thread
> >>to end under Win2K. 0x0A means something is ocurring at the wrong IRQL,
> >>and when the exception occurs the IRQL is 2--DISPATCH_LEVEL. Just
> >>before KeWaitForSingleObject() is called, I did a KdPrint() that showed
> >>the IRQL as 1--APC_LEVEL.
> >
> >
> > KeWaitForXxxx will raise irql to dispatch_level at some point
internally.
> > Make sure you are waiting for the thread object (not thread handle)
> I figured KeWaitForSingleObject() was raising the IRQL, and as you can
> see from the code above, I'm not waiting on the handle. Thanks again
> for your comments. They are appreciated.
>
> Mike
> mmccully@ionwerks.com
>