001package org.eclipse.aether.named.support; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024import java.util.concurrent.TimeUnit; 025 026/** 027 * Named lock support implementation that is using "adapted" semaphore (to be able to use semaphores not sharing common 028 * API). 029 */ 030public class AdaptedSemaphoreNamedLock 031 extends NamedLockSupport 032{ 033 /** 034 * Wrapper for semaphore-like stuff, that do not share common ancestor. Semaphore must be created to support {@link 035 * Integer#MAX_VALUE} permissions. 036 */ 037 public interface AdaptedSemaphore 038 { 039 boolean tryAcquire( int perms, long time, TimeUnit unit ) throws InterruptedException; 040 041 void release( int perms ); 042 } 043 044 /** 045 * Count of permissions involved with "nop" locking. When required lock step is preceded with a step that already 046 * fulfills currently requested locking, no locking is needed. In other words, caller already possesses the access 047 * to lock protected resource. The "nop" locking is used to track proper "boxing" of lock/unlock calls. 048 */ 049 private static final int NONE = 0; 050 051 /** 052 * Count of permissions involved with shared locking 053 */ 054 private static final int SHARED = 1; 055 056 /** 057 * Count of permissions involved with exclusive locking 058 */ 059 private static final int EXCLUSIVE = Integer.MAX_VALUE; 060 061 private final ThreadLocal<Deque<Integer>> threadPerms; 062 063 private final AdaptedSemaphore semaphore; 064 065 public AdaptedSemaphoreNamedLock( final String name, 066 final NamedLockFactorySupport factory, 067 final AdaptedSemaphore semaphore ) 068 { 069 super( name, factory ); 070 this.threadPerms = ThreadLocal.withInitial( ArrayDeque::new ); 071 this.semaphore = semaphore; 072 } 073 074 @Override 075 public boolean lockShared( final long time, final TimeUnit unit ) throws InterruptedException 076 { 077 Deque<Integer> perms = threadPerms.get(); 078 if ( !perms.isEmpty() ) 079 { // we already own shared or exclusive lock 080 perms.push( NONE ); 081 return true; 082 } 083 if ( semaphore.tryAcquire( SHARED, time, unit ) ) 084 { 085 perms.push( SHARED ); 086 return true; 087 } 088 return false; 089 } 090 091 @Override 092 public boolean lockExclusively( final long time, final TimeUnit unit ) throws InterruptedException 093 { 094 Deque<Integer> perms = threadPerms.get(); 095 if ( !perms.isEmpty() ) 096 { // we already own shared or exclusive lock 097 if ( perms.contains( EXCLUSIVE ) ) 098 { 099 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 without lock" ); 122 } 123 int step = steps.pop(); 124 if ( step > NONE ) 125 { 126 semaphore.release( step ); 127 } 128 } 129}