Never Sleep(0) in an Infinite Loop
I ran into a problem installing some COM+ components today. The installer was using Regsvcs.exe to register each COM+ component. I noticed after a while that the installer wasn’t making any progress and that my dual-proc system was stuck at 50% CPU utilization. I attached a debugger to the offending process, regsvcs, and found that it was stuck in the following infinite loop (disassembly courtesy of Reflector):
internal void System.EnterpriseServices.CatalogSync.Wait() { if (this._set) { RegistryKey key1 = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes\CLSID"); while (true) { int num1 = (int) key1.GetValue("CLBVersion", 0); if (num1 != this._version) { break; } Thread.Sleep(0); } this._set = false; } }
There are two severe problems with this code.
- The loop should time out. There must be some reasonable limit after
- which you can incontrovertibly say that something must have gone wrong, and throw an exception. There has to be some way to terminate a loop.
- Never use Sleep(0) in a loop. Sleep(0) yields the processor only
- if there’s a runnable thread. If there isn’t, Sleep(0) will return immediately. If the code is sitting in a tight loop, the net effect is that it will maximize the CPU until the thread’s quantum is exhausted. There are no other runnable threads, so the scheduler immediately starts this thread again. This code will run until your CPU burns out.
(And, yes, I have committed both of these sins in shipping code. Why do you ask?)
I don’t know what the calling code is doing or why CLBVersion isn’t being altered by some other thread or process. I had to use RegEdit to modify this value to get the loop to terminate, whereupon RegSvcs immediately did its work and terminated. And then it started all over again, with the next invocation of RegSvcs on another COM+ component. I don’t know if the components are really installed properly. I had to leave at that point.