fabric-engine/PublicDev — pull request #89 by jcgagnon (2012-04-20 18:19:43+00:00)
Title: Windows Cond fix

@pzion : Changed MT::Cond to use Windows' CONDITION_VARIABLE (Vista or later). This fixed the performance problem (explained below) but caused new issues. The problem is that when a DLL is in the process to exit, waiting for worker threads to finish cause a deadlock; ThreadPool::terminate() was causing a deadlock. For Python, I could simply add a "finalize()" callback to terminate the threadpool before the DLL exists. However, for Node, I couldn't. I posted to the NodeJS dev list and it was confirmed there was nothing; one suggested to use stdlib's atexit() but that is too late too. After doing some reading (ref: see my code comment), I decided to fix that by having, only for Node in Windows, the worker threads waiting for a max of 200 ms before checking if the thread pool is exiting (m_running). This worked well.

More details about the performance problem:
After a lot of investigations, tracing and dumping, I finally understood quite well what was going on with the older implementation:
- To emulate a "broadcast" behavior with a single Event (which is like a single open or closed gate), the system was trying to "group" the threads what need to stop waiting
- The code would work well if the gate was closed for a while (ResetEvent) before being reopened (SetEvent)
- The problem is that in fact the gate was reopened before all the workers have been wake up
- This made that a "limit case" was constantly happening, which is that older threads were not able to leave the gate fast because threads for the new generation were already there, actively waiting for the gate to be closed (the "while" loop with myGeneration == windowsGeneration). Since those new generation threads were constantly taking the Critical Section, they were preventing the older ones to leave that same loop (kind of starvation)
- This was constantly happening because as soon as a thread was out of the gate, it often came back right away, looking for new work (Threads' "while( m_running )" loop)

All this was leading to a 100% CPU usage for 4 CPUS, with 30% of the performance of a single CPU. Quite bad.