
com.hazelcast.nio.tcp.SocketAcceptorThread Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
*
* Licensed 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 com.hazelcast.nio.tcp;
import com.hazelcast.instance.OutOfMemoryErrorDispatcher;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.IOService;
import com.hazelcast.nio.tcp.nonblocking.SelectorMode;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import static com.hazelcast.nio.IOUtil.closeResource;
import static java.lang.System.currentTimeMillis;
import static java.util.concurrent.TimeUnit.SECONDS;
public class SocketAcceptorThread extends Thread {
private static final long SHUTDOWN_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final long SELECT_TIMEOUT_MILLIS = SECONDS.toMillis(60);
private static final int SELECT_IDLE_COUNT_THRESHOLD = 10;
private final ServerSocketChannel serverSocketChannel;
private final TcpIpConnectionManager connectionManager;
private final ILogger logger;
private final IOService ioService;
// When true, enables workaround for bug occurring when SelectorImpl.select returns immediately
// with no channels selected, resulting in 100% CPU usage while doing no progress.
// See issue: https://github.com/hazelcast/hazelcast/issues/7943
private final boolean selectorWorkaround = (SelectorMode.getConfiguredValue() == SelectorMode.SELECT_WITH_FIX);
private Selector selector;
private SelectionKey selectionKey;
public SocketAcceptorThread(
ThreadGroup threadGroup,
String name,
ServerSocketChannel serverSocketChannel,
TcpIpConnectionManager connectionManager) {
super(threadGroup, name);
this.serverSocketChannel = serverSocketChannel;
this.connectionManager = connectionManager;
this.ioService = connectionManager.getIoService();
this.logger = ioService.getLogger(this.getClass().getName());
}
@Override
public void run() {
if (logger.isFinestEnabled()) {
logger.finest("Starting SocketAcceptor on " + serverSocketChannel);
}
try {
selector = Selector.open();
serverSocketChannel.configureBlocking(false);
selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
if (selectorWorkaround) {
acceptLoopWithSelectorFix();
} else {
acceptLoop();
}
} catch (OutOfMemoryError e) {
OutOfMemoryErrorDispatcher.onOutOfMemory(e);
} catch (IOException e) {
logger.severe(e.getClass().getName() + ": " + e.getMessage(), e);
} finally {
closeSelector();
}
}
private void acceptLoop() throws IOException {
while (connectionManager.isLive()) {
// block until new connection or interruption.
int keyCount = selector.select();
if (isInterrupted()) {
break;
}
if (keyCount == 0) {
continue;
}
Iterator it = selector.selectedKeys().iterator();
handleSelectionKeys(it);
}
}
private void acceptLoopWithSelectorFix() throws IOException {
int idleCount = 0;
while (connectionManager.isLive()) {
// block with a timeout until new connection or interruption.
long before = currentTimeMillis();
int keyCount = selector.select(SELECT_TIMEOUT_MILLIS);
if (isInterrupted()) {
break;
}
if (keyCount == 0) {
long selectTimeTaken = currentTimeMillis() - before;
idleCount = selectTimeTaken < SELECT_TIMEOUT_MILLIS ? idleCount + 1 : 0;
// select unblocked before timing out with no keys selected --> bug detected
if (idleCount > SELECT_IDLE_COUNT_THRESHOLD) {
// rebuild the selector
rebuildSelector();
idleCount = 0;
}
continue;
}
idleCount = 0;
Iterator it = selector.selectedKeys().iterator();
handleSelectionKeys(it);
}
}
private void rebuildSelector() throws IOException {
// cancel existing selection key, register new one on the new selector
selectionKey.cancel();
closeSelector();
Selector newSelector = Selector.open();
selector = newSelector;
selectionKey = serverSocketChannel.register(newSelector, SelectionKey.OP_ACCEPT);
}
private void handleSelectionKeys(Iterator it) {
while (it.hasNext()) {
SelectionKey sk = it.next();
it.remove();
// of course it is acceptable!
if (sk.isValid() && sk.isAcceptable()) {
acceptSocket();
}
}
}
private void closeSelector() {
if (selector == null) {
return;
}
if (logger.isFinestEnabled()) {
logger.finest("Closing selector " + Thread.currentThread().getName());
}
try {
selector.close();
} catch (Exception e) {
logger.finest("Exception while closing selector", e);
}
}
private void acceptSocket() {
if (!connectionManager.isLive()) {
return;
}
SocketChannelWrapper socketChannelWrapper = null;
try {
final SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
socketChannelWrapper = connectionManager.wrapSocketChannel(socketChannel, false);
}
} catch (Exception e) {
if (e instanceof ClosedChannelException && !connectionManager.isLive()) {
// ClosedChannelException
// or AsynchronousCloseException
// or ClosedByInterruptException
logger.finest("Terminating socket acceptor thread...", e);
} else {
logger.warning("Unexpected error while accepting connection! "
+ e.getClass().getName() + ": " + e.getMessage());
try {
serverSocketChannel.close();
} catch (Exception ex) {
logger.finest("Closing server socket failed", ex);
}
ioService.onFatalError(e);
}
}
if (socketChannelWrapper != null) {
final SocketChannelWrapper socketChannel = socketChannelWrapper;
logger.info("Accepting socket connection from " + socketChannel.socket().getRemoteSocketAddress());
if (connectionManager.isSocketInterceptorEnabled()) {
configureAndAssignSocket(socketChannel);
} else {
ioService.executeAsync(new Runnable() {
@Override
public void run() {
configureAndAssignSocket(socketChannel);
}
});
}
}
}
private void configureAndAssignSocket(SocketChannelWrapper socketChannel) {
try {
connectionManager.initSocket(socketChannel.socket());
connectionManager.interceptSocket(socketChannel.socket(), true);
socketChannel.configureBlocking(connectionManager.getIoThreadingModel().isBlocking());
connectionManager.newConnection(socketChannel, null);
} catch (Exception e) {
logger.warning(e.getClass().getName() + ": " + e.getMessage(), e);
closeResource(socketChannel);
}
}
public void shutdown() {
logger.finest("Shutting down SocketAcceptor thread.");
interrupt();
try {
join(SHUTDOWN_TIMEOUT_MILLIS);
} catch (InterruptedException e) {
logger.finest(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy