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.support;
20  
21  import java.util.ArrayDeque;
22  import java.util.Deque;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.locks.ReadWriteLock;
25  
26  import org.eclipse.aether.named.NamedLockKey;
27  
28  /**
29   * Named lock support implementation that is using {@link ReadWriteLock} instances. The adapted lock MUST SUPPORT
30   * reentrancy, non re-entrant locks will NOT work. It is the responsibility of an adapting lock, to ensure that
31   * above lock requirement stands.
32   */
33  public class ReadWriteLockNamedLock extends NamedLockSupport {
34      private enum Step {
35          /**
36           * Step when {@link ReadWriteLock#readLock()} was locked
37           */
38          SHARED,
39  
40          /**
41           * Step when {@link ReadWriteLock#writeLock()} was locked
42           */
43          EXCLUSIVE
44      }
45  
46      private final ThreadLocal<Deque<Step>> threadSteps;
47  
48      private final ReadWriteLock readWriteLock;
49  
50      public ReadWriteLockNamedLock(
51              final NamedLockKey key, final NamedLockFactorySupport factory, final ReadWriteLock readWriteLock) {
52          super(key, factory);
53          this.threadSteps = ThreadLocal.withInitial(ArrayDeque::new);
54          this.readWriteLock = readWriteLock;
55      }
56  
57      @Override
58      protected boolean doLockShared(final long time, final TimeUnit unit) throws InterruptedException {
59          Deque<Step> steps = threadSteps.get();
60          if (readWriteLock.readLock().tryLock(time, unit)) {
61              steps.push(Step.SHARED);
62              return true;
63          }
64          return false;
65      }
66  
67      @Override
68      protected boolean doLockExclusively(final long time, final TimeUnit unit) throws InterruptedException {
69          Deque<Step> steps = threadSteps.get();
70          if (!steps.isEmpty()) { // we already own shared or exclusive lock
71              if (!steps.contains(Step.EXCLUSIVE)) {
72                  throw new LockUpgradeNotSupportedException(this); // Lock upgrade not supported
73              }
74          }
75          if (readWriteLock.writeLock().tryLock(time, unit)) {
76              steps.push(Step.EXCLUSIVE);
77              return true;
78          }
79          return false;
80      }
81  
82      @Override
83      protected void doUnlock() {
84          Deque<Step> steps = threadSteps.get();
85          if (steps.isEmpty()) {
86              throw new IllegalStateException("Wrong API usage: unlock without lock");
87          }
88          Step step = steps.pop();
89          if (Step.SHARED == step) {
90              readWriteLock.readLock().unlock();
91          } else if (Step.EXCLUSIVE == step) {
92              readWriteLock.writeLock().unlock();
93          }
94      }
95  }