001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Contact: sequoia@continuent.org
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: * Initial developer(s): Emmanuel Cecchet.
021: * Contributor(s): _________________________.
022: */package org.continuent.sequoia.controller.scheduler.schema;
023:
024: import java.util.Iterator;
025: import java.util.LinkedList;
026:
027: import org.continuent.sequoia.controller.requests.AbstractRequest;
028:
029: /**
030: * A <code>TransactionExclusiveLock</code> is an exclusive lock that let the
031: * owner of the lock acquire several times the lock (but it needs to be released
032: * only once). Acquire supports timeout and graceful withdrawal of timed out
033: * requests.
034: *
035: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
036: * @version 1.0
037: */
038: public class TransactionExclusiveLock {
039: private boolean isLocked = false;
040:
041: /** Transaction id of the lock holder. */
042: private long locker;
043:
044: /** <code>ArrayList</code> of <code>WaitingListElement</code>. */
045: private LinkedList waitingList = new LinkedList();
046:
047: /**
048: * The element stored in the waiting list is the waiting thread and the
049: * transaction id of the request waiting.
050: */
051: private class WaitingListElement {
052: /** Waiting thread */
053: Thread thread;
054:
055: /** Transaction id of the request waiting. */
056: long transactionId;
057:
058: /**
059: * Creates a new <code>WaitingListElement</code> instance.
060: *
061: * @param thread the waiting thread.
062: * @param transactionId the transaction id of the request waiting.
063: */
064: WaitingListElement(Thread thread, long transactionId) {
065: this .thread = thread;
066: this .transactionId = transactionId;
067: }
068:
069: /**
070: * Returns the transaction id of the request waiting.
071: *
072: * @return an <code>int</code> value
073: */
074: public long getTransactionId() {
075: return transactionId;
076: }
077:
078: /**
079: * Returns the waiting thread.
080: *
081: * @return a <code>Thread</code> value
082: */
083: public Thread getThread() {
084: return thread;
085: }
086: }
087:
088: /**
089: * Acquires an exclusive lock on this table. If the lock is already held by
090: * the same transaction as the given request, this method is non-blocking else
091: * the caller is blocked until the transaction holding the lock releases it at
092: * commit/rollback time.
093: *
094: * @param request request asking for the lock (timeout field is used and
095: * updated upon waiting)
096: * @return boolean true is the lock has been successfully acquired, false on
097: * timeout or error
098: * @see #release()
099: */
100: public boolean acquire(AbstractRequest request) {
101: long tid = request.getTransactionId();
102:
103: synchronized (Thread.currentThread()) {
104: WaitingListElement wle = null;
105: synchronized (this ) {
106: if (!isLocked) { // Lock is free, take it
107: locker = tid;
108: isLocked = true;
109: return true;
110: } else {
111: if (locker == tid)
112: return true; // We already have the lock
113: else { // Wait for the lock
114: wle = new WaitingListElement(Thread
115: .currentThread(), tid);
116: waitingList.addLast(wle);
117: }
118: }
119: }
120: // At this point, we have to wait for the lock.
121: try {
122: int timeout = request.getTimeout();
123: if (timeout == 0) {
124: Thread.currentThread().wait(); // No timeout
125: // Note: isLocked and locker are already set.
126: return true;
127: } else { // Wait with timeout
128: long start = System.currentTimeMillis();
129: // Convert seconds to milliseconds for wait call
130: long lTimeout = timeout * 1000L;
131: Thread.currentThread().wait(lTimeout);
132: long end = System.currentTimeMillis();
133: int remaining = (int) (lTimeout - (end - start));
134: if (remaining > 0) { // Ok
135: request.setTimeout(remaining);
136: // Note: isLocked and locker are already set.
137: return true;
138: } else { // Too late, remove ourselves from the waiting list
139: synchronized (this ) {
140: for (Iterator iter = waitingList.iterator(); iter
141: .hasNext();) {
142: WaitingListElement e = (WaitingListElement) iter
143: .next();
144: if (e.equals(wle)) {
145: iter.remove();
146: return false;
147: }
148: }
149: // Not found, we got the lock before being able to acquire the
150: // lock on "this". Give the lock to the next one.
151: release();
152: }
153: return false;
154: }
155: }
156: } catch (InterruptedException ie) {
157: synchronized (this ) { // Something wrong happened, remove ourselves from the waiting list
158: waitingList.remove(Thread.currentThread());
159: }
160: return false;
161: }
162: }
163: }
164:
165: /**
166: * Releases the lock on this table.
167: *
168: * @see #acquire(AbstractRequest)
169: */
170: public synchronized void release() {
171: if (!waitingList.isEmpty()) {
172: // Wake up the first waiting thread and update locker transaction id
173: WaitingListElement e = (WaitingListElement) waitingList
174: .removeFirst();
175: Thread thread = e.getThread();
176: locker = e.getTransactionId();
177: synchronized (thread) {
178: thread.notify();
179: // isLocked remains true
180: }
181: } else
182: isLocked = false;
183: }
184:
185: /**
186: * Returns <code>true</code> if the lock is owned by someone.
187: *
188: * @return <code>boolean</code> value
189: */
190: public boolean isLocked() {
191: return isLocked;
192: }
193:
194: /**
195: * Returns the transaction id of the lock owner. The return value is undefined
196: * if the lock is not owned (usually it is the last owner).
197: *
198: * @return int the transaction id.
199: */
200: public long getLocker() {
201: return locker;
202: }
203:
204: /**
205: * Returns the waitingList.
206: *
207: * @return an <code>LinkedList</code> of <code>WaitingListElement</code>
208: */
209: public LinkedList getWaitingList() {
210: return waitingList;
211: }
212:
213: /**
214: * Returns <code>true</code> if the given transaction id is contained in
215: * this lock waiting queue.
216: *
217: * @param transactionId a transaction id
218: * @return a <code>boolean</code> value
219: */
220: public synchronized boolean isWaiting(long transactionId) {
221: for (Iterator iter = waitingList.iterator(); iter.hasNext();) {
222: WaitingListElement e = (WaitingListElement) iter.next();
223: if (e.getTransactionId() == transactionId)
224: return true;
225: }
226: return false;
227: }
228: }
|