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 extends NamedLockSupport
31  {
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      {
38          boolean tryAcquire( int perms, long time, TimeUnit unit ) throws InterruptedException;
39  
40          void release( int perms );
41      }
42  
43      /**
44       * Count of permissions involved with "nop" locking. When required lock step is preceded with a step that already
45       * fulfils currently requested locking, no locking is needed. In other words, caller already possesses the access to
46       * lock protected resource. The "nop" locking is used to track proper "boxing" of lock/unlock calls.
47       */
48      private static final int NONE = 0;
49  
50      /**
51       * Count of permissions involved with shared locking
52       */
53      private static final int SHARED = 1;
54  
55      /**
56       * Count of permissions involved with exclusive locking
57       */
58      private static final int EXCLUSIVE = Integer.MAX_VALUE;
59  
60      private final ThreadLocal<Deque<Integer>> threadPerms;
61  
62      private final AdaptedSemaphore semaphore;
63  
64      public AdaptedSemaphoreNamedLock( final String name, final NamedLockFactorySupport factory,
65                                        final AdaptedSemaphore semaphore )
66      {
67          super( name, factory );
68          this.threadPerms = ThreadLocal.withInitial( ArrayDeque::new );
69          this.semaphore = semaphore;
70      }
71  
72      @Override
73      public boolean lockShared( final long time, final TimeUnit unit ) throws InterruptedException
74      {
75          Deque<Integer> perms = threadPerms.get();
76          if ( !perms.isEmpty() )
77          { // we already own shared or exclusive lock
78              perms.push( NONE );
79              return true;
80          }
81          if ( semaphore.tryAcquire( SHARED, time, unit ) )
82          {
83              perms.push( SHARED );
84              return true;
85          }
86          return false;
87      }
88  
89      @Override
90      public boolean lockExclusively( final long time, final TimeUnit unit ) throws InterruptedException
91      {
92          Deque<Integer> perms = threadPerms.get();
93          if ( !perms.isEmpty() )
94          { // we already own shared or exclusive lock
95              if ( perms.contains( EXCLUSIVE ) )
96              {
97                  perms.push( NONE );
98                  return true;
99              }
100             else
101             {
102                 return false; // Lock upgrade not supported
103             }
104         }
105         if ( semaphore.tryAcquire( EXCLUSIVE, time, unit ) )
106         {
107             perms.push( EXCLUSIVE );
108             return true;
109         }
110         return false;
111     }
112 
113     @Override
114     public void unlock()
115     {
116         Deque<Integer> steps = threadPerms.get();
117         if ( steps.isEmpty() )
118         {
119             throw new IllegalStateException( "Wrong API usage: unlock w/o lock" );
120         }
121         int step = steps.pop();
122         if ( step > 0 )
123         {
124             semaphore.release( step );
125         }
126     }
127 }