michael@0: /*
michael@0: * ====================================================================
michael@0: *
michael@0: * Licensed to the Apache Software Foundation (ASF) under one or more
michael@0: * contributor license agreements. See the NOTICE file distributed with
michael@0: * this work for additional information regarding copyright ownership.
michael@0: * The ASF licenses this file to You under the Apache License, Version 2.0
michael@0: * (the "License"); you may not use this file except in compliance with
michael@0: * the License. You may obtain a copy of the License at
michael@0: *
michael@0: * http://www.apache.org/licenses/LICENSE-2.0
michael@0: *
michael@0: * Unless required by applicable law or agreed to in writing, software
michael@0: * distributed under the License is distributed on an "AS IS" BASIS,
michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0: * See the License for the specific language governing permissions and
michael@0: * limitations under the License.
michael@0: * ====================================================================
michael@0: *
michael@0: * This software consists of voluntary contributions made by many
michael@0: * individuals on behalf of the Apache Software Foundation. For more
michael@0: * information on the Apache Software Foundation, please see
michael@0: * .
michael@0: *
michael@0: */
michael@0:
michael@0: package ch.boye.httpclientandroidlib.impl.conn.tsccm;
michael@0:
michael@0:
michael@0: import java.util.Date;
michael@0: import java.util.concurrent.locks.Condition;
michael@0:
michael@0: import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
michael@0:
michael@0: /**
michael@0: * Represents a thread waiting for a connection.
michael@0: * This class implements throwaway objects. It is instantiated whenever
michael@0: * a thread needs to wait. Instances are not re-used, except if the
michael@0: * waiting thread experiences a spurious wakeup and continues to wait.
michael@0: *
michael@0: * All methods assume external synchronization on the condition
michael@0: * passed to the constructor.
michael@0: * Instances of this class do not synchronize access!
michael@0: *
michael@0: *
michael@0: * @since 4.0
michael@0: */
michael@0: @NotThreadSafe
michael@0: public class WaitingThread {
michael@0:
michael@0: /** The condition on which the thread is waiting. */
michael@0: private final Condition cond;
michael@0:
michael@0: /** The route specific pool on which the thread is waiting. */
michael@0: //@@@ replace with generic pool interface
michael@0: private final RouteSpecificPool pool;
michael@0:
michael@0: /** The thread that is waiting for an entry. */
michael@0: private Thread waiter;
michael@0:
michael@0: /** True if this was interrupted. */
michael@0: private boolean aborted;
michael@0:
michael@0:
michael@0: /**
michael@0: * Creates a new entry for a waiting thread.
michael@0: *
michael@0: * @param cond the condition for which to wait
michael@0: * @param pool the pool on which the thread will be waiting,
michael@0: * or null
michael@0: */
michael@0: public WaitingThread(Condition cond, RouteSpecificPool pool) {
michael@0:
michael@0: if (cond == null) {
michael@0: throw new IllegalArgumentException("Condition must not be null.");
michael@0: }
michael@0:
michael@0: this.cond = cond;
michael@0: this.pool = pool;
michael@0: }
michael@0:
michael@0:
michael@0: /**
michael@0: * Obtains the condition.
michael@0: *
michael@0: * @return the condition on which to wait, never null
michael@0: */
michael@0: public final Condition getCondition() {
michael@0: // not synchronized
michael@0: return this.cond;
michael@0: }
michael@0:
michael@0:
michael@0: /**
michael@0: * Obtains the pool, if there is one.
michael@0: *
michael@0: * @return the pool on which a thread is or was waiting,
michael@0: * or null
michael@0: */
michael@0: public final RouteSpecificPool getPool() {
michael@0: // not synchronized
michael@0: return this.pool;
michael@0: }
michael@0:
michael@0:
michael@0: /**
michael@0: * Obtains the thread, if there is one.
michael@0: *
michael@0: * @return the thread which is waiting, or null
michael@0: */
michael@0: public final Thread getThread() {
michael@0: // not synchronized
michael@0: return this.waiter;
michael@0: }
michael@0:
michael@0:
michael@0: /**
michael@0: * Blocks the calling thread.
michael@0: * This method returns when the thread is notified or interrupted,
michael@0: * if a timeout occurrs, or if there is a spurious wakeup.
michael@0: *
michael@0: * This method assumes external synchronization.
michael@0: *
michael@0: * @param deadline when to time out, or null
for no timeout
michael@0: *
michael@0: * @return true
if the condition was satisfied,
michael@0: * false
in case of a timeout.
michael@0: * Typically, a call to {@link #wakeup} is used to indicate
michael@0: * that the condition was satisfied. Since the condition is
michael@0: * accessible outside, this cannot be guaranteed though.
michael@0: *
michael@0: * @throws InterruptedException if the waiting thread was interrupted
michael@0: *
michael@0: * @see #wakeup
michael@0: */
michael@0: public boolean await(Date deadline)
michael@0: throws InterruptedException {
michael@0:
michael@0: // This is only a sanity check. We cannot synchronize here,
michael@0: // the lock would not be released on calling cond.await() below.
michael@0: if (this.waiter != null) {
michael@0: throw new IllegalStateException
michael@0: ("A thread is already waiting on this object." +
michael@0: "\ncaller: " + Thread.currentThread() +
michael@0: "\nwaiter: " + this.waiter);
michael@0: }
michael@0:
michael@0: if (aborted)
michael@0: throw new InterruptedException("Operation interrupted");
michael@0:
michael@0: this.waiter = Thread.currentThread();
michael@0:
michael@0: boolean success = false;
michael@0: try {
michael@0: if (deadline != null) {
michael@0: success = this.cond.awaitUntil(deadline);
michael@0: } else {
michael@0: this.cond.await();
michael@0: success = true;
michael@0: }
michael@0: if (aborted)
michael@0: throw new InterruptedException("Operation interrupted");
michael@0: } finally {
michael@0: this.waiter = null;
michael@0: }
michael@0: return success;
michael@0:
michael@0: } // await
michael@0:
michael@0:
michael@0: /**
michael@0: * Wakes up the waiting thread.
michael@0: *
michael@0: * This method assumes external synchronization.
michael@0: */
michael@0: public void wakeup() {
michael@0:
michael@0: // If external synchronization and pooling works properly,
michael@0: // this cannot happen. Just a sanity check.
michael@0: if (this.waiter == null) {
michael@0: throw new IllegalStateException
michael@0: ("Nobody waiting on this object.");
michael@0: }
michael@0:
michael@0: // One condition might be shared by several WaitingThread instances.
michael@0: // It probably isn't, but just in case: wake all, not just one.
michael@0: this.cond.signalAll();
michael@0: }
michael@0:
michael@0: public void interrupt() {
michael@0: aborted = true;
michael@0: this.cond.signalAll();
michael@0: }
michael@0:
michael@0:
michael@0: } // class WaitingThread