View Javadoc
1   package org.eclipse.aether.named.support;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayDeque;
23  import java.util.Deque;
24  import java.util.concurrent.TimeUnit;
25  
26  /**
27   * Named lock support implementation that is using "adapted" semaphore (to be able to use semaphores not sharing common
28   * API).
29   */
30  public class AdaptedSemaphoreNamedLock
31      extends NamedLockSupport
32  {
33      /**
34       * Wrapper for semaphore-like stuff, that do not share common ancestor. Semaphore must be created to support {@link
35       * Integer#MAX_VALUE} permissions.
36       */
37      public interface AdaptedSemaphore
38      {
39          boolean tryAcquire( int perms, long time, TimeUnit unit ) throws InterruptedException;
40  
41          void release( int perms );
42      }
43  
44      /**
45       * Count of permissions involved with "nop" locking. When required lock step is preceded with a step that already
46       * fulfills currently requested locking, no locking is needed. In other words, caller already possesses the access
47       * to lock protected resource. The "nop" locking is used to track proper "boxing" of lock/unlock calls.
48       */
49      private static final int NONE = 0;
50  
51      /**
52       * Count of permissions involved with shared locking
53       */
54      private static final int SHARED = 1;
55  
56      /**
57       * Count of permissions involved with exclusive locking
58       */
59      private static final int EXCLUSIVE = Integer.MAX_VALUE;
60  
61      private final ThreadLocal<Deque<Integer>> threadPerms;
62  
63      private final AdaptedSemaphore semaphore;
64  
65      public AdaptedSemaphoreNamedLock( final String name,
66                                        final NamedLockFactorySupport factory,
67                                        final AdaptedSemaphore semaphore )
68      {
69          super( name, factory );
70          this.threadPerms = ThreadLocal.withInitial( ArrayDeque::new );
71          this.semaphore = semaphore;
72      }
73  
74      @Override
75      public boolean lockShared( final long time, final TimeUnit unit ) throws InterruptedException
76      {
77          Deque<Integer> perms = threadPerms.get();
78          if ( !perms.isEmpty() )
79          { // we already own shared or exclusive lock
80              perms.push( NONE );
81              return true;
82          }
83          if ( semaphore.tryAcquire( SHARED, time, unit ) )
84          {
85              perms.push( SHARED );
86              return true;
87          }
88          return false;
89      }
90  
91      @Override
92      public boolean lockExclusively( final long time, final TimeUnit unit ) throws InterruptedException
93      {
94          Deque<Integer> perms = threadPerms.get();
95          if ( !perms.isEmpty() )
96          { // we already own shared or exclusive lock
97              if ( perms.contains( EXCLUSIVE ) )
98              {
99                  perms.push( NONE );
100                 return true;
101             }
102             else
103             {
104                 return false; // Lock upgrade not supported
105             }
106         }
107         if ( semaphore.tryAcquire( EXCLUSIVE, time, unit ) )
108         {
109             perms.push( EXCLUSIVE );
110             return true;
111         }
112         return false;
113     }
114 
115     @Override
116     public void unlock()
117     {
118         Deque<Integer> steps = threadPerms.get();
119         if ( steps.isEmpty() )
120         {
121             throw new IllegalStateException( "Wrong API usage: unlock w/o lock" );
122         }
123         int step = steps.pop();
124         if ( step > NONE )
125         {
126             semaphore.release( step );
127         }
128     }
129 }