summaryrefslogtreecommitdiff
path: root/java/JACE/ASX/TimedWait.java
blob: dc1d0bab67371949c98dccc5857e951e6e49e35d (plain)
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*************************************************
 *
 * = PACKAGE
 *    JACE.ASX
 *
 * = FILENAME
 *    TimedWait.java
 *
 *@author Prashant Jain and Doug Schmidt
 *
 *************************************************/
package JACE.ASX;

/**
 * A wait/notify system with absolute time timeouts and built-in
 * check of a condition. <P>
 *
 * Subclasses define the condition to check, and the object to 
 * wait on can be specified.
 */
public abstract class TimedWait
{
  /**
   * Default Constructor. Sets "this" to be used for the delegation of
   * the wait() call to. 
   */
  public TimedWait ()
  {
    object_ = this;
  }

  /**
   * Constructor. Allows subclasses to supply us with an Object that
   * is delegated the wait() call.
   *@param obj The Object that is delegated the wait() call.
   */
  public TimedWait (Object obj)
  {
    object_ = obj;
  }

  /**
   * Hook method that needs to be implemented by subclasses.
   */
  public abstract boolean condition ();

  /**
   * Wait until condition becomes true. Note that the method
   * blocks. Also note that this method is final to ensure that no one
   * overrides it. 
   * IMPORTANT: This method assumes it is called with the object_'s
   * monitor lock already held.
   *@exception InterruptedException Interrupted during wait
   */
  public final void timedWait () throws InterruptedException
  {
    // Acquire the monitor lock.
    if (!condition ()) 
      {
	// Only attempt to perform the wait if the condition isn't
	// true initially.
	for (;;) 
	  {
	    // Wait until we are notified.
	    object_.wait ();

	  // Recheck the condition.
	  if (condition ()) 
	    break; // Condition became true.

	  // else we were falsely notified so go back into wait
	  }
      }
  }

  /**
   * Template Method that implements the actual timed wait. Note that
   * this method is final to ensure that no one overrides it. 
   * IMPORTANT: This method assumes it is called with the object_'s
   * monitor lock already held.
   * If the specified wait time is zero, this checks the condition,
   * then returns on success or throws a TimeoutException on failure.
   *@param tv Absolute time to wait until before throwing an exception
   * if the condition isn't satisfied
   *@exception java.lang.InterruptedException Interrupted during wait
   *@exception JACE.ASX.TimeoutException Reached timeout specified
   */
  public final void timedWait (TimeValue tv)
    throws InterruptedException,
           TimeoutException 
  {
    if (tv == null) {
	this.timedWait();
	return;
    }

    // Acquire the monitor lock.
    if (!condition ()) 
      {
	long start = System.currentTimeMillis();
	long waitTime = tv.getMilliTime() - start;

	for (;;) {
	  
	  // Prevent a conversion from absolute to relative time from
	  // generating a zero or negative waitTime.
	  if (waitTime < 1)
	    throw new TimeoutException ();

	  // Wait until we are notified.
	  object_.wait (waitTime);

	  // Recheck the condition.
	  if (!condition ()) {

	    long now = System.currentTimeMillis();
	    
	    // Timed out!
	    if (now >= tv.getMilliTime ()) 
	      throw new TimeoutException ();
	    else 
	      // We still have some time left to wait, so adjust the
	      // wait_time.
	      waitTime = tv.getMilliTime() - now; 
	  }
	  else
	    break;  // Condition became true.
	}
      }
  }

  /**
   * Notify any one thread waiting on the object_.  
   * IMPORTANT: This method assumes it is called with the object_'s
   * monitor lock already held.
   */
  public final void signal () {
    object_.notify ();
  }

  /**
   * Notify all threads waiting on the object_.  
   * IMPORTANT: This method assumes it is called with the object_'s
   * monitor lock already held.
   */
  public final void broadcast () {
    object_.notifyAll ();
  }

  /** 
   * The object we delegate to. If a subclass gives us a particular
   * object,  we use that to delegate to, otherwise, we ``delegate''
   * to ourself (i.e., this).
   */
  protected Object object_;

}