/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.worker.block; import alluxio.Configuration; import alluxio.PropertyKey; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import javax.annotation.concurrent.ThreadSafe; /** * Read/write lock associated with clients rather than threads. Either its read lock or write lock * can be released by a thread different from the one acquiring them (but supposed to be requested * by the same client). */ @ThreadSafe public final class ClientRWLock implements ReadWriteLock { /** Total number of permits. This value decides the max number of concurrent readers. */ private static final int MAX_AVAILABLE = Configuration.getInt(PropertyKey.WORKER_TIERED_STORE_BLOCK_LOCK_READERS); /** * Uses the unfair lock to prevent a read lock that fails to release from locking the block * forever and thus blocking all the subsequent write access. * See https://alluxio.atlassian.net/browse/ALLUXIO-2636. */ private final Semaphore mAvailable = new Semaphore(MAX_AVAILABLE, false); /** Reference count. */ private AtomicInteger mReferences = new AtomicInteger(); /** * Constructs a new {@link ClientRWLock}. */ public ClientRWLock() {} @Override public Lock readLock() { return new SessionLock(1); } @Override public Lock writeLock() { return new SessionLock(MAX_AVAILABLE); } /** * @return the reference count */ public int getReferenceCount() { return mReferences.get(); } /** * Increments the reference count. */ public void addReference() { mReferences.incrementAndGet(); } /** * Decrements the reference count. * * @return the new reference count */ public int dropReference() { return mReferences.decrementAndGet(); } private final class SessionLock implements Lock { private final int mPermits; private SessionLock(int permits) { mPermits = permits; } @Override public void lock() { mAvailable.acquireUninterruptibly(mPermits); } @Override public void lockInterruptibly() throws InterruptedException { mAvailable.acquire(mPermits); } @Override public boolean tryLock() { return mAvailable.tryAcquire(mPermits); } @Override public boolean tryLock(long time, TimeUnit unit) { try { return mAvailable.tryAcquire(mPermits, time, unit); } catch (InterruptedException e) { return false; } } @Override public void unlock() { mAvailable.release(mPermits); } @Override public Condition newCondition() { throw new UnsupportedOperationException("newCondition() is not supported"); } } }