Try catch with locks in C++
Clash Royale CLAN TAG#URR8PPP
up vote
25
down vote
favorite
In Java:
Lock lock = new ReentrantLock();
try
lock.lock();
someFunctionLikelyToCauseAnException();
catch(e)...
finally
lock.unlock();
My question is with this above example we know that the lock WILL always get unlocked because finally always executes, but what is the guarantee with C++?
mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????
How will this work and why?
java c++ multithreading operating-system locking
New contributor
 |Â
show 10 more comments
up vote
25
down vote
favorite
In Java:
Lock lock = new ReentrantLock();
try
lock.lock();
someFunctionLikelyToCauseAnException();
catch(e)...
finally
lock.unlock();
My question is with this above example we know that the lock WILL always get unlocked because finally always executes, but what is the guarantee with C++?
mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????
How will this work and why?
java c++ multithreading operating-system locking
New contributor
23
Use std::lock_guard for RAII approach.std::lock_guard
object will unlock whatever mutex it had in destructor.
â Yksisarvinen
Sep 6 at 14:55
7
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
16
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
5
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
14
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39
 |Â
show 10 more comments
up vote
25
down vote
favorite
up vote
25
down vote
favorite
In Java:
Lock lock = new ReentrantLock();
try
lock.lock();
someFunctionLikelyToCauseAnException();
catch(e)...
finally
lock.unlock();
My question is with this above example we know that the lock WILL always get unlocked because finally always executes, but what is the guarantee with C++?
mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????
How will this work and why?
java c++ multithreading operating-system locking
New contributor
In Java:
Lock lock = new ReentrantLock();
try
lock.lock();
someFunctionLikelyToCauseAnException();
catch(e)...
finally
lock.unlock();
My question is with this above example we know that the lock WILL always get unlocked because finally always executes, but what is the guarantee with C++?
mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????
How will this work and why?
java c++ multithreading operating-system locking
New contributor
edited Sep 6 at 16:51
Peter Mortensen
12.9k1983111
12.9k1983111
New contributor
asked Sep 6 at 14:53
Brijendar Bakchodia
18126
18126
New contributor
New contributor
23
Use std::lock_guard for RAII approach.std::lock_guard
object will unlock whatever mutex it had in destructor.
â Yksisarvinen
Sep 6 at 14:55
7
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
16
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
5
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
14
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39
 |Â
show 10 more comments
23
Use std::lock_guard for RAII approach.std::lock_guard
object will unlock whatever mutex it had in destructor.
â Yksisarvinen
Sep 6 at 14:55
7
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
16
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
5
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
14
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39
23
23
Use std::lock_guard for RAII approach.
std::lock_guard
object will unlock whatever mutex it had in destructor.â Yksisarvinen
Sep 6 at 14:55
Use std::lock_guard for RAII approach.
std::lock_guard
object will unlock whatever mutex it had in destructor.â Yksisarvinen
Sep 6 at 14:55
7
7
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
16
16
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
5
5
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
14
14
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39
 |Â
show 10 more comments
3 Answers
3
active
oldest
votes
up vote
53
down vote
accepted
For this we use the RAII-style construct std::lock_guard
. When you use
std::mutex m;
// start of some scope
std::lock_guard lg(m);
// stuff
// end of scope
lg
will ensure that m
will be unlocked no matter what path the scope is left as it is destroyed at scope exit and std::lock_guard
s destructor will call unlock
Even if an exception is thrown the stack will be unwound (stack unwinding) and that process destroys lg
which in turn will call unlock
guaranteeing that the lock is released.
9
Just in case the OP doesn't see what's really going on here;lg
is a local variable. Thelg(m)
expression calls the constructor of thestd::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. Thelock_guard
constructor locks the given lockm
, and the destructor unlocks it.
â besmirched
Sep 6 at 15:42
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Solock_guard
is wrapping the mutexm
basically like a smart pointer?
â Brijendar Bakchodia
Sep 6 at 17:28
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than rawnew
ordelete
, so you'd write itauto m = std::make_unique<mutex>();
Second, notice thatstd::lock_guard
's constructor takes astd::mutex&
. So just turn that pointer into a reference by dereferencing it:std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,std::lock_guard
andstd::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.
â besmirched
Sep 6 at 19:12
 |Â
