View Javadoc

1   /*
2    * Copyright 2002-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  /*
17    File: Mutex.java
18  
19    Originally written by Doug Lea and released into the public domain.
20    This may be used for any purposes whatsoever without acknowledgment.
21    Thanks for the assistance and support of Sun Microsystems Labs,
22    and everyone contributing, testing, and using this code.
23  
24    History:
25    Date       Who                What
26    11Jun1998  dl               Create public version
27  */
28  
29  package org.apache.commons.jelly.tags.threads;
30  
31  /***
32   * A simple non-reentrant mutual exclusion lock.
33   * The lock is free upon construction. Each acquire gets the
34   * lock, and each release frees it. Releasing a lock that
35   * is already free has no effect.
36   * <p>
37   * This implementation makes no attempt to provide any fairness
38   * or ordering guarantees. If you need them, consider using one of
39   * the Semaphore implementations as a locking mechanism.
40   * <p>
41   * <b>Sample usage</b><br>
42   * <p>
43   * Mutex can be useful in constructions that cannot be
44   * expressed using java synchronized blocks because the
45   * acquire/release pairs do not occur in the same method or
46   * code block. For example, you can use them for hand-over-hand
47   * locking across the nodes of a linked list. This allows
48   * extremely fine-grained locking,  and so increases
49   * potential concurrency, at the cost of additional complexity and
50   * overhead that would normally make this worthwhile only in cases of
51   * extreme contention.
52   * <pre>
53   * class Node {
54   *   Object item;
55   *   Node next;
56   *   Mutex lock = new Mutex(); // each node keeps its own lock
57   *
58   *   Node(Object x, Node n) { item = x; next = n; }
59   * }
60   *
61   * class List {
62   *    protected Node head; // pointer to first node of list
63   *
64   *    // Use plain java synchronization to protect head field.
65   *    //  (We could instead use a Mutex here too but there is no
66   *    //  reason to do so.)
67   *    protected synchronized Node getHead() { return head; }
68   *
69   *    boolean search(Object x) throws InterruptedException {
70   *      Node p = getHead();
71   *      if (p == null) return false;
72   *
73   *      //  (This could be made more compact, but for clarity of illustration,
74   *      //  all of the cases that can arise are handled separately.)
75   *
76   *      p.lock.acquire();              // Prime loop by acquiring first lock.
77   *                                     //    (If the acquire fails due to
78   *                                     //    interrupt, the method will throw
79   *                                     //    InterruptedException now,
80   *                                     //    so there is no need for any
81   *                                     //    further cleanup.)
82   *      for (;;) {
83   *        if (x.equals(p.item)) {
84   *          p.lock.release();          // release current before return
85   *          return true;
86   *        }
87   *        else {
88   *          Node nextp = p.next;
89   *          if (nextp == null) {
90   *            p.lock.release();       // release final lock that was held
91   *            return false;
92   *          }
93   *          else {
94   *            try {
95   *              nextp.lock.acquire(); // get next lock before releasing current
96   *            }
97   *            catch (InterruptedException ex) {
98   *              p.lock.release();    // also release current if acquire fails
99   *              throw ex;
100  *            }
101  *            p.lock.release();      // release old lock now that new one held
102  *            p = nextp;
103  *          }
104  *        }
105  *      }
106  *    }
107  *
108  *    synchronized void add(Object x) { // simple prepend
109  *      // The use of `synchronized'  here protects only head field.
110  *      // The method does not need to wait out other traversers
111  *      // who have already made it past head.
112  *
113  *      head = new Node(x, head);
114  *    }
115  *
116  *    // ...  other similar traversal and update methods ...
117  * }
118  * </pre>
119  * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
120  *
121  * --------------------------------------------------------------------------------------------
122  * This Mutex is used to add the still running lock to the JellyThread tag.
123  **/
124 
125 public class Mutex {
126 
127     /*** The lock status **/
128     protected boolean inuse_ = false;
129 
130     public void acquire() throws InterruptedException {
131         if (Thread.interrupted()) throw new InterruptedException();
132         synchronized (this) {
133             try {
134                 while (inuse_) wait();
135                 inuse_ = true;
136             } catch (InterruptedException ex) {
137                 notify();
138                 throw ex;
139             }
140         }
141     }
142 
143     public synchronized void release() {
144         inuse_ = false;
145         notify();
146     }
147 
148 
149     public boolean attempt(long msecs) throws InterruptedException {
150         if (Thread.interrupted()) throw new InterruptedException();
151         synchronized (this) {
152             if (!inuse_) {
153                 inuse_ = true;
154                 return true;
155             } else if (msecs <= 0)
156                 return false;
157             else {
158                 long waitTime = msecs;
159                 long start = System.currentTimeMillis();
160                 try {
161                     for (; ;) {
162                         wait(waitTime);
163                         if (!inuse_) {
164                             inuse_ = true;
165                             return true;
166                         } else {
167                             waitTime = msecs - (System.currentTimeMillis() - start);
168                             if (waitTime <= 0)
169                                 return false;
170                         }
171                     }
172                 } catch (InterruptedException ex) {
173                     notify();
174                     throw ex;
175                 }
176             }
177         }
178     }
179 
180 }