View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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.Assert;
26  import org.junit.Rule;
27  import org.junit.Test;
28  import org.junit.rules.TestName;
29  
30  import static org.hamcrest.MatcherAssert.assertThat;
31  import static org.hamcrest.Matchers.*;
32  
33  /**
34   * UT support for {@link NamedLockFactory}.
35   */
36  public abstract class NamedLockFactoryTestSupport {
37  
38      protected static NamedLockFactory namedLockFactory;
39  
40      @Rule
41      public TestName testName = new TestName();
42  
43      protected String lockName() {
44          return testName.getMethodName();
45      }
46  
47      @Test(expected = IllegalStateException.class)
48      public void testFailure() throws InterruptedException {
49          // note: set system property "aether.named.diagnostic.enabled" to "true" to have log output
50          // this test does NOT assert its presence, only the proper flow
51          Thread t1 = new Thread(() -> {
52              try {
53                  namedLockFactory.getLock(lockName()).lockShared(1L, TimeUnit.MINUTES);
54                  namedLockFactory.getLock(lockName()).lockShared(1L, TimeUnit.MINUTES);
55              } catch (InterruptedException e) {
56                  throw new RuntimeException(e);
57              }
58          });
59          Thread t2 = new Thread(() -> {
60              try {
61                  namedLockFactory.getLock(lockName()).lockShared(1L, TimeUnit.MINUTES);
62                  namedLockFactory.getLock(lockName()).lockShared(1L, TimeUnit.MINUTES);
63                  namedLockFactory.getLock(lockName()).lockShared(1L, TimeUnit.MINUTES);
64              } catch (InterruptedException e) {
65                  throw new RuntimeException(e);
66              }
67          });
68          t1.start();
69          t2.start();
70          t1.join();
71          t2.join();
72          throw namedLockFactory.onFailure(new IllegalStateException("failure"));
73      }
74  
75      @Test
76      public void refCounting() {
77          final String name = lockName();
78          try (NamedLock one = namedLockFactory.getLock(name);
79                  NamedLock two = namedLockFactory.getLock(name)) {
80              assertThat(one, sameInstance(two));
81              one.close();
82              two.close();
83  
84              try (NamedLock three = namedLockFactory.getLock(name)) {
85                  assertThat(three, not(sameInstance(two)));
86              }
87          }
88      }
89  
90      @Test(expected = IllegalStateException.class)
91      public void unlockWoLock() {
92          final String name = lockName();
93          try (NamedLock one = namedLockFactory.getLock(name)) {
94              one.unlock();
95          }
96      }
97  
98      @Test
99      public void wwBoxing() throws InterruptedException {
100         final String name = lockName();
101         try (NamedLock one = namedLockFactory.getLock(name)) {
102             assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
103             assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
104             one.unlock();
105             one.unlock();
106         }
107     }
108 
109     @Test
110     public void rrBoxing() throws InterruptedException {
111         final String name = lockName();
112         try (NamedLock one = namedLockFactory.getLock(name)) {
113             assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
114             assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
115             one.unlock();
116             one.unlock();
117         }
118     }
119 
120     @Test
121     public void wrBoxing() throws InterruptedException {
122         final String name = lockName();
123         try (NamedLock one = namedLockFactory.getLock(name)) {
124             assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
125             assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
126             one.unlock();
127             one.unlock();
128         }
129     }
130 
131     @Test
132     public void rwBoxing() throws InterruptedException {
133         final String name = lockName();
134         try (NamedLock one = namedLockFactory.getLock(name)) {
135             assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
136             try {
137                 one.lockExclusively(1L, TimeUnit.MILLISECONDS);
138             } catch (LockUpgradeNotSupportedException e) {
139                 // good
140             }
141             one.unlock();
142         }
143     }
144 
145     @Test(timeout = 5000)
146     public void sharedAccess() throws InterruptedException {
147         final String name = lockName();
148         CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners
149         CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers
150         Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
151         Thread t2 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
152         t1.start();
153         t2.start();
154         t1.join();
155         t2.join();
156         winners.await();
157         losers.await();
158     }
159 
160     @Test(timeout = 5000)
161     public void exclusiveAccess() throws InterruptedException {
162         final String name = lockName();
163         CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
164         CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
165         Thread t1 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
166         Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
167         t1.start();
168         t2.start();
169         t1.join();
170         t2.join();
171         winners.await();
172         losers.await();
173     }
174 
175     @Test(timeout = 5000)
176     public void mixedAccess() throws InterruptedException {
177         final String name = lockName();
178         CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
179         CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
180         Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
181         Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
182         t1.start();
183         t2.start();
184         t1.join();
185         t2.join();
186         winners.await();
187         losers.await();
188     }
189 
190     @Test(timeout = 5000)
191     public void fullyConsumeLockTime() throws InterruptedException {
192         long start = System.nanoTime();
193         final String name = lockName();
194         CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
195         CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
196         Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
197         Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
198         t1.start();
199         t2.start();
200         t1.join();
201         t2.join();
202         winners.await();
203         losers.await();
204         long end = System.nanoTime();
205         long duration = end - start;
206         long expectedDuration = TimeUnit.MILLISECONDS.toNanos(ACCESS_WAIT_MILLIS);
207         assertThat(duration, greaterThanOrEqualTo(expectedDuration)); // equal in ideal case
208     }
209 
210     @Test(timeout = 5000)
211     public void releasedExclusiveAllowAccess() throws InterruptedException {
212         final String name = lockName();
213         CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
214         CountDownLatch losers = new CountDownLatch(0); // we expect 0 loser
215         Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
216         try (NamedLock namedLock = namedLockFactory.getLock(name)) {
217             assertThat(namedLock.lockExclusively(50L, TimeUnit.MILLISECONDS), is(true));
218             try {
219                 t1.start();
220                 Thread.sleep(50L);
221             } finally {
222                 namedLock.unlock();
223             }
224         }
225         t1.join();
226         winners.await();
227         losers.await();
228     }
229 
230     private static final long ACCESS_WAIT_MILLIS = 1000L;
231 
232     private static class Access implements Runnable {
233         final NamedLockFactory namedLockFactory;
234         final String name;
235         final boolean shared;
236         final CountDownLatch winner;
237         final CountDownLatch loser;
238 
239         public Access(
240                 NamedLockFactory namedLockFactory,
241                 String name,
242                 boolean shared,
243                 CountDownLatch winner,
244                 CountDownLatch loser) {
245             this.namedLockFactory = namedLockFactory;
246             this.name = name;
247             this.shared = shared;
248             this.winner = winner;
249             this.loser = loser;
250         }
251 
252         @Override
253         public void run() {
254             try (NamedLock lock = namedLockFactory.getLock(name)) {
255                 if (shared
256                         ? lock.lockShared(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS)
257                         : lock.lockExclusively(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS)) {
258                     try {
259                         winner.countDown();
260                         loser.await();
261                     } finally {
262                         lock.unlock();
263                     }
264                 } else {
265                     loser.countDown();
266                     winner.await();
267                 }
268             } catch (InterruptedException e) {
269                 Assert.fail(e.getMessage());
270             }
271         }
272     }
273 }