views:

127

answers:

6

I have the following code in my program:

  EnterCriticalSection(&critsec[x]);
  // stuff
  LeaveCriticalSection(&critsec[x]);

It works fine 99.999% of the time but occasionally a handle seems to get left behind. Now I have done the obvious things like make sure that x did not change value between the enter and make sure that there isn't any "return" or "break" inside "// stuff" but I was wondering if there could be something else that would cause an enter/leave pair to leave a handle behind. Perhaps running out of memory or overflowing some counter in the OS or whatever.

EDIT: I am new to C++, the program has only recently been converted from C. It has no exceptions anywhere in the entire program.

+1  A: 

Since you're in C++ land, an exception leaving that // stuff part will skip LeaveCriticalSection(). Look up RAII ("Resource-Aquisition-Is-Initialization") as a tool to prevent that from happening. Here's a somewhat simple example for such a class:

class CriticalSectionLock {
public:
  CriticalSectionLock(CRITICAL_SECTION& c) : cs_(c){EnterCriticalSection(&cs_);}
  ~CriticalSectionLock()                           {LeaveCriticalSection(&cs_);}
private:
  CRITICAL_SECTION& cs_;
};


void f()
{
  CriticalSectionLock lock(critsec[x])
   // stuff
} // lock's destructor will automagically call LeaveCriticalSection()

On a side-note: Deadlocks can sometimes give the impression of some lock not being properly unlocked.

sbi
+1  A: 

The most probable cause is an exception. Are you catching the exceptions inside this function and whether they call Leave or not? Also, note that it is better to use CSingleLock class to lock the critical section instead of using raw APIs like this. By using CSingleLock you can guantee proper cleanup incase of exceptions.

Naveen
+2  A: 

Mick,

There are a number of things that could be happening.

Exceptions can cause control flow to exit the block before executing the LeaveCriticalSection call. To avoid this problem you can wrap up the entering & exiting of the critical section within a stack based object using the Resource Acquisition Is Initialisation (RAII) pattern.

Without a more complete listing, however, it is impossible to say whether there are any other issues with your code.

Cheers Seb

Seb Rose
This is embarrassing, but I don't know what an "exception" is. By the way I am a long time C programmer and rather new to C++, is an exception a C++ concept?
Mick
@Mick: You can get a quick overview here: http://www.parashift.com/c++-faq-lite/exceptions.html
Naveen
Or here: http://www.cplusplus.com/doc/tutorial/exceptions/
Naveen
If I understand correctly - I will have no exceptions going on within my program unless I put them there. I know for sure I didn't.
Mick
If you are not throwing exceptions from your code, then it may be one of the Structured Exceptions thrown whenever something really bad happens, something like an AccessViolation or StackOverflow etc.
Naveen
So you mean that perhaps Enter/LeaveCriticalSection may have failed within themselves. Is there some way i could detect this? I note that neither returns any value.
Mick
@Mick: Exceptions can be thrown by code that you call. The C++ standard library can throw exceptions, 3rd-party code can. When in C++ land, you have to deal with the fact that exceptions might be thrown. Live with the fact and adapt your code accordingly.
sbi
A: 

Expanding on sbi's answer (as you say you're new to C++), an exception ignores the rest of the code from the place where it is invoked until it reaches a place where the exception can be handled (a 'catch') - the only exception to this is when the exception wraps up memory in a stack - it calls the destructors of stack variables.

To ensure that the 'Leave' is always called, use the following class: (excuse the lack of formatting), and put both an instance of this class and the critical code in a new stack. This ensures that the 'Leave' is always called, both in non-exception and exception scenarios.

edit: Updating p-o-c code to reflect comment.

class AutoCritical
{
public:
  AutoCritical(CritSec * p_CritSec) : m_Sec(p_CritSec) 
   { EnterCriticalSection(m_CritSec); };
  ~AutoCritical() { LeaveCriticalSection(m_CritSec); };
private:
  CritSec * m_Sec;
};

calling place:

// non-critical code ....
{   //open stack for critical code
    AutoCritical a(&critsec[x]);
    // do critical stuff here ...
}   // close stack
Fox
@Fox: In principle that's nice, except that it's not clear how `AutoCritical` can access `critsec`.
sbi
A: 

Is it possible that you're not losing the handle with unmatching Enter/Leave but rather by forgetting to call DeleteCriticalSection.

Nicholaz
All my critical sections are global variables. I initialize them right at the start of main and from then on the number of critical sections remains unchanged. I never delete them at all - I just let the OS kill them when I quit the program.
Mick
+3  A: 

If you don't explicitly delete the critical section and if there was ever contention on the critical section, you will leak a handle. Some implementations of critical sections on Windows allocate a semaphore when two or more threads overlap in their attempts to enter a single critical section.

It's not a leak. Or rather, it isn't a leak if the number of "leaked" handles is less than or equal to the number of global critical sections you are using.

MSN
This sounds very much like the answer! (except that I'm not sure I understand it!) Are you saying that when I have, and use, an array of critical sections, handles will only be created when there are contentions?
Mick
I'm confused - surely I don't want to delete the critical section. I just want to delete the handle that has been created by the contention. I will want to use the critical section over and over.
Mick
Just had it confirmed elsewhere that yours is the correct answer. There is no bug, there is no leak. I was just thrown off by the slow but steady growth of handles due to the rarity of contention.
Mick