Author: manik.surtani(a)jboss.com
Date: 2008-09-25 10:27:07 -0400 (Thu, 25 Sep 2008)
New Revision: 6787
Added:
core/branches/2.2.X/src/main/java/org/jboss/cache/util/concurrent/locks/
core/branches/2.2.X/src/main/java/org/jboss/cache/util/concurrent/locks/UpgradableLock.java
Modified:
core/branches/2.2.X/src/main/java/org/jboss/cache/lock/StripedLock.java
Log:
JBCACHE-1410: JDBCCacheLoader may attempt to create a node entry in the DB twice
Modified: core/branches/2.2.X/src/main/java/org/jboss/cache/lock/StripedLock.java
===================================================================
--- core/branches/2.2.X/src/main/java/org/jboss/cache/lock/StripedLock.java 2008-09-24
11:47:40 UTC (rev 6786)
+++ core/branches/2.2.X/src/main/java/org/jboss/cache/lock/StripedLock.java 2008-09-25
14:27:07 UTC (rev 6787)
@@ -1,13 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.jboss.cache.lock;
import net.jcip.annotations.ThreadSafe;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
+import org.jboss.cache.util.concurrent.locks.UpgradableLock;
import java.util.List;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A simple implementation of lock striping, using Fqns as the keys to lock on, primarily
used to help make
@@ -30,8 +48,7 @@
private final int lockSegmentMask;
private final int lockSegmentShift;
- final ReentrantReadWriteLock[] sharedLocks;
- private final Log log = LogFactory.getLog(StripedLock.class);
+ final UpgradableLock[] sharedLocks;
/**
* This constructor just calls {@link #StripedLock(int)} with a default concurrency
value of 20.
@@ -58,9 +75,9 @@
lockSegmentShift = 32 - tempLockSegShift;
lockSegmentMask = numLocks - 1;
- sharedLocks = new ReentrantReadWriteLock[numLocks];
+ sharedLocks = new UpgradableLock[numLocks];
- for (int i = 0; i < numLocks; i++) sharedLocks[i] = new
ReentrantReadWriteLock();
+ for (int i = 0; i < numLocks; i++) sharedLocks[i] = new UpgradableLock();
}
/**
@@ -71,15 +88,16 @@
*/
public void acquireLock(Fqn fqn, boolean exclusive)
{
- ReentrantReadWriteLock lock = getLock(fqn);
+ UpgradableLock lock = getLock(fqn);
if (exclusive)
{
- // allow for reentrancy
- lock.writeLock().lock();
+ lock.acquireWriteLockWithUpgrade();
}
else
+ {
lock.readLock().lock();
+ }
}
/**
@@ -89,28 +107,11 @@
*/
public void releaseLock(Fqn fqn)
{
- ReentrantReadWriteLock lock = getLock(fqn);
- if (lock.isWriteLockedByCurrentThread())
- {
- lock.writeLock().unlock();
- // check that we still don't have a stale WL
-// if (lock.isWriteLockedByCurrentThread() && log.isWarnEnabled())
-// log.warn("Write lock still exists on Fqn " + fqn + " for
current thread. Perhaps this was write-locked more than once?");
- }
- else
- {
- try
- {
- lock.readLock().unlock();
- }
- catch (IllegalMonitorStateException imse)
- {
- throw new CacheException("Lock is held by a different thread! Cannot be
unlocked by current thread.", imse);
- }
- }
+ UpgradableLock lock = getLock(fqn);
+ lock.unlock();
}
- final ReentrantReadWriteLock getLock(Object o)
+ final UpgradableLock getLock(Object o)
{
return sharedLocks[hashToIndex(o)];
}
@@ -160,4 +161,4 @@
{
for (Fqn f : fqns) acquireLock(f, exclusive);
}
-}
+}
\ No newline at end of file
Added:
core/branches/2.2.X/src/main/java/org/jboss/cache/util/concurrent/locks/UpgradableLock.java
===================================================================
---
core/branches/2.2.X/src/main/java/org/jboss/cache/util/concurrent/locks/UpgradableLock.java
(rev 0)
+++
core/branches/2.2.X/src/main/java/org/jboss/cache/util/concurrent/locks/UpgradableLock.java 2008-09-25
14:27:07 UTC (rev 6787)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. 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.jboss.cache.util.concurrent.locks;
+
+import org.jboss.cache.util.concurrent.ConcurrentHashSet;
+
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A {@link java.util.concurrent.locks.ReentrantReadWriteLock} with support for acquiring
write locks when the current
+ * thread already has a read lock. Also supports releasing such write locks and
re-acquiring read locks in the process.
+ */
+public class UpgradableLock extends ReentrantReadWriteLock
+{
+ private final Set<Thread> upgraders = new ConcurrentHashSet<Thread>();
+
+ /**
+ * Attempts to upgrade a read lock to a write lock. If no read locks are held, a
write lock is simply attempted.
+ */
+ public void acquireWriteLockWithUpgrade()
+ {
+ boolean upgradeNeeded = true;
+ try
+ {
+ readLock().unlock();
+ }
+ catch (IllegalMonitorStateException imse)
+ {
+ // read lock was never held; no upgrading needed.
+ upgradeNeeded = false;
+ }
+
+ if (upgradeNeeded) upgraders.add(Thread.currentThread());
+
+ writeLock().lock();
+ }
+
+ /**
+ * Unlocks any locks held by the current thread. If a write lock is held, it is
released. If a read lock is held
+ * it is released. This method supports reentrant locks so unlocking repeatedly may
release >1 write lock. It
+ * also supports upgraded locks, i.e., read locks that were upgraded to write locks.
+ */
+ public void unlock()
+ {
+ if (isWriteLockedByCurrentThread())
+ {
+ writeLock().unlock();
+ Thread current;
+ if (!isWriteLockedByCurrentThread() && upgraders.contains((current =
Thread.currentThread())))
+ {
+ // re-acquire the RL.
+ readLock().lock();
+ upgraders.remove(current);
+ }
+ }
+ else
+ {
+ try
+ {
+ readLock().unlock();
+ }
+ catch (IllegalMonitorStateException imse)
+ {
+ // perhaps the RL was already released earlier?
+ }
+ }
+ }
+}
\ No newline at end of file