/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. 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;
import com.hazelcast.logging.ILogger;
import com.hazelcast.util.ThreadWatcher;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public final class InOutSelector implements Runnable {
protected final ILogger logger;
protected final Selector selector;
protected final Queue<Runnable> selectorQueue = new ConcurrentLinkedQueue<Runnable>();
protected final ConnectionManager connectionManager;
private final int waitTime;
protected boolean live = true;
protected final ThreadWatcher threadWatcher = new ThreadWatcher();
final static long TEN_SECOND_MILLIS = TimeUnit.SECONDS.toMillis(10);
private long lastPublish = 0;
public InOutSelector(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
logger = connectionManager.ioService.getLogger(this.getClass().getName());
this.waitTime = 1;
Selector selectorTemp = null;
try {
selectorTemp = Selector.open();
} catch (final IOException e) {
handleSelectorException(e);
}
this.selector = selectorTemp;
live = true;
}
public void shutdown() {
selectorQueue.clear();
try {
final CountDownLatch l = new CountDownLatch(1);
addTask(new Runnable() {
public void run() {
live = false;
threadLocalShutdown();
l.countDown();
}
});
l.await(5, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
}
}
protected void threadLocalShutdown() {
}
public void addTask(final Runnable runnable) {
selectorQueue.offer(runnable);
}
private void processSelectionQueue() {
//noinspection WhileLoopSpinsOnField
while (live) {
final Runnable runnable = selectorQueue.poll();
if (runnable == null) {
return;
}
runnable.run();
}
}
public void publishUtilization() {
}
public final void run() {
try {
connectionManager.ioService.onIOThreadStart();
//noinspection WhileLoopSpinsOnField
while (live) {
threadWatcher.incrementRunCount();
long currentMillis = System.currentTimeMillis();
if ((currentMillis - lastPublish) > TEN_SECOND_MILLIS) {
publishUtilization();
lastPublish = currentMillis;
}
processSelectionQueue();
if (!live) return;
int selectedKeyCount;
try {
long startWait = System.nanoTime();
selectedKeyCount = selector.select(waitTime);
long now = System.nanoTime();
threadWatcher.addWait((now - startWait), now);
if (Thread.interrupted()) {
connectionManager.ioService.handleInterruptedException(Thread.currentThread(), new RuntimeException());
return;
}
} catch (Throwable exp) {
continue;
}
if (selectedKeyCount == 0) {
continue;
}
final Set<SelectionKey> setSelectedKeys = selector.selectedKeys();
final Iterator<SelectionKey> it = setSelectedKeys.iterator();
while (it.hasNext()) {
final SelectionKey sk = it.next();
try {
it.remove();
if (sk.isValid() && sk.isReadable()) {
Connection connection = (Connection) sk.attachment();
connection.getReadHandler().handle();
}
if (sk.isValid() && sk.isWritable()) {
sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
Connection connection = (Connection) sk.attachment();
connection.getWriteHandler().handle();
}
} catch (Throwable e) {
handleSelectorException(e);
}
}
}
} catch (OutOfMemoryError e) {
connectionManager.ioService.onOutOfMemory(e);
} catch (Throwable e) {
logger.log(Level.WARNING, "unhandled exception in " + Thread.currentThread().getName(), e);
} finally {
try {
logger.log(Level.FINEST, "closing selector " + Thread.currentThread().getName());
selector.close();
} catch (final Exception ignored) {
}
}
}
protected void handleSelectorException(final Throwable e) {
String msg = "Selector exception at " + Thread.currentThread().getName() + ", cause= " + e.toString();
logger.log(Level.WARNING, msg, e);
}
}