A customer found that their program was crashing with this stack:
ntdll!TppRaiseHandleStatus ntdll!TppSetupNextWait ntdll!TppWaitCompletion ntdll!TppWorkerThread kernel32!BaseThreadInitThunk ntdll!RtlUserThreadStart
"Since none of our code is on the stack, it is unclear what may have gone wrong here."
A naming convention followed by many teams is to
come up with a short prefix for a component and put that prefix
on every function in that component.
all of the I/O manager functions
Io, then a capital letter.
Furthermore, functions intended
to be called from other components use the unadorned prefix,
and functions that are internal to the component add a
p (for private) to the prefix.
For example, all of the functions internal to the I/O manager
Iop, then a capital letter.
(An older convention is to use the lowercase prefix
to indicate internal functions.
Under the older convention, the internal functions in the I/O manager
would begin with
io followed by a capital letter.)
Given that information about naming conventions,
you can guess that the functions prefixed
are functions that are internal to a component whose prefix is
And your guess would be correct. This code is from the thread pool.
One of the purposes of the thread pool is to consolidate waits.
If one object wants to do something when handle A is signaled,
and another object wants to do something when handle B is signaled,
one way to do this is to have each object create a thread, and have
that thread wait on the corresponding handle.
Unfortunately, that results in two threads, and when you have
a hundred objects, this results in a hundred threads,
and that doesn't scale well.
Better would be to put one thread in charge of all the waiting,
so that it can use
That way, you need only ⌈n ∕
MAXIMUM_WAIT_OBJECTS⌉ threads to wait on
Okay, so let's see what this stack is telling us.
I am not familiar with the thread pool's internals,
so this is all educated guesswork.
It's educated by the fact that
code is not intentionally written to be impossible to understand.
if a function is called
it probably sets up the next wait.
The thread pool started its own worker thread with
TppWorkerThread; that makes sense.
And then a
completed, which was handled by
After handling that wait,
the thread wants to go back
to waiting until the next handle becomes signaled,
which is done by the function
But something went wrong, and it needs to raise a status,
which is done by the
And that's where we crashed.
What could go wrong in
The most likely reason in my opinion is that the list of handles
it is being asked to wait on is no longer valid,
probably because an application closed a handle while it was still
registered with the thread pool,
so when the thread pool tried to wait on it,
it got an
If this handle is closed while the wait is still pending, the function's behavior is undefined.
The customer wrote back that they have another crashing stack that points at their application code, so they have something else to work with to help pin down where the handle went bad.