1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.tomcat.util.net;
18
19 import org.apache.juli.logging.Log;
20 import org.apache.juli.logging.LogFactory;
21 import org.apache.tomcat.jni.Error;
22 import org.apache.tomcat.util.ExceptionUtils;
23 import org.apache.tomcat.util.res.StringManager;
24
25 public class Acceptor<U> implements Runnable {
26
27 private static final Log log = LogFactory.getLog(Acceptor.class);
28 private static final StringManager sm = StringManager.getManager(Acceptor.class);
29
30 private static final int INITIAL_ERROR_DELAY = 50;
31 private static final int MAX_ERROR_DELAY = 1600;
32
33 private final AbstractEndpoint<?,U> endpoint;
34 private String threadName;
35 protected volatile AcceptorState state = AcceptorState.NEW;
36
37
38 public Acceptor(AbstractEndpoint<?,U> endpoint) {
39 this.endpoint = endpoint;
40 }
41
42
43 public final AcceptorState getState() {
44 return state;
45 }
46
47
48 final void setThreadName(final String threadName) {
49 this.threadName = threadName;
50 }
51
52
53 final String getThreadName() {
54 return threadName;
55 }
56
57
58 @Override
59 public void run() {
60
61 int errorDelay = 0;
62
63 // Loop until we receive a shutdown command
64 while (endpoint.isRunning()) {
65
66 // Loop if endpoint is paused
67 while (endpoint.isPaused() && endpoint.isRunning()) {
68 state = AcceptorState.PAUSED;
69 try {
70 Thread.sleep(50);
71 } catch (InterruptedException e) {
72 // Ignore
73 }
74 }
75
76 if (!endpoint.isRunning()) {
77 break;
78 }
79 state = AcceptorState.RUNNING;
80
81 try {
82 //if we have reached max connections, wait
83 endpoint.countUpOrAwaitConnection();
84
85 // Endpoint might have been paused while waiting for latch
86 // If that is the case, don't accept new connections
87 if (endpoint.isPaused()) {
88 continue;
89 }
90
91 U socket = null;
92 try {
93 // Accept the next incoming connection from the server
94 // socket
95 socket = endpoint.serverSocketAccept();
96 } catch (Exception ioe) {
97 // We didn't get a socket
98 endpoint.countDownConnection();
99 if (endpoint.isRunning()) {
100 // Introduce delay if necessary
101 errorDelay = handleExceptionWithDelay(errorDelay);
102 // re-throw
103 throw ioe;
104 } else {
105 break;
106 }
107 }
108 // Successful accept, reset the error delay
109 errorDelay = 0;
110
111 // Configure the socket
112 if (endpoint.isRunning() && !endpoint.isPaused()) {
113 // setSocketOptions() will hand the socket off to
114 // an appropriate processor if successful
115 if (!endpoint.setSocketOptions(socket)) {
116 endpoint.closeSocket(socket);
117 }
118 } else {
119 endpoint.destroySocket(socket);
120 }
121 } catch (Throwable t) {
122 ExceptionUtils.handleThrowable(t);
123 String msg = sm.getString("endpoint.accept.fail");
124 // APR specific.
125 // Could push this down but not sure it is worth the trouble.
126 if (t instanceof Error) {
127 Error e = (Error) t;
128 if (e.getError() == 233) {
129 // Not an error on HP-UX so log as a warning
130 // so it can be filtered out on that platform
131 // See bug 50273
132 log.warn(msg, t);
133 } else {
134 log.error(msg, t);
135 }
136 } else {
137 log.error(msg, t);
138 }
139 }
140 }
141 state = AcceptorState.ENDED;
142 }
143
144
145 /**
146 * Handles exceptions where a delay is required to prevent a Thread from
147 * entering a tight loop which will consume CPU and may also trigger large
148 * amounts of logging. For example, this can happen if the ulimit for open
149 * files is reached.
150 *
151 * @param currentErrorDelay The current delay being applied on failure
152 * @return The delay to apply on the next failure
153 */
154 protected int handleExceptionWithDelay(int currentErrorDelay) {
155 // Don't delay on first exception
156 if (currentErrorDelay > 0) {
157 try {
158 Thread.sleep(currentErrorDelay);
159 } catch (InterruptedException e) {
160 // Ignore
161 }
162 }
163
164 // On subsequent exceptions, start the delay at 50ms, doubling the delay
165 // on every subsequent exception until the delay reaches 1.6 seconds.
166 if (currentErrorDelay == 0) {
167 return INITIAL_ERROR_DELAY;
168 } else if (currentErrorDelay < MAX_ERROR_DELAY) {
169 return currentErrorDelay * 2;
170 } else {
171 return MAX_ERROR_DELAY;
172 }
173 }
174
175
176 public enum AcceptorState {
177 NEW, RUNNING, PAUSED, ENDED
178 }
179 }
180