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