1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.tomgibara.pronto.util;
19
20 /**
21 * An instance of this object can wait on an object's monitor until a condition
22 * is met, or a given number of milliseconds has elapsed - which ever occurs
23 * soonest. The class expects that it will be notified via the specified monitor
24 * when the condition may have changed.
25 *
26 * This class is safe for multithreaded use.
27 *
28 * @author Tom Gibara
29 */
30
31
32 public class Waiter {
33
34
35
36 /**
37 * The object on which to wait for a possible change in condition.
38 */
39
40 private final Object monitor;
41
42
43
44 /**
45 * Creates a new waiter for waiting on the supplied object.
46 *
47 * @param monitor
48 * the object on which the waiter waits for a change in
49 * condition.
50 */
51
52 public Waiter(final Object monitor) {
53 Arguments.notNull(monitor, "monitor");
54 this.monitor = monitor;
55 }
56
57
58
59 /**
60 * The monitor on which this object will wait for changes in condition.
61 *
62 * @return the monitor on which this object will wait while waiting for a
63 * condition to be met.
64 */
65
66 public Object getMonitor() {
67 return monitor;
68 }
69
70
71
72 /**
73 * This method will cause the calling thread to wait until the specified
74 * condtion is met or a timeout occurs, which ever is soonest. This class
75 * expects that the monitor will be notified when a condition is met. By
76 * default, this method will not abruptly in response to an interrupted
77 * exception, but this behavior may be modified by overriding the
78 * handleInterruption method.
79 *
80 * @param timeout
81 * the number of milliseconds to wait for, or 0 to wait
82 * indefinitely
83 * @param cond
84 * the condition for a timely return from this method
85 *
86 * @return true if the condition was met, false otherwise
87 */
88
89 public boolean waitForCondition(final long timeout, final Condition cond) {
90 Arguments.notNegative(timeout, "timeout");
91 Arguments.notNull(cond, "cond");
92
93 if (timeout == 0) {
94 while (!cond.isMet()) {
95 synchronized (monitor) {
96 try {
97 monitor.wait();
98 } catch (InterruptedException e) {
99 if (!handleInterruption(e)) return cond.isMet();
100 }
101 }
102 }
103 return true;
104 } else {
105 final long start = System.currentTimeMillis();
106 while (!cond.isMet()) {
107 final long time = start + timeout - System.currentTimeMillis();
108 if (time <= 0) return cond.isMet();
109 synchronized (monitor) {
110 try {
111 monitor.wait(time);
112 } catch (InterruptedException e) {
113 if (!handleInterruption(e)) return cond.isMet();
114 }
115 }
116 }
117 return true;
118 }
119 }
120
121
122
123 /**
124 * This method is called when an interruption occurs while waiting. The
125 * default implementation of this method returns true without performing any
126 * other action. The method may be overridden to change the response of this
127 * class to any interruptions that occur during waits.
128 *
129 * @param e
130 * the interrupting exception
131 *
132 * @return whether the object should continue to wait for the condition to
133 * be met
134 */
135
136 protected boolean handleInterruption(final InterruptedException e) {
137 return true;
138 }
139
140
141
142 /**
143 * Implementations of this interface are supplied to a Waiter and control
144 * when the object may cease waiting.
145 */
146
147 public interface Condition {
148
149 /**
150 * @return true if the condition has been met to stop waiting, false
151 * otherwise
152 */
153
154 boolean isMet();
155
156 }
157
158 }