-
Notifications
You must be signed in to change notification settings - Fork 0
/
Deadlock_Scenarios.java
143 lines (120 loc) · 3.47 KB
/
Deadlock_Scenarios.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package pl.amazingcode.threadscollider.multi;
import static org.assertj.core.api.BDDAssertions.then;
import static pl.amazingcode.threadscollider.ThreadsCollider.ThreadsColliderBuilder.threadsCollider;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.condition.EnabledIf;
import pl.amazingcode.threadscollider.Processors;
import pl.amazingcode.threadscollider.ThreadsCollider;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
final class Deadlock_Scenarios {
private List<Integer> list1;
private List<Integer> list2;
private Lock lock1;
private Lock lock2;
private static boolean enoughProcessorCores() {
return Runtime.getRuntime().availableProcessors() >= 8;
}
@BeforeEach
void setUp() {
list1 = new ArrayList<>();
list2 = new ArrayList<>();
lock1 = new ReentrantLock();
lock2 = new ReentrantLock();
}
@Disabled
@RepeatedTest(10)
@EnabledIf("enoughProcessorCores")
void Detect_deadlock() {
// Given
List<Exception> exceptions = new ArrayList<>();
// When
try (ThreadsCollider collider =
threadsCollider()
.withAction(() -> update1(list1, list2), "update1")
.times(Processors.HALF)
.withAction(() -> update2(list2, list1), "update2")
.times(Processors.HALF)
.withThreadsExceptionsConsumer(exceptions::add)
.withAwaitTerminationTimeout(100)
.asMilliseconds()
.build()) {
collider.collide();
}
// Then
then(exceptions).isEmpty();
}
private void update1(List<Integer> list1, List<Integer> list2) {
synchronized (list1) {
list1.add(1);
synchronized (list2) {
list2.add(1);
}
}
}
private void update2(List<Integer> list2, List<Integer> list1) {
synchronized (list2) {
list2.add(1);
synchronized (list1) {
list1.add(1);
}
}
}
@Disabled
@RepeatedTest(10)
@EnabledIf("enoughProcessorCores")
void Detect_deadlock_with_locks() {
// Given
List<Exception> exceptions = new ArrayList<>();
// When
try (ThreadsCollider collider =
threadsCollider()
.withAction(() -> update1WithLocks(list1, list2), "update1WithLocks")
.times(Processors.HALF)
.withAction(() -> update2WithLocks(list2, list1), "update2WithLocks")
.times(Processors.HALF)
.withThreadsExceptionsConsumer(exceptions::add)
.withAwaitTerminationTimeout(100)
.asMilliseconds()
.build()) {
collider.collide();
}
// Then
then(exceptions).isEmpty();
}
private void update1WithLocks(List<Integer> list1, List<Integer> list2) {
lock1.lock();
try {
list1.add(1);
lock2.lock();
try {
list2.add(1);
} finally {
lock2.unlock();
}
} finally {
lock1.unlock();
}
}
private void update2WithLocks(List<Integer> list2, List<Integer> list1) {
lock2.lock();
try {
list2.add(1);
lock1.lock();
try {
list1.add(1);
} finally {
lock1.unlock();
}
} finally {
lock2.unlock();
}
}
}