1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java $
3 * $Revision: 649217 $
4 * $Date: 2008-04-17 11:32:32 -0700 (Thu, 17 Apr 2008) $
5 *
6 * ====================================================================
7 *
8 *  Licensed to the Apache Software Foundation (ASF) under one or more
9 *  contributor license agreements.  See the NOTICE file distributed with
10 *  this work for additional information regarding copyright ownership.
11 *  The ASF licenses this file to You under the Apache License, Version 2.0
12 *  (the "License"); you may not use this file except in compliance with
13 *  the License.  You may obtain a copy of the License at
14 *
15 *      http://www.apache.org/licenses/LICENSE-2.0
16 *
17 *  Unless required by applicable law or agreed to in writing, software
18 *  distributed under the License is distributed on an "AS IS" BASIS,
19 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 *  See the License for the specific language governing permissions and
21 *  limitations under the License.
22 * ====================================================================
23 *
24 * This software consists of voluntary contributions made by many
25 * individuals on behalf of the Apache Software Foundation.  For more
26 * information on the Apache Software Foundation, please see
27 * <http://www.apache.org/>.
28 *
29 */
30
31package org.apache.http.impl.conn.tsccm;
32
33
34import java.util.Date;
35import java.util.concurrent.locks.Condition;
36
37
38/**
39 * Represents a thread waiting for a connection.
40 * This class implements throwaway objects. It is instantiated whenever
41 * a thread needs to wait. Instances are not re-used, except if the
42 * waiting thread experiences a spurious wakeup and continues to wait.
43 * <br/>
44 * All methods assume external synchronization on the condition
45 * passed to the constructor.
46 * Instances of this class do <i>not</i> synchronize access!
47 *
48 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
49 */
50public class WaitingThread {
51
52    /** The condition on which the thread is waiting. */
53    private final Condition cond;
54
55    /** The route specific pool on which the thread is waiting. */
56    //@@@ replace with generic pool interface
57    private final RouteSpecificPool pool;
58
59    /** The thread that is waiting for an entry. */
60    private Thread waiter;
61
62    /** True if this was interrupted. */
63    private boolean aborted;
64
65
66    /**
67     * Creates a new entry for a waiting thread.
68     *
69     * @param cond      the condition for which to wait
70     * @param pool      the pool on which the thread will be waiting,
71     *                  or <code>null</code>
72     */
73    public WaitingThread(Condition cond, RouteSpecificPool pool) {
74
75        if (cond == null) {
76            throw new IllegalArgumentException("Condition must not be null.");
77        }
78
79        this.cond = cond;
80        this.pool = pool;
81    }
82
83
84    /**
85     * Obtains the condition.
86     *
87     * @return  the condition on which to wait, never <code>null</code>
88     */
89    public final Condition getCondition() {
90        // not synchronized
91        return this.cond;
92    }
93
94
95    /**
96     * Obtains the pool, if there is one.
97     *
98     * @return  the pool on which a thread is or was waiting,
99     *          or <code>null</code>
100     */
101    public final RouteSpecificPool getPool() {
102        // not synchronized
103        return this.pool;
104    }
105
106
107    /**
108     * Obtains the thread, if there is one.
109     *
110     * @return  the thread which is waiting, or <code>null</code>
111     */
112    public final Thread getThread() {
113        // not synchronized
114        return this.waiter;
115    }
116
117
118    /**
119     * Blocks the calling thread.
120     * This method returns when the thread is notified or interrupted,
121     * if a timeout occurrs, or if there is a spurious wakeup.
122     * <br/>
123     * This method assumes external synchronization.
124     *
125     * @param deadline  when to time out, or <code>null</code> for no timeout
126     *
127     * @return  <code>true</code> if the condition was satisfied,
128     *          <code>false</code> in case of a timeout.
129     *          Typically, a call to {@link #wakeup} is used to indicate
130     *          that the condition was satisfied. Since the condition is
131     *          accessible outside, this cannot be guaranteed though.
132     *
133     * @throws InterruptedException     if the waiting thread was interrupted
134     *
135     * @see #wakeup
136     */
137    public boolean await(Date deadline)
138        throws InterruptedException {
139
140        // This is only a sanity check. We cannot synchronize here,
141        // the lock would not be released on calling cond.await() below.
142        if (this.waiter != null) {
143            throw new IllegalStateException
144                ("A thread is already waiting on this object." +
145                 "\ncaller: " + Thread.currentThread() +
146                 "\nwaiter: " + this.waiter);
147        }
148
149        if (aborted)
150            throw new InterruptedException("Operation interrupted");
151
152        this.waiter = Thread.currentThread();
153
154        boolean success = false;
155        try {
156            if (deadline != null) {
157                success = this.cond.awaitUntil(deadline);
158            } else {
159                this.cond.await();
160                success = true;
161            }
162            if (aborted)
163                throw new InterruptedException("Operation interrupted");
164        } finally {
165            this.waiter = null;
166        }
167        return success;
168
169    } // await
170
171
172    /**
173     * Wakes up the waiting thread.
174     * <br/>
175     * This method assumes external synchronization.
176     */
177    public void wakeup() {
178
179        // If external synchronization and pooling works properly,
180        // this cannot happen. Just a sanity check.
181        if (this.waiter == null) {
182            throw new IllegalStateException
183                ("Nobody waiting on this object.");
184        }
185
186        // One condition might be shared by several WaitingThread instances.
187        // It probably isn't, but just in case: wake all, not just one.
188        this.cond.signalAll();
189    }
190
191    public void interrupt() {
192        aborted = true;
193        this.cond.signalAll();
194    }
195
196
197} // class WaitingThread
198