Spawning child processes under Windows and worried about opened handles’ counter going through the roof, even though they all gently quit and are no longer listed in system process list?
Oh you should!
I’ve been working on a certain piece of software for over a year now and it’s not happened until today that we spotted and fixed a gruesome bug hiding in a location that is executed very, very often. It would have probably stayed there and happily lived ever after if it wasn’t stress tests that we finally got to run in their full glory for the very first time. I don’t want to/am not allowed to get into too much level of detail, so let’s just say we were extremely surprised to find out that our lovely & shiny piece of software caused Windows 7 to come to a complete halt after 2 hours of non-stop tests (having first crashed Google Chrome and Process Explorer I usually keep opened). Worse, the use case we were stress-testing was expected to happen every single time somebody attempted to launch our tool. If they were to use the product for longer than an hour, it wouldn’t lead anywhere good.
Topping it all off, it’s probably not going to come as a surprise that we had only 6 work-days left till project dead-line 🙂 That’s life.
Having panicked for an hour or two and having tested a rather hacky workaround that could awfully backfire and lead to ugly side effects, not to mention it would require off-site person’s effort, I launched the tests again and started sniffing around. Deferred crash, slow-down effect over time, overall system damage – why? What could be the reason?
At moments like these it’s very useful to have Process Explorer at hand (okay, a cup of coffee is also highly recommended), just to look at a variety of counters it shows you. They provide you with a lot of insight into how every single active process behaves. Recently they even implemented a new feature that allows you to check GPU activity and memory usage! If you happen to have a spare monitor that you don’t know what to do with (right 🙂 ), keep the tool full-screen. Trust me: I learned my lesson the hard way today 🙂
Any road, when I focused on the stress-test process that had been running for a few minutes, I instantly noticed that there were thousands and thousands of zombie process and thread handles. The processes they represented were long gone, not to mention the threads which should have died a long time ago, but no. How come! You don’t need to release a child process handle under Windows, you don’t get one when doing a CreateProcessA() call, do you?
Well, for Linux programmers it’s probably an easy spot – yes, you do. However, under Windows, the handle is not returned by stack so it’s a potential banana skin. Instead, you need to dive into process_info structure, that you are expected to provide a pointer to as one of the arguments, and there you’ll find it.
Also, judging by what MSDN page for the function says, it’s a good idea to release main thread handle for the newly spawned process, because otherwise reference counting may hit you hard.
After we started giving the child process’ handles the care they deserved, the problem disappeared and we could call it a day 🙂
All in all, the lesson learned today is that stress tests are vital. And the easier you automate them, the better.