1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.concurrent.locks;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotNull;
21 import static org.junit.jupiter.api.Assertions.assertNotSame;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.time.Duration;
25 import java.util.concurrent.atomic.AtomicInteger;
26 import java.util.concurrent.locks.ReadWriteLock;
27 import java.util.concurrent.locks.ReentrantLock;
28 import java.util.concurrent.locks.ReentrantReadWriteLock;
29 import java.util.concurrent.locks.StampedLock;
30 import java.util.function.LongConsumer;
31
32 import org.apache.commons.lang3.AbstractLangTest;
33 import org.apache.commons.lang3.ArrayUtils;
34 import org.apache.commons.lang3.ThreadUtils;
35 import org.apache.commons.lang3.concurrent.locks.LockingVisitors.LockVisitor;
36 import org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReadWriteLockVisitor;
37 import org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor;
38 import org.apache.commons.lang3.concurrent.locks.LockingVisitors.StampedLockVisitor;
39 import org.apache.commons.lang3.function.FailableConsumer;
40 import org.junit.jupiter.api.Test;
41 import org.junit.jupiter.params.ParameterizedTest;
42 import org.junit.jupiter.params.provider.ValueSource;
43
44
45
46
47 class LockingVisitorsTest extends AbstractLangTest {
48
49 private static final Duration SHORT_DELAY = Duration.ofMillis(100);
50 private static final Duration DELAY = Duration.ofMillis(1500);
51 private static final int NUMBER_OF_THREADS = 10;
52 private static final Duration TOTAL_DELAY = DELAY.multipliedBy(NUMBER_OF_THREADS);
53
54 protected boolean containsTrue(final boolean[] booleanArray) {
55 synchronized (booleanArray) {
56 return ArrayUtils.contains(booleanArray, true);
57 }
58 }
59
60 private void runTest(final Duration delay, final boolean exclusiveLock, final LongConsumer runTimeCheck,
61 final boolean[] booleanValues, final LockVisitor<boolean[], ?> visitor) throws InterruptedException {
62 assertNotNull(visitor.getLock());
63 assertNotNull(visitor.getObject());
64 final boolean[] runningValues = new boolean[10];
65
66 for (int i = 0; i < booleanValues.length; i++) {
67 final int index = i;
68 final FailableConsumer<boolean[], ?> consumer = b -> {
69 b[index] = false;
70 ThreadUtils.sleep(delay);
71 b[index] = true;
72 set(runningValues, index, false);
73 };
74 final Thread t = new Thread(() -> {
75 if (exclusiveLock) {
76 visitor.acceptWriteLocked(consumer);
77 } else {
78 visitor.acceptReadLocked(consumer);
79 }
80 });
81 set(runningValues, i, true);
82 t.start();
83 }
84 while (containsTrue(runningValues)) {
85 ThreadUtils.sleep(SHORT_DELAY);
86 }
87
88 for (final boolean booleanValue : booleanValues) {
89 assertTrue(booleanValue);
90 }
91
92
93 }
94
95 protected void set(final boolean[] booleanArray, final int offset, final boolean value) {
96 synchronized (booleanArray) {
97 booleanArray[offset] = value;
98 }
99 }
100
101 @ParameterizedTest
102 @ValueSource(booleans = { true, false })
103 void testBuilderLockVisitor(final boolean fair) {
104 final AtomicInteger obj = new AtomicInteger();
105 final ReadWriteLock lock = new ReentrantReadWriteLock(fair);
106
107 final LockVisitor<AtomicInteger, ReadWriteLock> lockVisitor = new LockVisitor.LVBuilder()
108 .setObject(obj)
109 .setLock(lock)
110 .setReadLockSupplier(lock::readLock)
111 .setWriteLockSupplier(lock::writeLock)
112 .get();
113
114 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
115 assertEquals(1, obj.get());
116 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
117 assertEquals(2, obj.get());
118 }
119
120 @ParameterizedTest
121 @ValueSource(booleans = { true, false })
122 void testBuilderReadWriteLockVisitor(final boolean fair) {
123 final AtomicInteger obj = new AtomicInteger();
124 final ReadWriteLock lock = new ReentrantReadWriteLock(fair);
125
126 final LockingVisitors.ReadWriteLockVisitor<AtomicInteger> lockVisitor = ReadWriteLockVisitor.<AtomicInteger>builder()
127 .setObject(obj)
128 .setLock(lock)
129 .get();
130
131 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
132 assertEquals(1, obj.get());
133 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
134 assertEquals(2, obj.get());
135 }
136
137 @ParameterizedTest
138 @ValueSource(booleans = { true, false })
139 void testBuilderReentrantLockVisitor(final boolean fair) {
140 final AtomicInteger obj = new AtomicInteger();
141 final ReentrantLock lock = new ReentrantLock(fair);
142
143 final LockingVisitors.ReentrantLockVisitor<AtomicInteger> lockVisitor = ReentrantLockVisitor.<AtomicInteger>builder()
144 .setObject(obj)
145 .setLock(lock)
146 .get();
147
148 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
149 assertEquals(1, obj.get());
150 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
151 assertEquals(2, obj.get());
152 }
153
154 @ParameterizedTest
155 @ValueSource(booleans = { true, false })
156 void testBuilderReentrantReadWriteLockVisitor(final boolean fair) {
157 final AtomicInteger obj = new AtomicInteger();
158 final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
159
160 final LockingVisitors.ReadWriteLockVisitor<AtomicInteger> lockVisitor = ReadWriteLockVisitor.<AtomicInteger>builder()
161 .setObject(obj)
162 .setLock(lock)
163 .get();
164
165 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
166 assertEquals(1, obj.get());
167 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
168 assertEquals(2, obj.get());
169 }
170
171 @Test
172 void testBuilderReentrantStampedLockVisitor() {
173 final AtomicInteger obj = new AtomicInteger();
174 final StampedLock lock = new StampedLock();
175
176 final LockingVisitors.StampedLockVisitor<AtomicInteger> lockVisitor = StampedLockVisitor.<AtomicInteger>builder()
177 .setObject(obj)
178 .setLock(lock)
179 .get();
180
181 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
182 assertEquals(1, obj.get());
183 lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
184 assertEquals(2, obj.get());
185 }
186
187 @Test
188 void testCreate() {
189 final AtomicInteger obj = new AtomicInteger();
190 final ReadWriteLock lock = new ReentrantReadWriteLock();
191 LockingVisitors.create(obj, lock).acceptReadLocked(AtomicInteger::incrementAndGet);
192 LockingVisitors.create(obj, lock).acceptReadLocked(null);
193 assertEquals(1, obj.get());
194 LockingVisitors.create(obj, lock).acceptWriteLocked(AtomicInteger::incrementAndGet);
195 LockingVisitors.create(obj, lock).acceptWriteLocked(null);
196 assertEquals(2, obj.get());
197 }
198
199 @SuppressWarnings("deprecation")
200 @Test
201 void testDeprecatedConstructor() {
202 assertNotNull(new LockingVisitors().toString());
203 }
204
205 @Test
206 void testReentrantLock() throws Exception {
207
208 final boolean[] booleanValues = new boolean[10];
209 runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues, LockingVisitors.reentrantLockVisitor(booleanValues));
210 }
211
212 @ParameterizedTest
213 @ValueSource(booleans = { true, false })
214 void testReentrantLockFairness(final boolean fairness) throws Exception {
215
216 final boolean[] booleanValues = new boolean[10];
217 runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues,
218 LockingVisitors.create(booleanValues, new ReentrantLock(fairness)));
219 }
220
221 @Test
222 void testReentrantReadWriteLockExclusive() throws Exception {
223
224 final boolean[] booleanValues = new boolean[10];
225 runTest(DELAY, true, millis -> assertTrue(millis >= TOTAL_DELAY.toMillis()), booleanValues,
226 LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
227 }
228
229 @Test
230 void testReentrantReadWriteLockNotExclusive() throws Exception {
231
232 final boolean[] booleanValues = new boolean[10];
233 runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues,
234 LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
235 }
236
237 @Test
238 void testResultValidation() {
239 final Object hidden = new Object();
240 final StampedLockVisitor<Object> lock = LockingVisitors.stampedLockVisitor(hidden);
241 final Object o1 = lock.applyReadLocked(h -> new Object());
242 assertNotNull(o1);
243 assertNotSame(hidden, o1);
244 final Object o2 = lock.applyWriteLocked(h -> new Object());
245 assertNotNull(o2);
246 assertNotSame(hidden, o2);
247 }
248
249 @Test
250 void testStampedLockExclusive() throws Exception {
251
252 final boolean[] booleanValues = new boolean[10];
253 runTest(DELAY, true, millis -> assertTrue(millis >= TOTAL_DELAY.toMillis()), booleanValues, LockingVisitors.stampedLockVisitor(booleanValues));
254 }
255
256 @Test
257 void testStampedLockNotExclusive() throws Exception {
258
259 final boolean[] booleanValues = new boolean[10];
260 runTest(DELAY, false, millis -> assertTrue(millis < TOTAL_DELAY.toMillis()), booleanValues, LockingVisitors.stampedLockVisitor(booleanValues));
261 }
262 }