// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.utils.db; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import com.cloud.utils.Profiler; // // Wrapper class for global database lock to reduce contention for database connections from within process // // Example of using dynamic named locks // // GlobalLock lock = GlobalLock.getInternLock("some table name" + rowId); // // if(lock.lock()) { // try { // do something // } finally { // lock.unlock(); // } // } // lock.releaseRef(); // public class GlobalLock { protected final static Logger s_logger = Logger.getLogger(GlobalLock.class); private String name; private int lockCount = 0; private Thread ownerThread = null; private int referenceCount = 0; private long holdingStartTick = 0; private static Map<String, GlobalLock> s_lockMap = new HashMap<String, GlobalLock>(); private GlobalLock(String name) { this.name = name; } public int addRef() { synchronized(this) { referenceCount++; return referenceCount; } } public int releaseRef() { int refCount; boolean needToRemove = false; synchronized(this) { referenceCount--; refCount = referenceCount; if(referenceCount < 0) s_logger.warn("Unmatched Global lock " + name + " reference usage detected, check your code!"); if(referenceCount == 0) needToRemove = true; } if(needToRemove) releaseInternLock(name); return refCount; } public static GlobalLock getInternLock(String name) { synchronized(s_lockMap) { if(s_lockMap.containsKey(name)) { GlobalLock lock = s_lockMap.get(name); lock.addRef(); return lock; } else { GlobalLock lock = new GlobalLock(name); lock.addRef(); s_lockMap.put(name, lock); return lock; } } } private static void releaseInternLock(String name) { synchronized(s_lockMap) { GlobalLock lock = s_lockMap.get(name); if(lock != null) { if(lock.referenceCount == 0) s_lockMap.remove(name); } else { s_logger.warn("Releasing " + name + ", but it is already released."); } } } public boolean lock(int timeoutSeconds) { int remainingMilliSeconds = timeoutSeconds*1000; Profiler profiler = new Profiler(); boolean interrupted = false; try { while(true) { synchronized(this) { if(ownerThread != null && ownerThread == Thread.currentThread()) { s_logger.warn("Global lock re-entrance detected"); lockCount++; if(s_logger.isTraceEnabled()) s_logger.trace("lock " + name + " is acquired, lock count :" + lockCount); return true; } if(ownerThread != null) { profiler.start(); try { wait(((long)timeoutSeconds)*1000L); } catch (InterruptedException e) { interrupted = true; } profiler.stop(); remainingMilliSeconds -= profiler.getDuration(); if(remainingMilliSeconds < 0) return false; continue; } else { // take ownership temporarily to prevent others enter into stage of acquiring DB lock ownerThread = Thread.currentThread(); addRef(); } } if(DbUtil.getGlobalLock(name, remainingMilliSeconds / 1000)) { synchronized(this) { lockCount++; holdingStartTick = System.currentTimeMillis(); if(s_logger.isTraceEnabled()) s_logger.trace("lock " + name + " is acquired, lock count :" + lockCount); return true; } } else { synchronized(this) { ownerThread = null; releaseRef(); return false; } } } } finally { if(interrupted) { Thread.currentThread().interrupt(); } } } public boolean unlock() { synchronized(this) { if(ownerThread != null && ownerThread == Thread.currentThread()) { lockCount--; if(lockCount == 0) { ownerThread = null; DbUtil.releaseGlobalLock(name); if(s_logger.isTraceEnabled()) s_logger.trace("lock " + name + " is returned to free state, total holding time :" + (System.currentTimeMillis() - holdingStartTick)); holdingStartTick = 0; // release holding position in intern map when we released the DB connection releaseRef(); notifyAll(); } if(s_logger.isTraceEnabled()) s_logger.trace("lock " + name + " is released, lock count :" + lockCount); return true; } return false; } } public String getName() { return name; } }