001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.global;
042:
043: //import java.util.logging.Level;
044: //import java.util.logging.Logger;
045:
046: /**
047: *
048: * @author Jaroslav Bachorik
049: */
050: public class TransactionalSupport {
051: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
052:
053: static final boolean DEBUG = System
054: .getProperty(TransactionalSupport.class.getName()) != null;
055:
056: //~ Instance fields ----------------------------------------------------------------------------------------------------------
057:
058: private final Object transactionLock = new Object();
059: private final ThreadLocal interruptedFlag = new ThreadLocal();
060: private final ThreadLocal lockRead = new ThreadLocal();
061: private final ThreadLocal lockWrite = new ThreadLocal();
062:
063: // final static private Logger LOGGER = Logger.getLogger(TransactionalSupport.class.getName());
064: private boolean lockedExclusively = false;
065:
066: // final static private Logger LOGGER = Logger.getLogger(TransactionalSupport.class.getName());
067: private boolean lockedShared = false;
068: private int sharedLockCount = 0;
069:
070: //~ Methods ------------------------------------------------------------------------------------------------------------------
071:
072: public void beginTrans(boolean mutable) {
073: beginTrans(mutable, false);
074: }
075:
076: public boolean beginTrans(boolean mutable, boolean failEarly) {
077: if (DEBUG) {
078: System.out.println("DEBUG: ["
079: + Thread.currentThread().getName()
080: + "] Starting transaction: mutable = " + mutable
081: + ", failEarly = " + failEarly); // NOI18N
082: }
083:
084: synchronized (transactionLock) {
085: boolean result = false;
086:
087: do {
088: if (mutable) {
089: result = lockExclusively();
090: } else {
091: result = lockShared();
092: }
093:
094: if (!result && !failEarly) {
095: if (DEBUG) {
096: System.out
097: .println("DEBUG: ["
098: + Thread.currentThread()
099: .getName()
100: + "] Couldn't start transaction. Going to wait for some time"); // NOI18N
101: }
102:
103: boolean interrupted = false;
104:
105: do {
106: interrupted = false;
107:
108: try {
109: transactionLock.wait();
110: } catch (InterruptedException e) {
111: interruptedFlag.set(new Object());
112: interrupted = true;
113: Thread.interrupted();
114: }
115: } while (interrupted);
116: }
117: } while (!result && !failEarly);
118:
119: return result;
120: }
121: }
122:
123: public void endTrans() {
124: synchronized (transactionLock) {
125: Integer roCounter = (Integer) lockRead.get();
126: Integer rwCounter = (Integer) lockWrite.get();
127:
128: if (roCounter == null) {
129: unlockShared();
130: } else {
131: if (DEBUG) {
132: System.out
133: .println("DEBUG: ["
134: + Thread.currentThread().getName()
135: + "] Releasing ownership for a shared lock"); // NOI18N
136: }
137:
138: int counter = roCounter.intValue();
139:
140: if (counter > 1) {
141: lockRead.set(new Integer(counter - 1));
142: } else {
143: lockRead.set(null);
144: unlockShared();
145: }
146: }
147:
148: if (rwCounter == null) {
149: if (DEBUG) {
150: System.out.println("DEBUG: ["
151: + Thread.currentThread().getName()
152: + "] Unlocking an exclusive lock"); // NOI18N
153: }
154:
155: lockedExclusively = false;
156: } else {
157: if (DEBUG) {
158: System.out
159: .println("DEBUG: ["
160: + Thread.currentThread().getName()
161: + "] Releasing ownership for an exclusive lock"); // NOI18N
162: }
163:
164: int counter = rwCounter.intValue();
165:
166: if (counter > 1) {
167: lockWrite.set(new Integer(counter - 1));
168: } else {
169: lockWrite.set(null);
170:
171: if (DEBUG) {
172: System.out.println("DEBUG: ["
173: + Thread.currentThread().getName()
174: + "] Unlocking an exclusive lock"); // NOI18N
175: }
176:
177: lockedExclusively = false;
178: }
179: }
180:
181: transactionLock.notifyAll();
182: }
183:
184: rethrowInterrupt();
185: }
186:
187: private boolean lockExclusively() {
188: if (lockedShared) {
189: return promoteToExclusive();
190: }
191:
192: if (lockedExclusively) {
193: return relockExclusively();
194: } else {
195: if (DEBUG) {
196: System.out
197: .println("DEBUG: ["
198: + Thread.currentThread().getName()
199: + "] Grabbing an exclusive lock for transaction"); // NOI18N
200: }
201:
202: lockedExclusively = true;
203: lockWrite.set(new Integer(1));
204:
205: return true;
206: }
207: }
208:
209: private boolean lockShared() {
210: boolean result;
211:
212: if (lockedExclusively) {
213: return relockExclusively();
214: } else {
215: if (DEBUG) {
216: System.out.println("DEBUG: ["
217: + Thread.currentThread().getName()
218: + "] Grabbing shared lock for transaction"); // NOI18N
219: }
220:
221: lockedShared = true;
222:
223: Integer counter = (Integer) lockRead.get();
224:
225: if (counter == null) {
226: lockRead.set(new Integer(1));
227: sharedLockCount++; // turn the lock counter only if the current thread doesn't own the sahred lock already
228: } else {
229: lockRead.set(new Integer(counter.intValue() + 1));
230: }
231:
232: result = true;
233:
234: return result;
235: }
236: }
237:
238: private boolean promoteToExclusive() {
239: boolean result;
240:
241: if (sharedLockCount > 1) {
242: System.err.println("WARNING: ["
243: + Thread.currentThread().getName()
244: + "] Cant promote a shared lock held by "
245: + sharedLockCount + " threads!"); // NOI18N
246:
247: return false; // can't promote a shared lock held by more threads
248: }
249:
250: Integer counter = (Integer) lockRead.get();
251:
252: if (counter != null) {
253: if (DEBUG) {
254: System.out
255: .println("DEBUG: ["
256: + Thread.currentThread().getName()
257: + "] Promoting a previously owned shared lock to the exclusive one"); // NOI18N
258: }
259:
260: lockedShared = false;
261: sharedLockCount = 0;
262: lockedExclusively = true;
263: lockWrite.set(new Integer(counter.intValue() + 1));
264: lockRead.set(null);
265: result = true;
266: } else {
267: if (DEBUG) {
268: System.out
269: .println("DEBUG: ["
270: + Thread.currentThread().getName()
271: + "] Failed to promote a previously owned shared lock"); // NOI18N
272: }
273:
274: result = false;
275: }
276:
277: return result;
278: }
279:
280: private boolean relockExclusively() {
281: boolean result;
282: Integer counter = (Integer) lockWrite.get();
283:
284: if (counter != null) {
285: if (DEBUG) {
286: System.out
287: .println("DEBUG: ["
288: + Thread.currentThread().getName()
289: + "] Relocking a previously owned exclusive lock"); // NOI18N
290: }
291:
292: lockWrite.set(new Integer(counter.intValue() + 1));
293: result = true;
294: } else {
295: if (DEBUG) {
296: System.out
297: .println("DEBUG: ["
298: + Thread.currentThread().getName()
299: + "] Failed to relock an exclusive lock. Not an owner."); // NOI18N
300: }
301:
302: result = false;
303: }
304:
305: return result;
306: }
307:
308: private void rethrowInterrupt() {
309: if (interruptedFlag.get() != null) {
310: Thread.currentThread().interrupt();
311: interruptedFlag.set(null);
312: }
313: }
314:
315: private void unlockShared() {
316: if (DEBUG) {
317: System.out.println("DEBUG: ["
318: + Thread.currentThread().getName()
319: + "] Unlocking a shared lock"); // NOI18N
320: }
321:
322: lockedShared = false;
323:
324: if (sharedLockCount > 0) {
325: sharedLockCount--;
326: }
327: }
328: }
|