show 5 more comments
up vote
26
down vote
what is the guarantee with C++?
The relevant guarantee in C++ works a bit differently in comparison to the one you mention in Java. Instead of a finally block, it's relying on the destruction of automatic variables that happens upon the scope's exit, when the stack frame gets unwound. This stack unwinding occurs regardless of how the scope was exited, whether gracefully or due to an exception.
The preferred approach for the scenario concerning such locks is to use RAII, as implemented for example by std::lock_guard
. It holds a mutex
object passed to its constructor -- inside of which it calls the mutex
's lock()
method, after which the thread owns the mutex -- and upon stack unwinding at the scope's exit its destructor is called -- inside of which it calls the mutex
's unlock()
method, thus releasing it.
The code will look like this:
std::mutex m;
std::lock_guard lock(m);
// Everything here is mutex-protected.
// Here you are guaranteed the std::mutex is released.
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
add a comment |Â
up vote
0
down vote
If an exception is thrown during execution of a piece of code protected by a critical section, that is, codes between "lock()" and "unlock()", it means the associated object that piece of code is operating on is no longer in a valid state. This, may or may not be rolled back by automatic unwinding of the stack triggered by the exception, because some side effect might have taken place before the exception is thrown ( a message has been sent through socket, a machine has been started, for example). At this point, the bigger issue here is not if the mutex will be released (the only guarantee from using lock_guard instead). There might well be some cases that the mutex still being locked is the desirable behavior, and can be explicitly reset after the caller cleaning all mess up.
My point is: this is not a language issue. No language feature can guarantee correct error handling. Don't take lock_guard and RAII as a silver bullet.
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
53
down vote
accepted
For this we use the RAII-style construct std::lock_guard
. When you use
std::mutex m;
// start of some scope
std::lock_guard lg(m);
// stuff
// end of scope
lg
will ensure that m
will be unlocked no matter what path the scope is left as it is destroyed at scope exit and std::lock_guard
s destructor will call unlock
Even if an exception is thrown the stack will be unwound (stack unwinding) and that process destroys lg
which in turn will call unlock
guaranteeing that the lock is released.
9
Just in case the OP doesn't see what's really going on here;lg
is a local variable. Thelg(m)
expression calls the constructor of thestd::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. Thelock_guard
constructor locks the given lockm
, and the destructor unlocks it.
â besmirched
Sep 6 at 15:42
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Solock_guard
is wrapping the mutexm
basically like a smart pointer?
â Brijendar Bakchodia
Sep 6 at 17:28
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than rawnew
ordelete
, so you'd write itauto m = std::make_unique<mutex>();
Second, notice thatstd::lock_guard
's constructor takes astd::mutex&
. So just turn that pointer into a reference by dereferencing it:std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,std::lock_guard
andstd::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.
â besmirched
Sep 6 at 19:12
 |Â
show 5 more comments
up vote
53
down vote
accepted
For this we use the RAII-style construct std::lock_guard
. When you use
std::mutex m;
// start of some scope
std::lock_guard lg(m);
// stuff
// end of scope
lg
will ensure that m
will be unlocked no matter what path the scope is left as it is destroyed at scope exit and std::lock_guard
s destructor will call unlock
Even if an exception is thrown the stack will be unwound (stack unwinding) and that process destroys lg
which in turn will call unlock
guaranteeing that the lock is released.
9
Just in case the OP doesn't see what's really going on here;lg
is a local variable. Thelg(m)
expression calls the constructor of thestd::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. Thelock_guard
constructor locks the given lockm
, and the destructor unlocks it.
â besmirched
Sep 6 at 15:42
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Solock_guard
is wrapping the mutexm
basically like a smart pointer?
â Brijendar Bakchodia
Sep 6 at 17:28
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than rawnew
ordelete
, so you'd write itauto m = std::make_unique<mutex>();
Second, notice thatstd::lock_guard
's constructor takes astd::mutex&
. So just turn that pointer into a reference by dereferencing it:std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,std::lock_guard
andstd::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.
â besmirched
Sep 6 at 19:12
 |Â
