23 Apr
Posted by: Christian Wimmer in: Common
While working on the logging mechanism in the RunAsSys example of the JEDI, I encountered a really strange problem on XP systems. The application worked fine on Vista system. However on Windows XP, it just stopped working with no response at all. This wasn’t the usual dead lock situation you know in multi threaded application, because there were only one thread. So what happened? I could luckily debug the program and found out that it hang on a log sequence. Going deeper, I found out that on XP a call to EnterCriticalSection hang indefinitely when it was called right after two subsequent calls to LeaveCriticalSection. Of course it was wrong. I should always leave the critical section only once for every entering. How could that happen? Just accident. I didn’t realize that the Delphi function “exit” called within try/finally, jumps into the finally block first and then exits the function.
Thus the next call to Enter will hang on XP Systems. It is in contrast to Vista where it works. So why did this happen? Maybe the internal lock may tell us more. The following output comes from this little test application:
The result shows the following output. The values in the left column are from Windows XP, where the ones on the right side come from Windows Vista.
| Version | XP | Vista |
| LockCount: | -1 | -1 |
| RecursionCount: | 0 | 0 |
| LockCount: | 0 | -2 |
| RecursionCount: | 1 | 1 |
| LockCount: | 1 | -2 |
| RecursionCount: | 2 | 2 |
| LockCount: | 2 | -2 |
| RecursionCount: | 3 | 3 |
| leave | ||
| LockCount: | 2 | -2 |
| RecursionCount: | 3 | 3 |
| LockCount: | 1 | -2 |
| RecursionCount: | 2 | 2 |
| LockCount: | 0 | -2 |
| RecursionCount: | 1 | 1 |
| LockCount: | -1 | -1 |
| RecursionCount: | 0 | 0 |
You see a significant difference between XP and Vista. Windows XP counts the (internal) Lockcount upwards. It is in contrast to Windows Vista, where the LockCount counts downwards and stops at -2. But why does a call to EnterCriticalSection lock up the app in XP and not in Vista?
The initial state at the beginning and at the (proper) end is
| LockCount: | -1 | -1 |
| RecursionCount: | 0 | 0 |
In this case, we can safely call EnterCriticalSection without waiting. There is no lock on the section. Let us see and compare what happens when we call Leave one more time.
| LockCount: | -2 | -1 |
| RecursionCount: | -1 | -1 |
Windows XP decreases the RecursionCount and additionally the LockCount value. In Vista only the RecursionCount is decreased. That is the difference because EnterCriticalSection blocks only if it finds a LockCount different of -1 (the initial state). The Windows Vista API seems to be aware that the same thread calls EnterCriticalSection several times and thus just increases the recursion value.
That is the reason why we have to be careful even if we use critical sections in only one thread. The MSDN help about LeaveCriticalSection states this problem the following way:
If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely.
It should be instead of “another thread” say “any thread”.
How did I get the lock count information from the VCL TCriticalSection? The next code shows this simple task:
5 Responses
Xepol
23|Apr|2008 1Strange code, since the exit causes the leave to be called twice.
should read
procedure XY;
var CS : TCriticalSection;
Begin
CS.Enter;
try
if SomeThingBadHappensHere then
begin
exit;
end;
finally
CS.Leave;
End;
end;
Christian Wimmer
23|Apr|2008 2That is the reason why I experienced the explained problem. Finally will be even called if a procedure is left by calling exit.
stanleyxu
23|Apr|2008 3Do you mean, that we should use TryEnterCriticalSection instead of EnterCriticalSection in some cases?
rawbite
24|Apr|2008 4It is just _unbelievable_ that a this week I have spent 2 full working days figuring out a similar situation with break statement :). The code was like this:
some thread:
…
while true do
begin
WaitForSingleObject(SuspendEvent, INFINITE);
try
EnterCriticalSection(cs);
if IAmTerminated then
begin
… access some other variables that are shared …
IAmRunning := false;
LeaveCriticalSection(cs);
break;
end;
finally
LeaveCriticalSection(cs);
end;
… do something useful …
end;
…
the main thread:
…
… set terminated to true for the thread
SetEvent(SuspendEvent); // “resume” thread.
WaitForSingleObject(ThreadHandle, INFINITE);
EnterCriticalSection(cs); // <- DEADLOCK HERE
try
… access some shared variables …
finally
LeaveCriticalSection(cs);
end;
…
So it seems that break is behaving similar as exit.
Denis A
24|Apr|2008 5You can also be interested in reading a related post “A Critical[Section] Difference: Windows XP vs. Windows Vista”
http://blogs.codegear.com/abauer/2008/02/06/38855
Leave a reply
Search
Paypal donation (EUR)
Categories
Most Viewed
Archives
Tags
ACL callback COM Conversion CreateProcess DACL Delphi dialog DidYouKnow DLL documentation Download elevation factory file Handle header HowTo interface JWA JWSCL Kernel Microsoft KillProcess Laptop mail mailinglist manifest permission Privilege Process ProcessExplorer RunEl Russinovich Service Setup Sid TerminateProcess Theme Thread Token UAC user Vista Window WindowsRecent Posts
Recent Comments
Blogroll
Pages
Meta
A design creation of Design Disease
Copyright © 2007 - JEDI Windows API - is proudly powered by WordPress
InSense 1.0 Theme by Design Disease brought to you by HostGator Web Hosting.