Click here to Skip to main content
15,891,905 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
What happens if I call EnterCriticalSection twice and LeaveCriticalSection once as shown below?
After executing LeaveCriticalSection, will the critical section still locked or released?

EnterCriticalSection
EnterCriticalSection

LeaveCriticalSection


Can any one please clarify?
Thanks
Posted
Comments
ThatsAlok 7-Jul-11 2:28am    
no effect if performed on same thread

I think you mean the same instance of the CriticalSection.
Nothing wrong for the calling thread, but next thread to call EnterCriticalSection will get stuck forever at the call.

It will be switched off by OS and kept in a wait state, never scheduled back for execution spending zero CPU time and never awaken. To wake up this thread the first thread should call LeaveCritical section on the same instance once again, but this call is missing.

—SA
 
Share this answer
 
Comments
MGPUDI 5-Jul-11 23:41pm    
Hi SAKryukov,

Thank you for your response, sorry I did not ask the question properly.Please find below the code. I had a problem that my application stops responding randomly, I thought that it was due to the improper unlocking of the critical section. Please see below the code with my comments.

void dispatcher() {

MAIN_LOOP(!done) {

ENTER_CRITICAL_SECTION(evt_cs);//locked critical section
while( !EMPTY(&main_event_queue) ) {

evt = dispatcher_pop_event();
LEAVE_CRITICAL_SECTION(evt_cs); //unlocked critical section

ENTER_CRITICAL_SECTION(evt_cs); //locked critical section

dispatcher_check_timer_events();//locked and unlocked critical section in this function call
//by the tile the control reaches while loop execution next time the evt_cs is locked twice but unlocked once
//I thought it caused my application to lock and moved the code to lock critical section below the dispatcher_check_timer_events
//and thinking that it will fix the problem, is my understanding correct?


}

LEAVE_CRITICAL_SECTION(evt_cs);
}

}