show 5 more comments
up vote
53
down vote
accepted
up vote
53
down vote
accepted
For this we use the RAII-style construct std::lock_guard
. When you use
std::mutex m;
// start of some scope
std::lock_guard lg(m);
// stuff
// end of scope
lg
will ensure that m
will be unlocked no matter what path the scope is left as it is destroyed at scope exit and std::lock_guard
s destructor will call unlock
Even if an exception is thrown the stack will be unwound (stack unwinding) and that process destroys lg
which in turn will call unlock
guaranteeing that the lock is released.
For this we use the RAII-style construct std::lock_guard
. When you use
std::mutex m;
// start of some scope
std::lock_guard lg(m);
// stuff
// end of scope
lg
will ensure that m
will be unlocked no matter what path the scope is left as it is destroyed at scope exit and std::lock_guard
s destructor will call unlock
Even if an exception is thrown the stack will be unwound (stack unwinding) and that process destroys lg
which in turn will call unlock
guaranteeing that the lock is released.
edited Sep 6 at 15:09
answered Sep 6 at 14:58
NathanOliver
75k15103153
75k15103153
9
Just in case the OP doesn't see what's really going on here;lg
is a local variable. Thelg(m)
expression calls the constructor of thestd::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. Thelock_guard
constructor locks the given lockm
, and the destructor unlocks it.
â besmirched
Sep 6 at 15:42
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Solock_guard
is wrapping the mutexm
basically like a smart pointer?
â Brijendar Bakchodia
Sep 6 at 17:28
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than rawnew
ordelete
, so you'd write itauto m = std::make_unique<mutex>();
Second, notice thatstd::lock_guard
's constructor takes astd::mutex&
. So just turn that pointer into a reference by dereferencing it:std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,std::lock_guard
andstd::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.
â besmirched
Sep 6 at 19:12
 |Â
show 5 more comments
9
Just in case the OP doesn't see what's really going on here;lg
is a local variable. Thelg(m)
expression calls the constructor of thestd::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. Thelock_guard
constructor locks the given lockm
, and the destructor unlocks it.
â besmirched
Sep 6 at 15:42
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Solock_guard
is wrapping the mutexm
basically like a smart pointer?
â Brijendar Bakchodia
Sep 6 at 17:28
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than rawnew
ordelete
, so you'd write itauto m = std::make_unique<mutex>();
Second, notice thatstd::lock_guard
's constructor takes astd::mutex&
. So just turn that pointer into a reference by dereferencing it:std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,std::lock_guard
andstd::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.
â besmirched
Sep 6 at 19:12
9
9
Just in case the OP doesn't see what's really going on here;
lg
is a local variable. The lg(m)
expression calls the constructor of the std::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. The lock_guard
constructor locks the given lock m
, and the destructor unlocks it.â besmirched
Sep 6 at 15:42
Just in case the OP doesn't see what's really going on here;
lg
is a local variable. The lg(m)
expression calls the constructor of the std::lock_guard
class, and C++ guarantees that the destructor of any local variable will be promptly called when the thread exits from the variable's scope--no matter how the thread exits. The lock_guard
constructor locks the given lock m
, and the destructor unlocks it.â besmirched
Sep 6 at 15:42
12
12
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
Note to future readers: RAII is one of C++'s most important idioms and describes one of the biggest ideological differences between C++ and Java. If you are coming to C++ from Java you life will be hundreds of times easier once you start taking advantages of it. Read the given link and this one.
â user4581301
Sep 6 at 16:38
So
lock_guard
is wrapping the mutex m
basically like a smart pointer?â Brijendar Bakchodia
Sep 6 at 17:28
So
lock_guard
is wrapping the mutex m
basically like a smart pointer?â Brijendar Bakchodia
Sep 6 at 17:28
4
4
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than raw
new
or delete
, so you'd write it auto m = std::make_unique<mutex>();
Second, notice that std::lock_guard
's constructor takes a std::mutex&
. So just turn that pointer into a reference by dereferencing it: std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
@BrijendarBakchodia First, you often don't need to allocate the mutex on the heap. If you do, you should be using a smart pointer rather than raw
new
or delete
, so you'd write it auto m = std::make_unique<mutex>();
Second, notice that std::lock_guard
's constructor takes a std::mutex&
. So just turn that pointer into a reference by dereferencing it: std::lock_guard lg(*m)
â Justin
Sep 6 at 18:00
1
1
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,
std::lock_guard
and std::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.â besmirched
Sep 6 at 19:12
@BrijendarBakchodia making sure that a mutex eventually gets unlocked and making sure that a heap-allocated object eventually gets freed are two separate problems. They are separate problems even if the heap-allocated thing happens to be a mutex. They are separate problems even though the preferred solutions,
std::lock_guard
and std::unique_ptr
, both happen to use the same design pattern (RAII). An important skill for software engineering is learning to recognize and untangle separate problems that appear in the same space, and to keep their solutions separated.â besmirched
Sep 6 at 19:12
 |Â
