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