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