show 5 more comments
up vote
26
down vote
what is the guarantee with C++?
The relevant guarantee in C++ works a bit differently in comparison to the one you mention in Java. Instead of a finally block, it's relying on the destruction of automatic variables that happens upon the scope's exit, when the stack frame gets unwound. This stack unwinding occurs regardless of how the scope was exited, whether gracefully or due to an exception.
The preferred approach for the scenario concerning such locks is to use RAII, as implemented for example by std::lock_guard
. It holds a mutex
object passed to its constructor -- inside of which it calls the mutex
's lock()
method, after which the thread owns the mutex -- and upon stack unwinding at the scope's exit its destructor is called -- inside of which it calls the mutex
's unlock()
method, thus releasing it.
The code will look like this:
std::mutex m;
std::lock_guard lock(m);
// Everything here is mutex-protected.
// Here you are guaranteed the std::mutex is released.
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
add a comment |Â
up vote
26
down vote
what is the guarantee with C++?
The relevant guarantee in C++ works a bit differently in comparison to the one you mention in Java. Instead of a finally block, it's relying on the destruction of automatic variables that happens upon the scope's exit, when the stack frame gets unwound. This stack unwinding occurs regardless of how the scope was exited, whether gracefully or due to an exception.
The preferred approach for the scenario concerning such locks is to use RAII, as implemented for example by std::lock_guard
. It holds a mutex
object passed to its constructor -- inside of which it calls the mutex
's lock()
method, after which the thread owns the mutex -- and upon stack unwinding at the scope's exit its destructor is called -- inside of which it calls the mutex
's unlock()
method, thus releasing it.
The code will look like this:
std::mutex m;
std::lock_guard lock(m);
// Everything here is mutex-protected.
// Here you are guaranteed the std::mutex is released.
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
add a comment |Â
up vote
26
down vote
up vote
26
down vote
what is the guarantee with C++?
The relevant guarantee in C++ works a bit differently in comparison to the one you mention in Java. Instead of a finally block, it's relying on the destruction of automatic variables that happens upon the scope's exit, when the stack frame gets unwound. This stack unwinding occurs regardless of how the scope was exited, whether gracefully or due to an exception.
The preferred approach for the scenario concerning such locks is to use RAII, as implemented for example by std::lock_guard
. It holds a mutex
object passed to its constructor -- inside of which it calls the mutex
's lock()
method, after which the thread owns the mutex -- and upon stack unwinding at the scope's exit its destructor is called -- inside of which it calls the mutex
's unlock()
method, thus releasing it.
The code will look like this:
std::mutex m;
std::lock_guard lock(m);
// Everything here is mutex-protected.
// Here you are guaranteed the std::mutex is released.
what is the guarantee with C++?
The relevant guarantee in C++ works a bit differently in comparison to the one you mention in Java. Instead of a finally block, it's relying on the destruction of automatic variables that happens upon the scope's exit, when the stack frame gets unwound. This stack unwinding occurs regardless of how the scope was exited, whether gracefully or due to an exception.
The preferred approach for the scenario concerning such locks is to use RAII, as implemented for example by std::lock_guard
. It holds a mutex
object passed to its constructor -- inside of which it calls the mutex
's lock()
method, after which the thread owns the mutex -- and upon stack unwinding at the scope's exit its destructor is called -- inside of which it calls the mutex
's unlock()
method, thus releasing it.
The code will look like this:
std::mutex m;
std::lock_guard lock(m);
// Everything here is mutex-protected.
// Here you are guaranteed the std::mutex is released.
edited 2 days ago
answered Sep 6 at 14:57
SkepticalEmpiricist
2,821720
2,821720
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
add a comment |Â
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
2
2
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
Well, the guarantee in C++ is almost exactly the same as the one in Java. It just concerns destructors of automatic variables rather than finally blocks.
â Jan Hudec
Sep 6 at 21:30
add a comment |Â
up vote
0
down vote
If an exception is thrown during execution of a piece of code protected by a critical section, that is, codes between "lock()" and "unlock()", it means the associated object that piece of code is operating on is no longer in a valid state. This, may or may not be rolled back by automatic unwinding of the stack triggered by the exception, because some side effect might have taken place before the exception is thrown ( a message has been sent through socket, a machine has been started, for example). At this point, the bigger issue here is not if the mutex will be released (the only guarantee from using lock_guard instead). There might well be some cases that the mutex still being locked is the desirable behavior, and can be explicitly reset after the caller cleaning all mess up.
My point is: this is not a language issue. No language feature can guarantee correct error handling. Don't take lock_guard and RAII as a silver bullet.
add a comment |Â
up vote
0
down vote
If an exception is thrown during execution of a piece of code protected by a critical section, that is, codes between "lock()" and "unlock()", it means the associated object that piece of code is operating on is no longer in a valid state. This, may or may not be rolled back by automatic unwinding of the stack triggered by the exception, because some side effect might have taken place before the exception is thrown ( a message has been sent through socket, a machine has been started, for example). At this point, the bigger issue here is not if the mutex will be released (the only guarantee from using lock_guard instead). There might well be some cases that the mutex still being locked is the desirable behavior, and can be explicitly reset after the caller cleaning all mess up.
My point is: this is not a language issue. No language feature can guarantee correct error handling. Don't take lock_guard and RAII as a silver bullet.
add a comment |Â
up vote
0
down vote
up vote
0
down vote
If an exception is thrown during execution of a piece of code protected by a critical section, that is, codes between "lock()" and "unlock()", it means the associated object that piece of code is operating on is no longer in a valid state. This, may or may not be rolled back by automatic unwinding of the stack triggered by the exception, because some side effect might have taken place before the exception is thrown ( a message has been sent through socket, a machine has been started, for example). At this point, the bigger issue here is not if the mutex will be released (the only guarantee from using lock_guard instead). There might well be some cases that the mutex still being locked is the desirable behavior, and can be explicitly reset after the caller cleaning all mess up.
My point is: this is not a language issue. No language feature can guarantee correct error handling. Don't take lock_guard and RAII as a silver bullet.
If an exception is thrown during execution of a piece of code protected by a critical section, that is, codes between "lock()" and "unlock()", it means the associated object that piece of code is operating on is no longer in a valid state. This, may or may not be rolled back by automatic unwinding of the stack triggered by the exception, because some side effect might have taken place before the exception is thrown ( a message has been sent through socket, a machine has been started, for example). At this point, the bigger issue here is not if the mutex will be released (the only guarantee from using lock_guard instead). There might well be some cases that the mutex still being locked is the desirable behavior, and can be explicitly reset after the caller cleaning all mess up.
My point is: this is not a language issue. No language feature can guarantee correct error handling. Don't take lock_guard and RAII as a silver bullet.
answered yesterday
John Z. Li
1308
1308
add a comment |Â
add a comment |Â
Brijendar Bakchodia is a new contributor. Be nice, and check out our Code of Conduct.
Brijendar Bakchodia is a new contributor. Be nice, and check out our Code of Conduct.
Brijendar Bakchodia is a new contributor. Be nice, and check out our Code of Conduct.
Brijendar Bakchodia is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52206884%2ftry-catch-with-locks-in-c%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
23
Use std::lock_guard for RAII approach.
std::lock_guard
object will unlock whatever mutex it had in destructor.â Yksisarvinen
Sep 6 at 14:55
7
You should read about stack unwinding, a very important feature of c++.
â François Andrieux
Sep 6 at 14:56
16
in a nutshell: c++ does not need finally, because it has destructors
â user463035818
Sep 6 at 14:58
5
@user4581301 Where have you read that Java has destructors? It has finalizers. It's called something different specifically because it's not a destructor. The issue is that the Finalizer is often called a Destructor, which I have a problem with.
â Andrew T Finnell
Sep 6 at 17:24
14
A lock is typically used with the pattern: "(1) state is consistent but wrong (2) enter the try (3) wait until you can take the lock (4) make state inconsistent (5) make state right and consistent, (6) enter the finally (7) release the lock (8) state is now consistent and right." What happens if a throw occurs after step 4 and before step 5? We go directly to step 6 but state is now both inconsistent and wrong! We then unlock the lock, and code that is waiting now has access to inconsistent wrong state, and it crashes. This pattern of unlocking in a finally is super dangerous.
â Eric Lippert
Sep 6 at 17:39