KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > lock > LockUtil


1 package org.jboss.cache.lock;
2
3 import org.apache.commons.logging.Log;
4 import org.apache.commons.logging.LogFactory;
5 import org.jboss.cache.CacheImpl;
6 import org.jboss.cache.GlobalTransaction;
7 import org.jboss.cache.Node;
8 import org.jboss.cache.NodeSPI;
9 import org.jboss.cache.TransactionTable;
10 import org.jboss.cache.statetransfer.StateTransferManager;
11
12 import javax.transaction.Status JavaDoc;
13 import javax.transaction.Transaction JavaDoc;
14 import javax.transaction.TransactionManager JavaDoc;
15 import java.util.Iterator JavaDoc;
16
17 public abstract class LockUtil
18 {
19    private final static Log log = LogFactory.getLog(StateTransferManager.class);
20
21    private static interface TransactionLockStatus extends Status JavaDoc
22    {
23       public static final int STATUS_BROKEN = Integer.MIN_VALUE;
24    }
25
26    public static boolean breakTransactionLock(NodeLock lock,
27                                               GlobalTransaction gtx,
28                                               boolean localTx,
29                                               CacheImpl cache)
30    {
31       TransactionTable tx_table = cache.getTransactionTable();
32       TransactionManager JavaDoc tm = cache.getTransactionManager();
33
34       boolean broken = false;
35       int tryCount = 0;
36       int lastStatus = TransactionLockStatus.STATUS_BROKEN;
37
38       while (!broken && lock.isOwner(gtx))
39       {
40          int status = breakTransactionLock(gtx, lock, tx_table, tm, localTx, lastStatus, tryCount);
41          if (status == TransactionLockStatus.STATUS_BROKEN)
42          {
43             broken = true;
44          }
45          else if (status != lastStatus)
46          {
47             tryCount = 0;
48          }
49          lastStatus = status;
50
51          tryCount++;
52       }
53
54       return broken;
55    }
56
57    /**
58     * Forcibly acquire a read lock on the given node for the given owner,
59     * breaking any existing locks that prevent the read lock. If the
60     * existing lock is held by a GlobalTransaction, breaking the lock may
61     * result in a rollback of the transaction.
62     *
63     * @param node the node
64     * @param newOwner the new owner (usually a Thread or GlobalTransaction)
65     * @param lockChildren <code>true</code> if this method should be recursively
66     * applied to <code>node</code>'s children.
67     */

68    public static void forceAcquireLock(NodeSPI node,
69                                        Object JavaDoc newOwner,
70                                        CacheImpl cache,
71                                        boolean lockChildren)
72    {
73
74       NodeLock lock = node.getLock();
75       boolean acquired = lock.isOwner(newOwner);
76
77       if (!acquired && log.isDebugEnabled())
78       {
79          log.debug("Force acquiring lock on node " + node.getFqn());
80       }
81
82       TransactionTable tx_table = cache.getTransactionTable();
83       TransactionManager JavaDoc tm = cache.getTransactionManager();
84       Object JavaDoc localAddress = cache.getLocalAddress();
85       boolean serializable = cache.getConfiguration().getIsolationLevel() == IsolationLevel.SERIALIZABLE;
86
87       while (!acquired)
88       {
89          Object JavaDoc curOwner = null;
90          boolean attempted = false;
91
92          // Keep breaking write locks until we acquire a read lock
93
// or there are no more write locks
94
while (!acquired && ((curOwner = lock.getWriterOwner()) != null))
95          {
96             acquired = acquireLockFromOwner(node, lock, curOwner, newOwner, tx_table, tm, localAddress);
97             attempted = true;
98          }
99
100          // If no more write locks, but we haven't acquired, see if we
101
// need to break read locks as well.
102
if (!acquired && serializable)
103          {
104             Iterator JavaDoc it = lock.getReaderOwners().iterator();
105             if (it.hasNext())
106             {
107                curOwner = it.next();
108                acquired = acquireLockFromOwner(node, lock, curOwner, newOwner, tx_table, tm, localAddress);
109                attempted = true;
110                // Don't keep iterating due to the risk of
111
// ConcurrentModificationException if readers are removed
112
// Just go back through our outer loop to get the next one
113
}
114          }
115
116          if (!acquired && !attempted)
117          {
118             // We only try to acquire above if someone else has the lock.
119
// Seems no one is holding a lock and it's there for the taking.
120
try
121             {
122                acquired = lock.acquire(newOwner, 1, NodeLock.LockType.READ);
123             }
124             catch (Exception JavaDoc ignored)
125             {
126             }
127          }
128       }
129
130       // Recursively unlock children
131
if (lockChildren)
132       {
133          for (NodeSPI n : node.getChildrenDirect())
134          {
135             forceAcquireLock(n, newOwner, cache, true);
136          }
137       }
138    }
139
140    /**
141     * Attempts to acquire a read lock on <code>node</code> for
142     * <code>newOwner</code>, if necessary breaking locks held by
143     * <code>curOwner</code>.
144     *
145     * @param node the node
146     * @param lock the lock
147     * @param curOwner the current owner
148     * @param newOwner the new owner
149     */

150    private static boolean acquireLockFromOwner(Node node,
151                                                NodeLock lock,
152                                                Object JavaDoc curOwner,
153                                                Object JavaDoc newOwner,
154                                                TransactionTable tx_table,
155                                                TransactionManager JavaDoc tm,
156                                                Object JavaDoc localAddress)
157    {
158       if (log.isTraceEnabled())
159       {
160          log.trace("Attempting to acquire lock for node " + node.getFqn() +
161                  " from owner " + curOwner);
162       }
163
164       boolean acquired = false;
165       boolean broken = false;
166       int tryCount = 0;
167       int lastStatus = TransactionLockStatus.STATUS_BROKEN;
168
169       while (!broken && !acquired)
170       {
171          if (curOwner instanceof GlobalTransaction)
172          {
173             GlobalTransaction gtx = (GlobalTransaction) curOwner;
174             boolean local = gtx.getAddress().equals(localAddress);
175             int status = breakTransactionLock(gtx, lock, tx_table, tm, local, lastStatus, tryCount);
176             if (status == TransactionLockStatus.STATUS_BROKEN)
177             {
178                broken = true;
179             }
180             else if (status != lastStatus)
181             {
182                tryCount = 0;
183             }
184             lastStatus = status;
185          }
186          else if (tryCount > 0)
187          {
188             lock.release(curOwner);
189             broken = true;
190          }
191
192          if (broken && log.isTraceEnabled())
193          {
194             log.trace("Broke lock for node " + node.getFqn() +
195                     " held by owner " + curOwner);
196          }
197
198          try
199          {
200             acquired = lock.acquire(newOwner, 1, NodeLock.LockType.READ);
201          }
202          catch (Exception JavaDoc ignore)
203          {
204          }
205
206          tryCount++;
207       }
208
209       return acquired;
210    }
211
212    /**
213     * Attempts to release the lock held by <code>gtx</code> by altering the
214     * underlying transaction. Different strategies will be employed
215     * depending on the status of the transaction and param
216     * <code>tryCount</code>. Transaction may be rolled back or marked
217     * rollback-only, or the lock may just be broken, ignoring the tx. Makes an
218     * effort to not affect the tx or break the lock if tx appears to be in
219     * the process of completion; param <code>tryCount</code> is used to help
220     * make decisions about this.
221     * <p/>
222     * This method doesn't guarantee to have broken the lock unless it returns
223     * {@link TransactionLockStatus#STATUS_BROKEN}.
224     *
225     * @param gtx the gtx holding the lock
226     * @param lock the lock
227     * @param lastStatus the return value from a previous invocation of this
228     * method for the same lock, or Status.STATUS_UNKNOW
229     * for the first invocation.
230     * @param tryCount number of times this method has been called with
231     * the same gtx, lock and lastStatus arguments. Should
232     * be reset to 0 anytime lastStatus changes.
233     * @return the current status of the Transaction associated with
234     * <code>gtx</code>, or {@link TransactionLockStatus#STATUS_BROKEN}
235     * if the lock held by gtx was forcibly broken.
236     */

237    private static int breakTransactionLock(GlobalTransaction gtx,
238                                            NodeLock lock,
239                                            TransactionTable tx_table,
240                                            TransactionManager JavaDoc tm,
241                                            boolean localTx,
242                                            int lastStatus,
243                                            int tryCount)
244    {
245       int status = Status.STATUS_UNKNOWN;
246       Transaction tx = tx_table.getLocalTransaction(gtx);
247       if (tx != null)
248       {
249          try
250          {
251             status = tx.getStatus();
252
253             if (status != lastStatus)
254             {
255                tryCount = 0;
256             }
257
258             switch (status)
259             {
260                case Status.STATUS_ACTIVE:
261                case Status.STATUS_MARKED_ROLLBACK:
262                case Status.STATUS_PREPARING:
263                case Status.STATUS_UNKNOWN:
264                   if (tryCount == 0)
265                   {
266                      if (log.isTraceEnabled())
267                      {
268                         log.trace("Attempting to break transaction lock held " +
269                                 " by " + gtx + " by rolling back local tx");
270                      }
271                      // This thread has to join the tx
272
tm.resume(tx);
273                      try
274                      {
275                         tx.rollback();
276                      }
277                      finally
278                      {
279                         tm.suspend();
280                      }
281
282                   }
283                   else if (tryCount > 100)
284                   {
285                      // Something is wrong; our initial rollback call
286
// didn't generate a valid state change; just force it
287
lock.release(gtx);
288                      status = TransactionLockStatus.STATUS_BROKEN;
289                   }
290                   break;
291
292                case Status.STATUS_COMMITTING:
293                case Status.STATUS_ROLLING_BACK:
294                   // We'll try up to 10 times before just releasing
295
if (tryCount < 10)
296                   {
297                      break;// let it finish
298
}
299                   // fall through and release
300

301                case Status.STATUS_COMMITTED:
302                case Status.STATUS_ROLLEDBACK:
303                case Status.STATUS_NO_TRANSACTION:
304                   lock.release(gtx);
305                   status = TransactionLockStatus.STATUS_BROKEN;
306                   break;
307
308                case Status.STATUS_PREPARED:
309                   // If the tx was started here, we can still abort the commit,
310
// otherwise we are in the middle of a remote commit() call
311
// and the status is just about to change
312
if (tryCount == 0 && localTx)
313                   {
314                      // We can still abort the commit
315
if (log.isTraceEnabled())
316                      {
317                         log.trace("Attempting to break transaction lock held " +
318                                 "by " + gtx + " by marking local tx as " +
319                                 "rollback-only");
320                      }
321                      tx.setRollbackOnly();
322                      break;
323                   }
324                   else if (tryCount < 10)
325                   {
326                      // EITHER tx was started elsewhere (in which case we'll
327
// wait a bit to allow the commit() call to finish;
328
// same as STATUS_COMMITTING above)
329
// OR we marked the tx rollbackOnly above and are just
330
// waiting a bit for the status to change
331
break;
332                   }
333
334                   // fall through and release
335
default:
336                   lock.release(gtx);
337                   status = TransactionLockStatus.STATUS_BROKEN;
338             }
339          }
340          catch (Exception JavaDoc e)
341          {
342             log.error("Exception breaking locks held by " + gtx, e);
343             lock.release(gtx);
344             status = TransactionLockStatus.STATUS_BROKEN;
345          }
346       }
347       else
348       {
349          // Race condition; gtx was cleared from tx_table.
350
// Just double check if gtx still holds a lock
351
if (gtx == lock.getWriterOwner()
352                  || lock.getReaderOwners().contains(gtx))
353          {
354             // TODO should we throw an exception??
355
lock.release(gtx);
356             status = TransactionLockStatus.STATUS_BROKEN;
357          }
358       }
359
360       return status;
361    }
362
363 }
364
Popular Tags