/* * 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. */ package org.apache.tomcat.util.net; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.jni.Error; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.res.StringManager; public class Acceptor<U> implements Runnable { private static final Log log = LogFactory.getLog(Acceptor.class); private static final StringManager sm = StringManager.getManager(Acceptor.class); private static final int INITIAL_ERROR_DELAY = 50; private static final int MAX_ERROR_DELAY = 1600; private final AbstractEndpoint<?,U> endpoint; private String threadName; protected volatile AcceptorState state = AcceptorState.NEW; public Acceptor(AbstractEndpoint<?,U> endpoint) { this.endpoint = endpoint; } public final AcceptorState getState() { return state; } final void setThreadName(final String threadName) { this.threadName = threadName; } final String getThreadName() { return threadName; } @Override public void run() { int errorDelay = 0; // Loop until we receive a shutdown command while (endpoint.isRunning()) { // Loop if endpoint is paused while (endpoint.isPaused() && endpoint.isRunning()) { state = AcceptorState.PAUSED; try { Thread.sleep(50); } catch (InterruptedException e) { // Ignore } } if (!endpoint.isRunning()) { break; } state = AcceptorState.RUNNING; try { //if we have reached max connections, wait endpoint.countUpOrAwaitConnection(); // Endpoint might have been paused while waiting for latch // If that is the case, don't accept new connections if (endpoint.isPaused()) { continue; } U socket = null; try { // Accept the next incoming connection from the server // socket socket = endpoint.serverSocketAccept(); } catch (Exception ioe) { // We didn't get a socket endpoint.countDownConnection(); if (endpoint.isRunning()) { // Introduce delay if necessary errorDelay = handleExceptionWithDelay(errorDelay); // re-throw throw ioe; } else { break; } } // Successful accept, reset the error delay errorDelay = 0; // Configure the socket if (endpoint.isRunning() && !endpoint.isPaused()) { // setSocketOptions() will hand the socket off to // an appropriate processor if successful if (!endpoint.setSocketOptions(socket)) { endpoint.closeSocket(socket); } } else { endpoint.destroySocket(socket); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); String msg = sm.getString("endpoint.accept.fail"); // APR specific. // Could push this down but not sure it is worth the trouble. if (t instanceof Error) { Error e = (Error) t; if (e.getError() == 233) { // Not an error on HP-UX so log as a warning // so it can be filtered out on that platform // See bug 50273 log.warn(msg, t); } else { log.error(msg, t); } } else { log.error(msg, t); } } } state = AcceptorState.ENDED; } /** * Handles exceptions where a delay is required to prevent a Thread from * entering a tight loop which will consume CPU and may also trigger large * amounts of logging. For example, this can happen if the ulimit for open * files is reached. * * @param currentErrorDelay The current delay being applied on failure * @return The delay to apply on the next failure */ private int handleExceptionWithDelay(int currentErrorDelay) { // Don't delay on first exception if (currentErrorDelay > 0) { try { Thread.sleep(currentErrorDelay); } catch (InterruptedException e) { // Ignore } } // On subsequent exceptions, start the delay at 50ms, doubling the delay // on every subsequent exception until the delay reaches 1.6 seconds. if (currentErrorDelay == 0) { return INITIAL_ERROR_DELAY; } else if (currentErrorDelay < MAX_ERROR_DELAY) { return currentErrorDelay * 2; } else { return MAX_ERROR_DELAY; } } public enum AcceptorState { NEW, RUNNING, PAUSED, ENDED } }