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  
25  import org.eclipse.aether.named.NamedLockKey;
26  
27  /**
28   * Named lock support implementation that is using "adapted" semaphore (to be able to use semaphores not sharing common
29   * API).
30   */
31  public class AdaptedSemaphoreNamedLock extends NamedLockSupport {
32      /**
33       * Wrapper for semaphore-like stuff, that do not share common ancestor. Semaphore must be created to support {@link
34       * Integer#MAX_VALUE} permissions.
35       */
36      public interface AdaptedSemaphore {
37          boolean tryAcquire(int perms, long time, TimeUnit unit) throws InterruptedException;
38  
39          void release(int perms);
40      }
41  
42      /**
43       * Count of permissions involved with "nop" locking. When required lock step is preceded with a step that already
44       * fulfills currently requested locking, no locking is needed. In other words, caller already possesses the access
45       * to lock protected resource. The "nop" locking is used to track proper "boxing" of lock/unlock calls.
46       */
47      private static final int NONE = 0;
48  
49      /**
50       * Count of permissions involved with shared locking
51       */
52      private static final int SHARED = 1;
53  
54      /**
55       * Count of permissions involved with exclusive locking
56       */
57      private static final int EXCLUSIVE = Integer.MAX_VALUE;
58  
59      private final ThreadLocal<Deque<Integer>> threadPerms;
60  
61      private final AdaptedSemaphore semaphore;
62  
63      public AdaptedSemaphoreNamedLock(
64              final NamedLockKey key, final NamedLockFactorySupport factory, final AdaptedSemaphore semaphore) {
65          super(key, factory);
66          this.threadPerms = ThreadLocal.withInitial(ArrayDeque::new);
67          this.semaphore = semaphore;
68      }
69  
70      @Override
71      protected boolean doLockShared(final long time, final TimeUnit unit) throws InterruptedException {
72          Deque<Integer> perms = threadPerms.get();
73          if (!perms.isEmpty()) { // we already own shared or exclusive lock
74              perms.push(NONE);
75              return true;
76          }
77          if (semaphore.tryAcquire(SHARED, time, unit)) {
78              perms.push(SHARED);
79              return true;
80          }
81          return false;
82      }
83  
84      @Override
85      protected boolean doLockExclusively(final long time, final TimeUnit unit) throws InterruptedException {
86          Deque<Integer> perms = threadPerms.get();
87          if (!perms.isEmpty()) { // we already own shared or exclusive lock
88              if (perms.contains(EXCLUSIVE)) {
89                  perms.push(NONE);
90                  return true;
91              } else {
92                  throw new LockUpgradeNotSupportedException(this); // Lock upgrade not supported
93              }
94          }
95          if (semaphore.tryAcquire(EXCLUSIVE, time, unit)) {
96              perms.push(EXCLUSIVE);
97              return true;
98          }
99          return false;
100     }
101 
102     @Override
103     protected void doUnlock() {
104         Deque<Integer> steps = threadPerms.get();
105         if (steps.isEmpty()) {
106             throw new IllegalStateException("Wrong API usage: unlock without lock");
107         }
108         int step = steps.pop();
109         if (step > NONE) {
110             semaphore.release(step);
111         }
112     }
113 }