/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.procedure2; /** * Locking for mutual exclusion between procedures. Used only by procedure framework internally. * {@link LockAndQueue} has two purposes: * <ol> * <li>Acquire/release exclusive/shared locks.</li> * <li>Maintains a list of procedures waiting on this lock. * {@link LockAndQueue} extends {@link ProcedureDeque} class. Blocked Procedures are added * to our super Deque. Using inheritance over composition to keep the Deque of waiting * Procedures is unusual, but we do it this way because in certain cases, there will be * millions of regions. This layout uses less memory. * </ol> * * <p>NOT thread-safe. Needs external concurrency control: e.g. uses in MasterProcedureScheduler are * guarded by schedLock(). * <br> * There is no need of 'volatile' keyword for member variables because of memory synchronization * guarantees of locks (see 'Memory Synchronization', * http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html) * <br> * We do not implement Lock interface because we need exclusive and shared locking, and also * because try-lock functions require procedure id. * <br> * We do not use ReentrantReadWriteLock directly because of its high memory overhead. */ public class LockAndQueue extends ProcedureDeque implements LockStatus { private Procedure<?> exclusiveLockOwnerProcedure = null; private int sharedLock = 0; // ====================================================================== // Lock Status // ====================================================================== @Override public boolean isLocked() { return hasExclusiveLock() || sharedLock > 0; } @Override public boolean hasExclusiveLock() { return this.exclusiveLockOwnerProcedure != null; } @Override public boolean isLockOwner(long procId) { return getExclusiveLockProcIdOwner() == procId; } @Override public boolean hasParentLock(final Procedure proc) { return proc.hasParent() && (isLockOwner(proc.getParentProcId()) || isLockOwner(proc.getRootProcId())); } @Override public boolean hasLockAccess(final Procedure proc) { return isLockOwner(proc.getProcId()) || hasParentLock(proc); } @Override public Procedure<?> getExclusiveLockOwnerProcedure() { return exclusiveLockOwnerProcedure; } @Override public long getExclusiveLockProcIdOwner() { if (exclusiveLockOwnerProcedure == null) { return Long.MIN_VALUE; } else { return exclusiveLockOwnerProcedure.getProcId(); } } @Override public int getSharedLockCount() { return sharedLock; } // ====================================================================== // try/release Shared/Exclusive lock // ====================================================================== public boolean trySharedLock() { if (hasExclusiveLock()) return false; sharedLock++; return true; } public boolean releaseSharedLock() { return --sharedLock == 0; } public boolean tryExclusiveLock(final Procedure proc) { if (isLocked()) return hasLockAccess(proc); exclusiveLockOwnerProcedure = proc; return true; } /** * @return True if we released a lock. */ public boolean releaseExclusiveLock(final Procedure proc) { if (isLockOwner(proc.getProcId())) { exclusiveLockOwnerProcedure = null; return true; } return false; } @Override public String toString() { return "exclusiveLockOwner=" + (hasExclusiveLock()? getExclusiveLockProcIdOwner(): "NONE") + ", sharedLockCount=" + getSharedLockCount() + ", waitingProcCount=" + size(); } }