1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.named;
20
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.TimeUnit;
23
24 import org.eclipse.aether.named.support.LockUpgradeNotSupportedException;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.TestInfo;
27 import org.junit.jupiter.api.Timeout;
28
29 import static org.junit.jupiter.api.Assertions.*;
30
31
32
33
34 public abstract class NamedLockFactoryTestSupport {
35
36 protected static NamedLockFactory namedLockFactory;
37
38 protected String lockName(TestInfo testInfo) {
39 return testInfo.getDisplayName();
40 }
41
42 @Test
43 void testFailure(TestInfo testInfo) throws InterruptedException {
44
45
46 assertThrows(IllegalStateException.class, () -> {
47 Thread t1 = new Thread(() -> {
48 try {
49 namedLockFactory.getLock(lockName(testInfo)).lockShared(1L, TimeUnit.MINUTES);
50 namedLockFactory.getLock(lockName(testInfo)).lockShared(1L, TimeUnit.MINUTES);
51 } catch (InterruptedException e) {
52 throw new RuntimeException(e);
53 }
54 });
55 Thread t2 = new Thread(() -> {
56 try {
57 namedLockFactory.getLock(lockName(testInfo)).lockShared(1L, TimeUnit.MINUTES);
58 namedLockFactory.getLock(lockName(testInfo)).lockShared(1L, TimeUnit.MINUTES);
59 namedLockFactory.getLock(lockName(testInfo)).lockShared(1L, TimeUnit.MINUTES);
60 } catch (InterruptedException e) {
61 throw new RuntimeException(e);
62 }
63 });
64 t1.start();
65 t2.start();
66 t1.join();
67 t2.join();
68 throw namedLockFactory.onFailure(new IllegalStateException("failure"));
69 });
70 }
71
72 @Test
73 void refCounting(TestInfo testInfo) {
74 final String name = lockName(testInfo);
75 try (NamedLock one = namedLockFactory.getLock(name);
76 NamedLock two = namedLockFactory.getLock(name)) {
77 assertSame(one, two);
78 one.close();
79 two.close();
80
81 try (NamedLock three = namedLockFactory.getLock(name)) {
82 assertNotSame(three, two);
83 }
84 }
85 }
86
87 @Test
88 void unlockWoLock(TestInfo testInfo) {
89 assertThrows(IllegalStateException.class, () -> {
90 final String name = lockName(testInfo);
91 try (NamedLock one = namedLockFactory.getLock(name)) {
92 one.unlock();
93 }
94 });
95 }
96
97 @Test
98 void wwBoxing(TestInfo testInfo) throws InterruptedException {
99 final String name = lockName(testInfo);
100 try (NamedLock one = namedLockFactory.getLock(name)) {
101 assertTrue(one.lockExclusively(1L, TimeUnit.MILLISECONDS));
102 assertTrue(one.lockExclusively(1L, TimeUnit.MILLISECONDS));
103 one.unlock();
104 one.unlock();
105 }
106 }
107
108 @Test
109 void rrBoxing(TestInfo testInfo) throws InterruptedException {
110 final String name = lockName(testInfo);
111 try (NamedLock one = namedLockFactory.getLock(name)) {
112 assertTrue(one.lockShared(1L, TimeUnit.MILLISECONDS));
113 assertTrue(one.lockShared(1L, TimeUnit.MILLISECONDS));
114 one.unlock();
115 one.unlock();
116 }
117 }
118
119 @Test
120 void wrBoxing(TestInfo testInfo) throws InterruptedException {
121 final String name = lockName(testInfo);
122 try (NamedLock one = namedLockFactory.getLock(name)) {
123 assertTrue(one.lockExclusively(1L, TimeUnit.MILLISECONDS));
124 assertTrue(one.lockShared(1L, TimeUnit.MILLISECONDS));
125 one.unlock();
126 one.unlock();
127 }
128 }
129
130 @Test
131 void rwBoxing(TestInfo testInfo) throws InterruptedException {
132 final String name = lockName(testInfo);
133 try (NamedLock one = namedLockFactory.getLock(name)) {
134 assertTrue(one.lockShared(1L, TimeUnit.MILLISECONDS));
135 try {
136 one.lockExclusively(1L, TimeUnit.MILLISECONDS);
137 fail();
138 } catch (LockUpgradeNotSupportedException e) {
139
140 }
141 one.unlock();
142 }
143 }
144
145 @Test
146 @Timeout(5)
147 public void sharedAccess(TestInfo testInfo) throws InterruptedException {
148 final String name = lockName(testInfo);
149 CountDownLatch winners = new CountDownLatch(2);
150 CountDownLatch losers = new CountDownLatch(0);
151 Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
152 Thread t2 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
153 t1.start();
154 t2.start();
155 t1.join();
156 t2.join();
157 winners.await();
158 losers.await();
159 }
160
161 @Test
162 @Timeout(5)
163 public void exclusiveAccess(TestInfo testInfo) throws InterruptedException {
164 final String name = lockName(testInfo);
165 CountDownLatch winners = new CountDownLatch(1);
166 CountDownLatch losers = new CountDownLatch(1);
167 Thread t1 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
168 Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
169 t1.start();
170 t2.start();
171 t1.join();
172 t2.join();
173 winners.await();
174 losers.await();
175 }
176
177 @Test
178 @Timeout(5)
179 public void mixedAccess(TestInfo testInfo) throws InterruptedException {
180 final String name = lockName(testInfo);
181 CountDownLatch winners = new CountDownLatch(1);
182 CountDownLatch losers = new CountDownLatch(1);
183 Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
184 Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
185 t1.start();
186 t2.start();
187 t1.join();
188 t2.join();
189 winners.await();
190 losers.await();
191 }
192
193 @Test
194 @Timeout(5)
195 public void fullyConsumeLockTime(TestInfo testInfo) throws InterruptedException {
196 long start = System.nanoTime();
197 final String name = lockName(testInfo);
198 CountDownLatch winners = new CountDownLatch(1);
199 CountDownLatch losers = new CountDownLatch(1);
200 Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
201 Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
202 t1.start();
203 t2.start();
204 t1.join();
205 t2.join();
206 winners.await();
207 losers.await();
208 long end = System.nanoTime();
209 long duration = end - start;
210 long expectedDuration = TimeUnit.MILLISECONDS.toNanos(ACCESS_WAIT_MILLIS);
211 assertTrue(duration >= expectedDuration, duration + " >= " + expectedDuration);
212 }
213
214 @Test
215 @Timeout(5)
216 public void releasedExclusiveAllowAccess(TestInfo testInfo) throws InterruptedException {
217 final String name = lockName(testInfo);
218 CountDownLatch winners = new CountDownLatch(1);
219 CountDownLatch losers = new CountDownLatch(0);
220 Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
221 try (NamedLock namedLock = namedLockFactory.getLock(name)) {
222 assertTrue(namedLock.lockExclusively(50L, TimeUnit.MILLISECONDS));
223 try {
224 t1.start();
225 Thread.sleep(50L);
226 } finally {
227 namedLock.unlock();
228 }
229 }
230 t1.join();
231 winners.await();
232 losers.await();
233 }
234
235 private static final long ACCESS_WAIT_MILLIS = 1000L;
236
237 private static class Access implements Runnable {
238 final NamedLockFactory namedLockFactory;
239 final String name;
240 final boolean shared;
241 final CountDownLatch winner;
242 final CountDownLatch loser;
243
244 public Access(
245 NamedLockFactory namedLockFactory,
246 String name,
247 boolean shared,
248 CountDownLatch winner,
249 CountDownLatch loser) {
250 this.namedLockFactory = namedLockFactory;
251 this.name = name;
252 this.shared = shared;
253 this.winner = winner;
254 this.loser = loser;
255 }
256
257 @Override
258 public void run() {
259 try (NamedLock lock = namedLockFactory.getLock(name)) {
260 if (shared
261 ? lock.lockShared(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS)
262 : lock.lockExclusively(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS)) {
263 try {
264 winner.countDown();
265 loser.await();
266 } finally {
267 lock.unlock();
268 }
269 } else {
270 loser.countDown();
271 winner.await();
272 }
273 } catch (InterruptedException e) {
274 fail(e.getMessage());
275 }
276 }
277 }
278 }