001: //========================================================================
002: //$Id: Timeout.java,v 1.3 2005/11/11 22:55:41 gregwilkins Exp $
003: //Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
004: //------------------------------------------------------------------------
005: //Licensed under the Apache License, Version 2.0 (the "License");
006: //you may not use this file except in compliance with the License.
007: //You may obtain a copy of the License at
008: //http://www.apache.org/licenses/LICENSE-2.0
009: //Unless required by applicable law or agreed to in writing, software
010: //distributed under the License is distributed on an "AS IS" BASIS,
011: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: //See the License for the specific language governing permissions and
013: //limitations under the License.
014: //========================================================================
015:
016: package org.mortbay.thread;
017:
018: import org.mortbay.log.Log;
019:
020: /* ------------------------------------------------------------ */
021: /** Timeout queue.
022: * This class implements a timeout queue for timers that are at least as likely to be cancelled as they are to expire.
023: * Unlike the util timeout class, the duration of the timouts is shared by all scheduled tasks and if the duration
024: * is changed, this affects all scheduled tasks.
025: *
026: * The nested class Task should be extended by users of this class to obtain call back notification of
027: * expiries.
028: *
029: * @author gregw
030: *
031: */
032: public class Timeout {
033:
034: private long _duration;
035: private long _now;
036: private Task _head = new Task();
037:
038: public Timeout() {
039: _head._timeout = this ;
040: }
041:
042: /* ------------------------------------------------------------ */
043: /**
044: * @return Returns the duration.
045: */
046: public long getDuration() {
047: return _duration;
048: }
049:
050: /* ------------------------------------------------------------ */
051: /**
052: * @param duration The duration to set.
053: */
054: public void setDuration(long duration) {
055: _duration = duration;
056: }
057:
058: /* ------------------------------------------------------------ */
059: public void setNow() {
060: _now = System.currentTimeMillis();
061: }
062:
063: /* ------------------------------------------------------------ */
064: public long getNow() {
065: return _now;
066: }
067:
068: /* ------------------------------------------------------------ */
069: public void setNow(long now) {
070: _now = now;
071: }
072:
073: /* ------------------------------------------------------------ */
074: public Task expired() {
075: long _expiry = _now - _duration;
076:
077: if (_head._next != _head) {
078: Task task = _head._next;
079: if (task._timestamp > _expiry)
080: return null;
081:
082: task.unlink();
083: synchronized (task) {
084: task._expired = true;
085: }
086: return task;
087: }
088: return null;
089: }
090:
091: /* ------------------------------------------------------------ */
092: public void tick() {
093: long _expiry = _now - _duration;
094:
095: while (_head._next != _head) {
096: Task task = _head._next;
097: if (task._timestamp > _expiry)
098: break;
099:
100: task.unlink();
101: try {
102: task.doExpire();
103: } catch (Throwable th) {
104: Log.warn(Log.EXCEPTION, th);
105: }
106: }
107: }
108:
109: /* ------------------------------------------------------------ */
110: public void schedule(Task task) {
111: schedule(task, 0L);
112: }
113:
114: /* ------------------------------------------------------------ */
115: public void schedule(Task task, long delay) {
116: if (task._timestamp != 0) {
117: task.unlink();
118: task._timestamp = 0;
119: }
120: task._expired = false;
121: task._delay = delay;
122: task._timestamp = _now + delay;
123:
124: Task last = _head._prev;
125: while (last != _head) {
126: if (last._timestamp <= task._timestamp)
127: break;
128: last = last._prev;
129: }
130: last.setNext(task);
131: }
132:
133: /* ------------------------------------------------------------ */
134: public void cancelAll() {
135: _head._next = _head._prev = _head;
136: }
137:
138: /* ------------------------------------------------------------ */
139: public boolean isEmpty() {
140: return _head._next == _head;
141: }
142:
143: /* ------------------------------------------------------------ */
144: public long getTimeToNext() {
145: if (_head._next == _head)
146: return -1;
147: long to_next = _duration + _head._next._timestamp - _now;
148: return to_next < 0 ? 0 : to_next;
149: }
150:
151: /* ------------------------------------------------------------ */
152: public String toString() {
153: StringBuffer buf = new StringBuffer();
154: buf.append(super .toString());
155:
156: Task task = _head._next;
157: while (task != _head) {
158: buf.append("-->");
159: buf.append(task);
160: task = task._next;
161: }
162:
163: return buf.toString();
164: }
165:
166: /* ------------------------------------------------------------ */
167: /* ------------------------------------------------------------ */
168: /* ------------------------------------------------------------ */
169: /* ------------------------------------------------------------ */
170: /** Task.
171: * The base class for scheduled timeouts. This class should be
172: * extended to implement the expire() method, which is called if the
173: * timeout expires.
174: *
175: * @author gregw
176: *
177: */
178: public static class Task {
179: Task _next;
180: Task _prev;
181: Timeout _timeout;
182: long _delay;
183: long _timestamp = 0;
184: boolean _expired = false;
185:
186: public Task() {
187: _next = _prev = this ;
188: }
189:
190: public void unlink() {
191: _next._prev = _prev;
192: _prev._next = _next;
193: _next = _prev = this ;
194: _timeout = null;
195: _expired = false;
196: }
197:
198: public void setNext(Task task) {
199: if (_timeout == null || task._timeout != null
200: && task._timeout != _timeout || task._next != task)
201: throw new IllegalStateException();
202: Task next_next = _next;
203: _next._prev = task;
204: _next = task;
205: _next._next = next_next;
206: _next._prev = this ;
207: _next._timeout = _timeout;
208: }
209:
210: /* ------------------------------------------------------------ */
211: /** Schedule the task on the given timeout.
212: * The task exiry will be called after the timeout duration.
213: * @param timer
214: */
215: public void schedule(Timeout timer) {
216: unlink();
217: timer.schedule(this );
218: }
219:
220: /* ------------------------------------------------------------ */
221: /** Schedule the task on the given timeout.
222: * The task exiry will be called after the timeout duration.
223: * @param timer
224: */
225: public void schedule(Timeout timer, long delay) {
226: unlink();
227: timer.schedule(this , delay);
228: }
229:
230: /* ------------------------------------------------------------ */
231: /** Reschedule the task on the current timeout.
232: * The task timeout is rescheduled as if it had been canceled and
233: * scheduled on the current timeout.
234: */
235: public void reschedule() {
236: Timeout timer = _timeout;
237: unlink();
238: timer.schedule(this , _delay);
239: }
240:
241: /* ------------------------------------------------------------ */
242: /** Cancel the task.
243: * Remove the task from the timeout.
244: */
245: public void cancel() {
246: _timestamp = 0;
247: unlink();
248: }
249:
250: public boolean isExpired() {
251: return _expired;
252: }
253:
254: /* ------------------------------------------------------------ */
255: /** Expire task.
256: * This method is called when the timeout expires.
257: *
258: */
259: public void expire() {
260: }
261:
262: private void doExpire() {
263: synchronized (this ) {
264: _expired = true;
265: expire();
266: }
267: }
268: }
269:
270: }
|