How to properly leave a Critical Section?
Asked Answered
L

5

8

I have the following C++ code where I make use of the Critical Section object:

EnterCriticalSection(&cs);

// code that may throw an exception

LeaveCriticalSection(&cs);

How can I ensure that the LeaveCriticalSection function is called even if an exception is thrown?

Longawa answered 1/5, 2015 at 10:5 Comment(0)
S
9

Just write a guard utilizing the destructor for clean up:

struct Guard {
  CriticalSection& cs;
  Guard(CriticalSection& cs)
  : cs(cs)
  {
    EnterCriticalSection(cs);
  }
  ~Guard() {
    LeaveCriticalSection(cs);
  }
  Guard(const Guard&) = delete;  
  Guard& operator = (const Guard&) = delete;
};

Usage:

void f() {
   Guard guard(cs);
   ...
}
Synchro answered 1/5, 2015 at 10:10 Comment(0)
A
8

Use RAII (Resource Acquisition Is Initialization) idiom:

struct GuardCS {
    GuardCS(CRITICAL_SECTION& p_cs) : cs(p_cs){
        EnterCriticalSection(&cs);
    }
    ~GuardCS() {
        LeaveCriticalSection(&cs);
    }
private:
    // Protect against copying, remove: =delete on pre c++11 compilers
    GuardCS(GuardCS const &) = delete;
    GuardCS& operator =(GuardCS const &) = delete;
    CRITICAL_SECTION& cs;
};

If you are using MFC by any chance there are classes that abstract such stuff: is Ccriticalsection usable in production?

Applecart answered 1/5, 2015 at 10:10 Comment(1)
If I could make one minor suggestion, and perhaps it is still wise to omit it simply for the sake of a simpler example to build upon, it might be healthy to make GuardCS noncopyable.Staghound
H
4

"How can I ensure that the LeaveCriticalSection function is called even if an exception is thrown?"

You can write a small helper class like this:

 class CsLocker {
 public:
     CsLocker(CriticalSection& cs)
     : cs_(cs) {
         EnterCriticalSection(&cs_);
     }
     ~CsLocker() {
          LeaveCriticalSection(&cs);
     }
     CsLocker(const CsLocker&) = delete;
     CsLocker& operator=(const CsLocker&) = delete;
 private:
     CriticalSection& cs_;
 };

That will guarantee that the critical section is unlocked whenever (and why ever) the scope is left.

Hertzog answered 1/5, 2015 at 10:13 Comment(2)
This is dangerous. If somebody accidentally copies the CsLocker, you're hosed.Windlass
@LightningRacisinObrit You're right, I have fixed that.Instate
E
3

I suggest you not to use WinAPI critical sections. You can get the same by using std::mutex. When you use it you also can use RAII idiom wrapper for auto unlocking mutex (std::lock_guard ).

UPDATE: one difference between critical section and mutex that you can lock critical section multiple times on one thread but this is not true for simple std::mutex. If you need recursive behaviour of locking use std::recursive_mutex std::lock_guard<std::recursive_mutex>

UPDATE 2: Detailed difference between critical sections and mutexes are described here, performance comparison is here.

Reasons: It is better to use standard-defined mechanism whenever you can. If you use platform-specific thing - wrap it around. So if you afraid for performance - create Critical section class with lock/unlock methods (to meet BasicLocakable concept requirements) and use std::lock_guard<MyCriticalSection>.

Emee answered 1/5, 2015 at 10:40 Comment(12)
Why shouldn't I use the WinAPI critical sections?Longawa
@Longawa Because your code won't be portable to other systems.Instate
You limit your code to execute on Windows-only systems without any specific reasons.Emee
@πάνταῥεῖ My code has not to be portable, anyway +1 for the advise.Longawa
There are so many benefits to using the standard tools. "I don't need portability" is not a good enough excuse to arbitrarily eschew them.Windlass
@LightningRacisinObrit Do I have the benefit of speed by using std objects instead of WinAPI critical sections?Longawa
@Nick: What makes you think the standard objects are not implemented using WinAPI critical sections, on a Windows toolchain? How else would they be implemented? The whole point of C++ is that it's an abstraction.Windlass
@LightningRacisinObrit I never said that, anyway there are so many syncrhonization objects in Windows, as for example mutex that are quite slower with respect to cirtical sections. I do not know how std::mutex is implemented, but if it does not use critical section it will be slower. Therefore, as I said, I don't need portability, I need speed first of all.Longawa
I do not like the statement 'I suggest you not to use WinAPI critical sections.' without reasoning. Especially, what are the differences beteween a CriticalSection and std::mutex or std::std::recursive_mutex ?Synchro
@Longawa Question about this #9997973Emee
@DieterLücking: Unless the OP is already aware of such a difference and requires to make use of it, that is also an insufficient reason to eschew standard types. Use them until you can't.Windlass
I like the idea to implement a wrapper implementing the BasicLockable concept +1.Synchro
D
1

The other answers are correct about using RAII objects but I feel it is worth pointing out an easy way to do this with Boost.ScopeExit.

#include <boost/scope_exit.hpp>
...
EnterCriticalSection(&cs);
BOOST_SCOPE_EXIT(&cs) {
        LeaveCriticalSection(&cs);
} BOOST_SCOPE_EXIT_END
// code that may throw an exception
Dispatch answered 1/5, 2015 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.