From 7c53de5b93dcb094a96e696e19df434bca4bb4da Mon Sep 17 00:00:00 2001 From: Roland Weber Date: Thu, 7 Feb 2008 20:01:04 +0000 Subject: [PATCH] HTTPCLIENT-726: test for spurious wakeups git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@619610 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/impl/conn/tsccm/TestAllTSCCM.java | 1 + .../impl/conn/tsccm/TestSpuriousWakeup.java | 196 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java diff --git a/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestAllTSCCM.java b/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestAllTSCCM.java index 0ad4cbb17..f0f4ac509 100644 --- a/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestAllTSCCM.java +++ b/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestAllTSCCM.java @@ -45,6 +45,7 @@ public class TestAllTSCCM extends TestCase { suite.addTest(TestDumbHelpers.suite()); suite.addTest(TestWaitingThread.suite()); + suite.addTest(TestSpuriousWakeup.suite()); return suite; } diff --git a/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java b/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java new file mode 100644 index 000000000..0938f1c64 --- /dev/null +++ b/module-client/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java @@ -0,0 +1,196 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.impl.conn.tsccm; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.Condition; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.http.HttpHost; +import org.apache.http.HttpVersion; +import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.PlainSocketFactory; +import org.apache.http.conn.Scheme; +import org.apache.http.conn.SchemeRegistry; +import org.apache.http.conn.SocketFactory; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.params.HttpConnectionManagerParams; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; + +// test imports +import org.apache.http.impl.conn.GetConnThread; + + + +/** + * Tests for spurious wakeups in WaitingThread. + * Requires some wrapping code to get at the lock and condition, + * which is required to trigger a wakeup without actually + * satisfying the condition. + * + * @author Roland Weber + */ +public class TestSpuriousWakeup extends TestCase { + + public final static + HttpHost TARGET = new HttpHost("target.test.invalid"); + public final static + HttpRoute ROUTE = new HttpRoute(TARGET); + + + public TestSpuriousWakeup(String testName) { + super(testName); + } + + public static void main(String args[]) { + String[] testCaseName = { TestSpuriousWakeup.class.getName() }; + junit.textui.TestRunner.main(testCaseName); + } + + public static Test suite() { + return new TestSuite(TestSpuriousWakeup.class); + } + + + /** + * An extended connection pool that gives access to some internals. + */ + private static class XConnPoolByRoute extends ConnPoolByRoute { + + /** The last WaitingThread object created. */ + protected WaitingThread newestWT; + + + public XConnPoolByRoute(ClientConnectionManager mgr) { + super(mgr); + } + + protected synchronized + WaitingThread newWaitingThread(Condition cond, + RouteSpecificPool rospl) { + WaitingThread wt = super.newWaitingThread(cond, rospl); + newestWT = wt; + return wt; + } + + } // class XConnPoolByRoute + + + /** + * An extended TSCCM that uses XConnPoolByRoute. + */ + private static class XTSCCM extends ThreadSafeClientConnManager { + + /** The extended connection pool. */ + protected XConnPoolByRoute extendedCPBR; + + + public XTSCCM(HttpParams params, SchemeRegistry schreg) { + super(params, schreg); + } + + protected AbstractConnPool createConnectionPool() { + extendedCPBR = new XConnPoolByRoute(this); + // no connection GC required + return extendedCPBR; + } + + } // class XTSCCM + + + + public void testSpuriousWakeup() throws Exception { + + // parameters with connection limit 1 + HttpParams params = new BasicHttpParams(); + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setUseExpectContinue(params, false); + HttpConnectionManagerParams.setDefaultMaxConnectionsPerHost(params, 1); + HttpConnectionManagerParams.setMaxTotalConnections(params, 1); + + SchemeRegistry schreg = new SchemeRegistry(); + SocketFactory sf = PlainSocketFactory.getSocketFactory(); + schreg.register(new Scheme("http", sf, 80)); + + XTSCCM mgr = new XTSCCM(params, schreg); + + try { + // take out the only connection + ManagedClientConnection conn = mgr.getConnection(ROUTE); + assertNotNull(conn); + + // send a thread waiting + GetConnThread gct = new GetConnThread(mgr, ROUTE, 0L); + gct.start(); + Thread.sleep(100); // give extra thread time to block + + assertEquals("thread not waiting", + Thread.State.WAITING, gct.getState()); + + // get access to the objects we need + Lock lck = mgr.extendedCPBR.poolLock; + Condition cnd = mgr.extendedCPBR.newestWT.getCondition(); + + // Now trigger spurious wakeups. We'll do it several times + // in a loop, just to be sure the connection manager has a + // fair chance of misbehaving, and the gct to register it. + + for (int i=0; i<3; i++) { + if (i > 0) + Thread.sleep(333); // don't go too fast + + try { + lck.lock(); + cnd.signalAll(); // this is the spurious wakeup + } finally { + lck.unlock(); + } + + // now give the waiting thread some time to register a wakeup + Thread.sleep(100); + + assertEquals("thread no longer waiting, iteration " + i, + Thread.State.WAITING, gct.getState()); + } + } finally { + // don't worry about releasing the connection, just shut down + mgr.shutdown(); + } + } // testSpuriousWakeup + + +} // class TestSpuriousWakeup