Under what circumstances will GetProcessTimes report that a process exited before it was created?

A customer reported that their automation started reporting strange values:

HANDLE process = ...;
FILETIME creation, exit, kernel, user;
if (GetProcessTimes(process, &creation,
                    &exit, &kernel, &user))
   // use the values of creation, exit, kernel, and user

Their test automation reported that the process had an exit time earlier than its creation time. How is this even possible? This apparent violation of causality was causing their automation to stop working.

If you take a closer look at the documentation for Get­Process­Times, it says for the lpExit­Time parameter:

If the process has not exited, the content of this structure is undefined.

What probably is happening is that the process being monitored has not yet exited, so the exit time is undefined. The undefined value might be less than the creation time. It might be greater than the creation time. Heck, if you're really (un)lucky, it might even be equal to the creation time.

My guess is that the "undefined" result is coming from uninitialized stack garbage, and the nature of uninitialized stack garbage is that while it is unpredictable, it can also often be consistent over short stretches.

Comments (23)
  1. Cezary says:

    FILETIMEs are not based on a monotonic timebase, so a NTP step at just the right time could also account for that, no? (Yes, NTP prefers gradual ramp changes and not steps for just this reason, but sometimes you have a step.)

  2. 12BitSlab says:

    Sorry for the off-topic comment — Happy 33rd Birthday to Windows 1.0!

    1. 12BitSlab says:

      That is, it was 33 years ago that billg announced Windows 1.0 to the world — not when it shipped.

    2. Brian_EE says:

      > 33rd Birthday…

      That should be “Happy 0x21st Birthday”. Hit the bars!

      1. 12BitSlab says:

        I like your way of thinking!

  3. DWalker07 says:

    The undefined process exit time might be January 1, 1980, and it will match the file creation times that you mentioned a couple of days ago!

  4. George says:

    It’s not like there seems to be a method that you can call that would give Process state, is there? If you got the handle to the process as close as is shown in the sample code, it would be highly unlikely, but somewhat of a race condition, for the ‘exitFileTime’ parameter to have been set. And if you had to call 2 different methods to learn this, there would be an inherent race condition. There might (or might not) be some hope in initializing the FILETIME exit structure to some value, and test whether it has been changed by calling GetProcessTimes, to attempt to learn all of this in one method call.

    1. ZLB says:

      GetProcessExitCode() can be used to find out if a process is still running. It returns 254 (STILL_ACTIVE) if it is not exited yet.

      …of course there is nothing to stop a process returning 254 as its exit code though….

      1. SimonRev says:

        You can check if a process is still running by
        WaitForSingleObject(, 0) == WAIT_TIMEOUT // Little Program error checking warning here, unless you turned on CrashMyProcessWhenInvalidHandlesAreUsed.

        While the race condition cannot be fully avoided, if you call WaitForSingleObject first then things will be fine since you will assume that the exit time is invalid. At least this is a safe race condition.

        1. The handle remains valid until you close it, so there is no race condition on the handle. There is a race condition on the exit state, but that race is pre-existing because app could exit immediately after you call GetProcessTime.

          1. Joshua says:

            I don’t see the race condition. Call GetExitCodeProcess() then call GetProcessTime(). If GetExitCodeProcess() returned STILL_ACTIVE ignore the value shoved into lpExitTime.

          2. JDG says:

            @Joshua The race condition is that you might be ignoring a valid value because the process actually exited right in between the calls. That there is a race condition is inarguable, as there is no single, atomic function that tells you definitively whether the process is running *and* what its exit code is, if applicable. The question is whether it is okay to potentially, in very rare cases, treat an exited process’ GetProcessTimes results as though they came from a live process. To me, intuitively, the answer would seem generally to be “yes”. :-)

      2. George says:

        Apparently, it’s GetExitCodeProcess, NOT GetProcessExitCode. And the correct value for STILL_ACTIVE seems to be 259, not 254, unless the MSDN article on that function is not correct. And having to use a separate function to query status leaves a race condition between getting status and getting process times (or vice versa.) Best you could do with that is to check GetExitCodeProcess, query for process times, GetExitCodeProcess again, and use the times if and only if the two status codes were the same.

        In an automation controller, you are probably looping thru a largeish list of processes that the controller likely started to see if they have ended yet. So either you need to spin up a bunch of threads to wait one per process, or poll efficiently.

        1. You’re working too hard. GetProcessTime already has an inherent race condition. GetExitCodeProcess does nothing to address the race condition inherent in GetProcessTime.

          1. JDG says:

            Assuming you check for the process having exited *before* retrieving the times, the worst case for the race condition is that you don’t use the exit time for a process that exited only a few nanoseconds ago. If the call and process exit had happened in the other order, it would have been meaningless data anyway. I’d venture that a program that handles both cases can handle getting the data the next time it tries. It’s a little bit like a speed of light constraint on communication — one way to look at it is that the process *has* exited, but news of the exit hasn’t yet reached your app and its calls to WaitForSingleObject and GetProcessTimes. :-)

      3. JDG says:

        Yes. You should NEVER use GetExitCodeProcess to determine *if* a process has exited. There’s a reason the function is called GetExitCodeProcess and not GetHasProcessExited. A still-running process gives a return value of STILL_RUNNING, but not vice versa. Always use WaitForSingleObject to test if a process is running — it can be made to return immediately with a zero millisecond timeout, as others have noted.

      4. Killer{R} says:

        Futhermore, process termination is not atomic. ExitProcess first sets exit code with NtTerminateProcess(NULL, ExitCode) – but this doesn’t actually terminate process, and only then process actually terminated with NtTerminateProcess(-1, ExitCode). So only WaitForSingleObject is guaranteed way to make sure process really terminated at some moment.
        PS: Above behaviour true for Windows till Win7. Didnt verify if its true for later versions.

    2. JM says:

      “It’s not like there seems to be a method that you can call that would give Process state, is there?” If WaitForSingleObject(process, 0) returns 0, the process has terminated. Of course, this is rarely useful since it would imply polling. Usually you just wait for termination.

      1. Joshua A. Schaeffer says:

        > Of course, this is rarely useful since it would imply polling.

        This invites an old question of mine, maybe better for a Microsoft engineer: why is there no version of WaitForSingleObject() that sends a packet to an IOCP? It gets unsettling seeing threads doing nothing but waiting on 64 handles; that can’t possibly scale or integrate with the thread pool’s IOCP…and there seems to be a ton of transitions to convert that signalled object into a high-level work item. Or how about this… why can’t an IOCP handle be made to signal when it has a queued item? Sometimes you have to use an IOCP for job notifications, but you don’t want to have a dedicated thread pumping it.

        I’m getting off-track but I blended it in as best I could. :)

  5. henke37 says:

    And here I thought it was time for a daylights savings reminder. Because I bet that changing the system clock will allow this insanity.

    1. Brian_EE says:

      Now that you mentioned it, we didn’t get our usual semi-annual “daylight saving time change” OldNewThing article about handling time in Windows. Perhaps Raymond has run out of ideas for articles on that topic, or he just forgot. Similarly, the quarterly link clearance posts are no longer quarterly.

      1. ZLB says:

        Ask for a refund.

  6. Frank says:

    UB causing time travel again.

Comments are closed.

Skip to main content