void dispatcher_check_timer_events(void) {

ENTER_CRITICAL_SECTION(evt_cs);
//some code
LEAVE_CRITICAL_SECTION(evt_cs);
return;
}
Sergey Alexandrovich Kryukov 6-Jul-11 0:53am    
Stopping responding randomly is a very usual thing is you have a deadlock.
There are, maybe, several problems with this code, I can immediately see three. First, the sequence of enter/leave critical section does not have much sense. Pay attention for a pair: leave immediately followed by enter. You go from locked state, then unlocked and immediately locked again, without any action in between. It means, this pair should be removed. Now, what happens if your code throws exception inside critical section? You need to catch exception and leave in any case. Third thing is: you have a loop based on condition, emptiness of the event queue. This is not a thread operation. You should use a blocking queue based on Event (event wait handle). In this way, your event will be in a wait state waiting at the WaitOne until awakened by the data arrived event. It is not a random name: event for a wait handle.
(I mean Even object here, see http://msdn.microsoft.com/en-us/library/ms682655(v=vs.85).aspx.).

Now, to see how to help you, I would need a bigger picture. As to the detection of your deadlock (let's assume there is a deadlock, this is likely) you would need to use system log and log some information in all critical points of you threads, especially before and after calls to all thread synchronization primitives and exceptions.

--SA
Sergey Alexandrovich Kryukov 6-Jul-11 0:55am    
Please edit your message with code -- cut is down to one line, referring to updated question. Move your code to question, enclose in <pre lang="c++"></pre> tags.
--SA
fjdiewornncalwe 5-Jul-11 23:48pm    
Well explained.
Sergey Alexandrovich Kryukov 6-Jul-11 0:41am    
Thank you, Marcus.
--SA
There would be no effect on same thread.
(My apology, I misunderstood your question terribly, the my old post doesnt make any sense. There would be no effect on same thread...)

Build this code,it should take away your doubts. (toggle the Enter and Leave in different combinations..)

#include <windows.h>
#include <stdio.h>
CRITICAL_SECTION sectionA;
LONG WINAPI func(LONG);
int wmain(void)
{
	HANDLE hThread;
	DWORD dwordThreadID;
	long i = 50;
	printf("The current thread ID is %u\n", GetCurrentThreadId());
	InitializeCriticalSection(&sectionA);
	hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,&dwordThreadID);
	CloseHandle(hThread);
	while(i--)
	{    
		EnterCriticalSection(&sectionA);
		printf("Main Thread Entered : Locked Section A for (%u) \n", GetCurrentThreadId());
		EnterCriticalSection(&sectionA);
		printf("Again Thread Entered : Locked Section A for (%u) \n", GetCurrentThreadId());
		LeaveCriticalSection(&sectionA);        
		printf("Main Thread releasing  (%u) critical sections\n", GetCurrentThreadId());
		// Remove this section and see 
		// LeaveCriticalSection(&sectionA);        
		Sleep(200);
	};
	return 0;
}
LONG WINAPI func(LONG lParam)
{
	long l=50;
	while(l--)
	{
		printf("Thread Trying to access \n", GetCurrentThreadId());
		EnterCriticalSection(&sectionA);
		printf("Thread2 Entered : Locked Section A for (%u) \n", GetCurrentThreadId());
		EnterCriticalSection(&sectionA);
		printf("Thread2 Entered : Locked Section A for (%u) \n", GetCurrentThreadId());
		LeaveCriticalSection(&sectionA);
		// Remove this section and see
		// LeaveCriticalSection(&sectionA);
		printf("Thread2 releasing (%u) critical sections\n", GetCurrentThreadId());
		Sleep(200);
	};
}


Hope this helps...
 
Share this answer
 
v4
Comments
MGPUDI 6-Jul-11 0:10am    
Hi harish85,

Thanks for your response, but I am interested to know if same thread locks the object twice and unlocks only once, will the object be still locked or unlocked? If you see my code mentioned in the earlier comment, I am locking an object twice and unlocking it once and going back to the beginning of while loop. At that time, if the object is unlocked, the object will not be accessed and I am thinking that that was causing my application to stop. I would like to know whether my assumption is correct or not.
CS locking is managed by a lock count. If you enter it twice and exit one, you have still one lock owned, and hence no other thread can lock the CCS (they all will wait for your thread to release all locks, that is: all your "entrance" must be "leaved" before someone else can "enter").

But note that, in loop, like
for(.,.,.)
{
   EnterCriticalSection(.);
   EnterCriticalSection(.);
   LeaveCriticalSection(.);
}

there is just one thread: so it will run the loop nicely. It will add 2 remove one for each iteration.
Wen it exists, you're left with a number of lock pending.

In anoter case like
EnterCriticalSection(.);
for(.,.,.)
{
    EnterCriticalSection(.);
    LeaveCriticalSection(.);
}
LeaveCriticalSection(.)

everithing is fine. Of course, the inner Enter/Leave in this case have no practical effect, but they can stay inside a function body that can be called either inside or outside the loop.

In general a good practice is to place an equal number of Enter/Leave into a same scope ( {} pairs) or let them be managed by a on-stack variable contruction / destruction, like
/// RAII enter/leave
class ccs_locker
{
   CRITICAL_SECTION* pcs;
public:
   ccs_locker(CRRITICAL_SECTION& cs) :pcs(&cs) { EnterCriticalSection(pcs); }
   ~ccs_unlocker() { LeaveCriticaSection(pcs); }
private:
   /// disable copy and assign
   ccs_locker(const ccs_locker&);
   ccs_locker& operator=(const ccs_locker&);
};


/// CRITSEC initializer
class criticalsection: public CRITICAL_SECTION
{
public:
   criticalsection() { InitializeCriticalSection(this); }
   ~criticalsection() { DeleteCriticalSection(this); }
private:
   //disable copy and assign
   criticalsection(const criticalsection&);
   criticalsection& operator=(const criticalsection&);
};

///now a proper always good code
{
  criticalsection cs;
    ccs_locker lock1(cs);
    for(...)
    {
      ccs_locker lock2(cs);
      ...
      if(...) return;
      ...
      if(...) throw ...;
      ...
      if(...) break;
      ...
    }
return;
}


Moral: no matter how you return, throw or break: destructor will always adjust the call pairs.
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900