/*
* JBoss, Home of Professional Open Source
* Copyright 2009 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.util.concurrent.locks.containers;
import net.jcip.annotations.ThreadSafe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* A container for locks. Used with lock striping.
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 4.0
*/
@ThreadSafe
public abstract class AbstractStripedLockContainer<L extends Lock> extends AbstractLockContainer<L> {
private int lockSegmentMask;
private int lockSegmentShift;
protected final int calculateNumberOfSegments(int concurrencyLevel) {
int tempLockSegShift = 0;
int numLocks = 1;
while (numLocks < concurrencyLevel) {
++tempLockSegShift;
numLocks <<= 1;
}
lockSegmentShift = 32 - tempLockSegShift;
lockSegmentMask = numLocks - 1;
return numLocks;
}
protected final int hashToIndex(Object object) {
return (hash(object) >>> lockSegmentShift) & lockSegmentMask;
}
/**
* Returns a hash code for non-null Object x. Uses the same hash code spreader as most other java.util hash tables,
* except that this uses the string representation of the object passed in.
*
* @param object the object serving as a key
* @return the hash code
*/
static final int hash(Object object) {
int h = object.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
protected abstract void initLocks(int numLocks);
@Override
public L acquireExclusiveLock(Object lockOwner, Object key, long timeout, TimeUnit unit) throws InterruptedException {
return acquire(lockOwner, key, timeout, unit, false);
}
@Override
public L acquireShareLock(Object lockOwner, Object key, long timeout, TimeUnit unit) throws InterruptedException {
return acquire(lockOwner, key, timeout, unit, true);
}
@Override
public void releaseExclusiveLock(Object lockOwner, Object key) {
final L lock = getExclusiveLock(key);
safeExclusiveRelease(lock, lockOwner);
}
@Override
public void releaseShareLock(Object lockOwner, Object key) {
final L lock = getShareLock(key);
safeExclusiveRelease(lock, lockOwner);
}
private L acquire(Object lockOwner, Object key, long timeout, TimeUnit unit, boolean share) throws InterruptedException {
L lock = share ? getShareLock(key) : getExclusiveLock(key);
boolean locked;
try {
locked = share ? tryShareLock(lock, timeout, unit, lockOwner) : tryExclusiveLock(lock, timeout, unit, lockOwner);
} catch (InterruptedException ie) {
if (share) {
safeShareRelease(lock, lockOwner);
} else {
safeExclusiveRelease(lock, lockOwner);
}
throw ie;
} catch (Throwable th) {
if (share) {
safeShareRelease(lock, lockOwner);
} else {
safeExclusiveRelease(lock, lockOwner);
}
locked = false;
}
return locked ? lock : null;
}
@Override
public int getLockId(Object key) {
return hashToIndex(key);
}
}