001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.lock;
023:
024: import org.jboss.util.deadlock.ApplicationDeadlockException;
025: import org.jboss.util.deadlock.Resource;
026: import org.jboss.util.deadlock.DeadlockDetector;
027:
028: import javax.transaction.Transaction;
029:
030: /**
031: * Implementents a non reentrant lock with deadlock detection
032: * <p/>
033: * It will throw a ReentranceException if the same thread tries to acquire twice
034: * or the same transaction tries to acquire twice
035: *
036: * @author <a href="bill@jboss.org">Bill Burke</a>
037: * @author <a href="alex@jboss.org">Alexey Loubyansky</a>
038: * @version $Revision: 57209 $
039: */
040: public class NonReentrantLock implements Resource {
041: public static class ReentranceException extends Exception {
042: public ReentranceException() {
043: }
044:
045: public ReentranceException(String message) {
046: super (message);
047: }
048: }
049:
050: //private static final Logger log = Logger.getLogger(NonReentrantLock.class);
051:
052: protected Thread lockHolder;
053: protected Object lock = new Object();
054: protected volatile int held = 0;
055: protected Transaction holdingTx = null;
056: private boolean inNonReentrant;
057:
058: public Object getResourceHolder() {
059: if (holdingTx != null)
060: return holdingTx;
061: return lockHolder;
062: }
063:
064: protected boolean acquireNonReentrant(long waitTime,
065: Transaction miTx) throws ApplicationDeadlockException,
066: InterruptedException, ReentranceException {
067: synchronized (lock) {
068: final Thread curThread = Thread.currentThread();
069: if (lockHolder != null) {
070: if (lockHolder == curThread) {
071: if (inNonReentrant) {
072: throw new ReentranceException(
073: "The same thread reentered: thread-holder="
074: + lockHolder + ", holding tx="
075: + holdingTx + ", current tx="
076: + miTx);
077: }
078: } else if (miTx != null && miTx.equals(holdingTx)) {
079: if (inNonReentrant) {
080: throw new ReentranceException(
081: "The same tx reentered: tx=" + miTx
082: + ", holding thread="
083: + lockHolder
084: + ", current thread="
085: + curThread);
086: }
087: } else {
088: // Always upgrade deadlock holder to Tx so that we can detect lock properly
089: Object deadlocker = curThread;
090: if (miTx != null)
091: deadlocker = miTx;
092: try {
093: DeadlockDetector.singleton.deadlockDetection(
094: deadlocker, this );
095: while (lockHolder != null) {
096: if (waitTime < 1) {
097: lock.wait();
098: } else {
099: lock.wait(waitTime);
100: }
101: // If we waited and never got lock, abort
102: if (waitTime > 0 && lockHolder != null)
103: return false;
104: }
105: } finally {
106: DeadlockDetector.singleton
107: .removeWaiting(deadlocker);
108: }
109: }
110: }
111:
112: ++held;
113: lockHolder = curThread;
114: holdingTx = miTx;
115: inNonReentrant = true;
116: }
117: return true;
118: }
119:
120: protected boolean acquireReentrant(long waitTime, Transaction miTx)
121: throws ApplicationDeadlockException, InterruptedException,
122: ReentranceException {
123: synchronized (lock) {
124: final Thread curThread = Thread.currentThread();
125: if (lockHolder != null) {
126: if (lockHolder != curThread
127: && (miTx == null || miTx.equals(holdingTx))) {
128: // Always upgrade deadlock holder to Tx so that we can detect lock properly
129: Object deadlocker = curThread;
130: if (miTx != null)
131: deadlocker = miTx;
132: try {
133: DeadlockDetector.singleton.deadlockDetection(
134: deadlocker, this );
135: while (lockHolder != null) {
136: if (waitTime < 1) {
137: lock.wait();
138: } else {
139: lock.wait(waitTime);
140: }
141: // If we waited and never got lock, abort
142: if (waitTime > 0 && lockHolder != null)
143: return false;
144: }
145: } finally {
146: DeadlockDetector.singleton
147: .removeWaiting(deadlocker);
148: }
149: }
150: }
151:
152: ++held;
153: lockHolder = curThread;
154: holdingTx = miTx;
155: }
156: return true;
157: }
158:
159: public boolean attempt(long waitTime, Transaction miTx,
160: boolean nonReentrant) throws ApplicationDeadlockException,
161: InterruptedException, ReentranceException {
162: return nonReentrant ? acquireNonReentrant(waitTime, miTx)
163: : acquireReentrant(waitTime, miTx);
164: }
165:
166: public void release(boolean nonReentrant) {
167: synchronized (lock) {
168: held--;
169: if (held < 0) {
170: throw new IllegalStateException(
171: "Released lock too many times");
172: } else if (held == 0) {
173: lockHolder = null;
174: holdingTx = null;
175: lock.notify();
176: }
177:
178: if (nonReentrant) {
179: inNonReentrant = false;
180: }
181: }
182: }
183: }
|