1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 }