-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
204 additions
and
0 deletions.
There are no files selected for viewing
204 changes: 204 additions & 0 deletions
204
docs/source/reference/programming-language/cpp/cpp-concurrency-deadlock.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
# C++ Concurrency Deadlock | ||
|
||
A deadlock happens when 2 or more threads wait for each other (including themselves) | ||
to take actions for resources, such as releasing a lock. | ||
|
||
* process p1 locks resource r1 and needs access to resource r2 | ||
* process p2 locks resource r2 and needs access to resource r1 | ||
|
||
```cpp | ||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_0() { | ||
std::lock_guard<std::mutex> locker_0(mtx_0); | ||
std::lock_guard<std::mutex> locker_1(mtx_1); | ||
// do something | ||
std::cout << "do_0" << std::endl; | ||
} | ||
void do_1() { | ||
std::lock_guard<std::mutex> locker_1(mtx_1); | ||
std::lock_guard<std::mutex> locker_0(mtx_0); | ||
// do something | ||
std::cout << "do_1" << std::endl; | ||
} | ||
}; | ||
|
||
void f_0(A_Class& o) { | ||
for(int i = 0; i < 100; ++i) { | ||
o.do_0(); | ||
} | ||
} | ||
|
||
void f_1(A_Class& o) { | ||
for(int i = 0; i < 100; ++i) { | ||
o.do_1(); | ||
} | ||
} | ||
|
||
int main() { | ||
A_Class o; | ||
std::thread t1(f_0, std::ref(o)); | ||
std::thread t2(f_1, std::ref(o)); | ||
t2.join(); t1.join(); | ||
return 0; | ||
} | ||
``` | ||
## Conditions | ||
There are 4 required conditions for deadlock to happen: | ||
* Mutual Exclusion | ||
* At least one resource is non-sharable. It can only be used by one process at one time | ||
* Hold and Wait | ||
* At least one process is "holding" one resource and "waiting" for another process to release another resource | ||
* No Preemption | ||
* Only the process "holding" one resource can release it voluntarily | ||
* Circular Wait | ||
* There exists a set of processes such that p_i is waiting for the resource hold by p_i+1 (p_n is waiting for the resource hold by p_0) | ||
## Handling | ||
1. prevention | ||
2. detection and recovery | ||
3. ignore ([Ostrich algorithm - Wikipedia](https://en.wikipedia.org/wiki/Ostrich_algorithm)) | ||
This page is not going to cover those details (yet) | ||
## Avoidance When Programming Cpp | ||
Try to avoid logic that requires multiple locks. | ||
* lock one lock at a time | ||
```cpp | ||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_2() { | ||
{ | ||
std::lock_guard<std::mutex> locker_0(mtx_0); | ||
; | ||
std::cout << "do_2" << std::endl; | ||
} | ||
{ | ||
std::lock_guard<std::mutex> locker_1(mtx_1); | ||
; | ||
std::cout << "do_2" << std::endl; | ||
} | ||
} | ||
}; | ||
``` | ||
|
||
Don't call a function from argument after locking. | ||
|
||
```cpp | ||
typedef void (* ArgFunc)(int a); | ||
|
||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_f(ArgFunc f) { | ||
std::scoped_lock lock(mtx_0, mtx_1); | ||
f(1); // don't do this call | ||
} | ||
}; | ||
``` | ||
Use algorithm (library function). | ||
* [std::lock](https://en.cppreference.com/w/cpp/thread/lock) | ||
* c++ 11 | ||
* [std::scoped_lock](https://en.cppreference.com/w/cpp/thread/scoped_lock) | ||
* c++ 17 | ||
```cpp | ||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_0() { | ||
std::lock(mtx_0, mtx_1) | ||
std::lock_guard<std::mutex> locker_0(mtx_0, std::adopt_lock); | ||
std::lock_guard<std::mutex> locker_1(mtx_1 , std::adopt_lock); | ||
// do something | ||
std::cout << "do_0" << std::endl; | ||
} | ||
void do_1() { | ||
std::lock(mtx_0, mtx_1) | ||
std::lock_guard<std::mutex> locker_1(mtx_1, std::adopt_lock); | ||
std::lock_guard<std::mutex> locker_0(mtx_0, std::adopt_lock); | ||
// do something | ||
std::cout << "do_1" << std::endl; | ||
} | ||
}; | ||
``` | ||
|
||
```cpp | ||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_0() { | ||
std::scoped_lock lock(mtx_0, mtx_1); | ||
// do something | ||
std::cout << "do_0" << std::endl; | ||
} | ||
void do_1() { | ||
std::scoped_lock lock(mtx_0, mtx_1); | ||
// do something | ||
std::cout << "do_1" << std::endl; | ||
} | ||
}; | ||
``` | ||
Use same lock order. | ||
* Multiple level mutex | ||
* if a thread locks a low-level mutex, it cannot lock a high-level one | ||
```cpp | ||
class A_Class { | ||
std::mutex mtx_0; | ||
std::mutex mtx_1; | ||
public: | ||
void do_0() { | ||
std::lock_guard<std::mutex> locker_0(mtx_0); | ||
std::lock_guard<std::mutex> locker_1(mtx_1); | ||
// do something | ||
std::cout << "do_0" << std::endl; | ||
} | ||
void do_1() { | ||
std::lock_guard<std::mutex> locker_0(mtx_0); | ||
std::lock_guard<std::mutex> locker_1(mtx_1); | ||
// do something | ||
std::cout << "do_1" << std::endl; | ||
} | ||
}; | ||
``` | ||
|
||
<!-- ## Starvation and Livelock --> | ||
|
||
|
||
## Granularity | ||
|
||
Think about what resources are to be protected when designing locks. | ||
|
||
Based on [herlihy4-5-presentation](http://fileadmin.cs.lth.se/cs/education/eda015f/2013/herlihy4-5-presentation.pdf): | ||
|
||
* Coarse-grained locking | ||
* One lock | ||
* Large number of resources | ||
* Fine-grained locking | ||
* More than one lock | ||
* Small number of resources | ||
|
||
## Reference/Read More | ||
|
||
* [Deadlock (The Java™ Tutorials > Essential Classes > Concurrency)](https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html) | ||
* [Operating Systems: Deadlocks](https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/7_Deadlocks.html) | ||
* [C++ Threading #4: Deadlock](https://www.youtube.com/watch?v=_N0B5ua7oN8&list=PL5jc9xFGsL8E12so1wlMS0r0hTQoJL74M&index=4) |