All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xsocket.connection.IoSocketDispatcher Maven / Gradle / Ivy

/*
 * Copyright (c) xlightweb.org, 2006 - 2010. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
 * The latest copy of this software may be found on http://www.xsocket.org/
 */
package org.xsocket.connection;


import java.io.Closeable;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.xsocket.DataConverter;



/**
 * Implementation of the {@link IDispatcher}
 *
 * 

This is a xSocket internal class and subject to change * * @author [email protected] */ final class IoSocketDispatcher extends MonitoredSelector implements Runnable, Closeable { private static final Logger LOG = Logger.getLogger(IoSocketDispatcher.class.getName()); static final String DISPATCHER_PREFIX = "xDispatcher"; // queues private final ConcurrentLinkedQueue registerQueue = new ConcurrentLinkedQueue(); private final ConcurrentLinkedQueue deregisterQueue = new ConcurrentLinkedQueue(); private final ConcurrentLinkedQueue keyUpdateQueue = new ConcurrentLinkedQueue(); // id private static int nextId = 1; private final String name; private final int id; private final static ThreadLocal THREADBOUND_ID = new ThreadLocal(); private final static ThreadLocal DIRECT_CALL_COUNTER = new ThreadLocal(); // flags private final AtomicBoolean isOpen = new AtomicBoolean(true); // connection handling private static final Integer MAX_HANDLES = IoProvider.getMaxHandles(); private int roughNumOfRegisteredHandles; private Selector selector; // memory management private final AbstractMemoryManager memoryManager; // wakeup private long lastTimeWokeUp = System.currentTimeMillis(); // statistics private long statisticsStartTime = System.currentTimeMillis(); private long countIdleTimeouts = 0; private long countConnectionTimeouts = 0; private long handledRegistractions = 0; private long handledReads = 0; private long handledWrites = 0; private long lastRequestReceiveRate = System.currentTimeMillis(); private long lastRequestSendRate = System.currentTimeMillis(); private long receivedBytes = 0; private long sentBytes = 0; private long countUnregisteredWrite = 0; public IoSocketDispatcher(AbstractMemoryManager memoryManager, String name) { this.memoryManager = memoryManager; this.name = IoSocketDispatcher.DISPATCHER_PREFIX + name; synchronized (this) { id = nextId; nextId++; } try { selector = Selector.open(); } catch (IOException ioe) { String text = "exception occured while opening selector. Reason: " + ioe.toString(); LOG.severe(text); throw new RuntimeException(text, ioe); } if (LOG.isLoggable(Level.FINE)) { LOG.fine("dispatcher " + this.hashCode() + " has been closed"); } } String getName() { return name; } int getId() { return id; } private static Integer getThreadBoundId() { return THREADBOUND_ID.get(); } long getCountUnregisteredWrite() { return countUnregisteredWrite; } Integer getMaxRegisterdHandles() { return MAX_HANDLES; } @Override int getNumRegisteredHandles() { int hdls = selector.keys().size(); roughNumOfRegisteredHandles = hdls; return hdls; } int getRoughNumRegisteredHandles() { return roughNumOfRegisteredHandles; } @Override void reinit() throws IOException { // save old selector Selector oldSelector = selector; // retrieve all keys HashSet keys = new HashSet(); keys.addAll(oldSelector.keys()); // create new selector selector = Selector.open(); // "move" all sockets to the new selector for (SelectionKey key : keys) { // get info int ops = key.interestOps(); IoSocketHandler socketHandler = (IoSocketHandler) key.attachment(); // cancel old key key.cancel(); try { socketHandler.getChannel().register(selector, ops, socketHandler); } catch (IOException ioe) { LOG.warning("could not reinit " + socketHandler.toString() + " " + DataConverter.toString(ioe)); } } // close old selector oldSelector.close(); if (LOG.isLoggable(Level.FINE)) { LOG.fine("selector has been reinitialized"); } } /** * {@inheritDoc} */ public void run() { // set thread name and attach dispatcher id to thread Thread.currentThread().setName(name); THREADBOUND_ID.set(id); DIRECT_CALL_COUNTER.set(0); if (LOG.isLoggable(Level.FINE)) { LOG.fine("selector " + name + " listening ..."); } int handledTasks = 0; while(isOpen.get()) { try { int eventCount = selector.select(5000); handledTasks = performRegisterHandlerTasks(); handledTasks += performKeyUpdateTasks(); if (eventCount > 0) { handleReadWriteKeys(); } handledTasks += performDeregisterHandlerTasks(); checkForLooping(eventCount + handledTasks, lastTimeWokeUp); } catch (Throwable e) { // eat and log exception if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + Thread.currentThread().getName() + "] exception occured while processing. Reason " + DataConverter.toString(e)); } } } for (IoSocketHandler socketHandler : getRegistered()) { socketHandler.onDeregisteredEvent(); } try { selector.close(); } catch (Exception e) { // eat and log exception if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by close selector within tearDown " + DataConverter.toString(e)); } } } private void handleReadWriteKeys() { Set selectedEventKeys = selector.selectedKeys(); Iterator it = selectedEventKeys.iterator(); // handle read & write while (it.hasNext()) { try { SelectionKey eventKey = it.next(); it.remove(); IoSocketHandler socketHandler = (IoSocketHandler) eventKey.attachment(); try { // read data if (eventKey.isValid() && eventKey.isReadable()) { onReadableEvent(socketHandler); } // write data if (eventKey.isValid() && eventKey.isWritable()) { onWriteableEvent(socketHandler); } } catch (Exception e) { socketHandler.close(e); } } catch (Exception e) { // eat and log exception if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by handling selection keys + " + e.toString()); } } } } private void onReadableEvent(IoSocketHandler socketHandler) { try { long read = socketHandler.onReadableEvent(); receivedBytes += read; handledReads++; } catch (Exception t) { SelectionKey key = getSelectionKey(socketHandler); if ((key != null) && key.isValid()) { key.cancel(); } if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by handling readable event " + DataConverter.toString(t)); } socketHandler.closeSilence(true); } } private void onWriteableEvent(IoSocketHandler socketHandler) { try { socketHandler.onWriteableEvent(); handledWrites++; } catch (ClosedChannelException ce) { IOException ioe = ConnectionUtils.toIOException("error occured by handling readable event. reason closed channel exception " + ce.toString(), ce); socketHandler.close(ioe); } catch (Exception e) { e = ConnectionUtils.toIOException("error occured by handling readable event. reason " + e.toString(), e); socketHandler.close(e); } } private void wakeUp() { lastTimeWokeUp = System.currentTimeMillis(); selector.wakeup(); } boolean preRegister() { // inc rough num of registered handles roughNumOfRegisteredHandles++; // check if max size reached if ((MAX_HANDLES != null) && (roughNumOfRegisteredHandles >= MAX_HANDLES) && // rough check (getNumRegisteredHandles() >= MAX_HANDLES)) { // accurate check return false; } else { return true; } } /** * {@inheritDoc} */ public boolean register(IoSocketHandler socketHandler, int ops) throws IOException { assert (!socketHandler.getChannel().isBlocking()); socketHandler.setMemoryManager(memoryManager); if (isDispatcherInstanceThread()) { registerHandlerNow(socketHandler, ops); } else { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + socketHandler.getId() + "] add new connection to register task queue"); } registerQueue.add(new RegisterTask(socketHandler, ops)); wakeUp(); } return true; } private final class RegisterTask implements Runnable { private final IoSocketHandler socketHandler; private final int ops; public RegisterTask(IoSocketHandler socketHandler, int ops) { this.socketHandler = socketHandler; this.ops = ops; } public void run() { try { registerHandlerNow(socketHandler, ops); } catch (IOException ioe) { ioe = ConnectionUtils.toIOException("error occured by registering handler " + socketHandler.getId() + " " + ioe.toString(), ioe); socketHandler.close(ioe); } } } /** * {@inheritDoc} */ public void deregisterAndClose(IoSocketHandler handler) { if (isOpen.get()) { if (isDispatcherInstanceThread()) { deregisterAndCloseNow(handler); } else { deregisterQueue.add(handler); wakeUp(); } } else { handler.onDeregisteredEvent(); } } private int performDeregisterHandlerTasks() { int handledTasks = 0; while (true) { IoSocketHandler socketHandler = deregisterQueue.poll(); if (socketHandler == null) { return handledTasks; } else { deregisterAndCloseNow(socketHandler); handledTasks++; } } } private void deregisterAndCloseNow(IoSocketHandler socketHandler) { try { SelectionKey key = socketHandler.getChannel().keyFor(selector); if ((key != null) && key.isValid()) { key.cancel(); if (roughNumOfRegisteredHandles > 0) { roughNumOfRegisteredHandles--; } } } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by deregistering socket handler " + e.toString()); } } socketHandler.onDeregisteredEvent(); } public void addKeyUpdateTask(Runnable task) { keyUpdateQueue.add(task); wakeUp(); } public void flushKeyUpdate() { wakeUp(); } public void suspendRead(final IoSocketHandler socketHandler) throws IOException { addKeyUpdateTask(new UpdateReadSelectionKeyTask(socketHandler, false)); } public void resumeRead(IoSocketHandler socketHandler) throws IOException { addKeyUpdateTask(new UpdateReadSelectionKeyTask(socketHandler, true)); } private final class UpdateReadSelectionKeyTask implements Runnable { private final IoSocketHandler socketHandler; private final boolean isSet; public UpdateReadSelectionKeyTask(IoSocketHandler socketHandler, boolean isSet) { this.socketHandler = socketHandler; this.isSet = isSet; } public void run() { assert (isDispatcherInstanceThread()); try { if (isSet) { setReadSelectionKeyNow(socketHandler); } else { unsetReadSelectionKeyNow(socketHandler); } } catch (Exception e) { e = ConnectionUtils.toIOException("Error by set read selection key now " + e.toString(), e); socketHandler.close(e); } } @Override public String toString() { return "setReadSelectionKeyTask#" + super.toString(); } } private int performKeyUpdateTasks() { int handledTasks = 0; while (true) { Runnable keyUpdateTask = keyUpdateQueue.poll(); if (keyUpdateTask == null) { return handledTasks; } else { keyUpdateTask.run(); handledTasks++; } } } public boolean isDispatcherInstanceThread() { Integer tbid = getThreadBoundId(); if ((tbid != null) && (tbid == id)) { return true; } return false; } private SelectionKey getSelectionKey(IoSocketHandler socketHandler) { SelectionKey key = socketHandler.getChannel().keyFor(selector); if (LOG.isLoggable(Level.FINE)) { if (key == null) { LOG.fine("[" + socketHandler.getId() + "] key is null"); } else if (!key.isValid()) { LOG.fine("[" + socketHandler.getId() + "] key is not valid"); } } return key; } public boolean setWriteSelectionKeyNow(final IoSocketHandler socketHandler) throws IOException { assert (isDispatcherInstanceThread()); SelectionKey key = getSelectionKey(socketHandler); if (key != null) { if (!isWriteable(key)) { key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); return true; } } else { throw new IOException("[" + socketHandler.getId() + "] Error occured by setting write selection key. key is null"); } return false; } public boolean unsetWriteSelectionKeyNow(final IoSocketHandler socketHandler) throws IOException { assert (isDispatcherInstanceThread()); SelectionKey key = getSelectionKey(socketHandler); if (key != null) { if (isWriteable(key)) { key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); return true; } } else { throw new IOException("[" + socketHandler.getId() + "] Error occured by unsetting write selection key. key is null"); } return false; } public boolean setReadSelectionKeyNow(final IoSocketHandler socketHandler) throws IOException { assert (isDispatcherInstanceThread()); SelectionKey key = getSelectionKey(socketHandler); if (key != null) { if (!isReadable(key)) { key.interestOps(key.interestOps() | SelectionKey.OP_READ); if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + socketHandler.getId() + "] key set to " + printSelectionKey(socketHandler)); } onReadableEvent(socketHandler); return true; } } else { throw new IOException("[" + socketHandler.getId() + "] Error occured by setting read selection key. key is null"); } return false; } private void unsetReadSelectionKeyNow(final IoSocketHandler socketHandler) throws IOException { assert (isDispatcherInstanceThread()); SelectionKey key = getSelectionKey(socketHandler); if (key != null) { if (isReadable(key)) { key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); } } else { throw new IOException("[" + socketHandler.getId() + "] Error occured by unsetting read selection key. key is null"); } } String getRegisteredOpsInfo(IoSocketHandler socketHandler) { SelectionKey key = getSelectionKey(socketHandler); if (key == null) { return ""; } else { return ConnectionUtils.printSelectionKeyValue(key.interestOps()); } } private int performRegisterHandlerTasks() throws IOException { int handledTasks = 0; while (true) { Runnable registerTask = registerQueue.poll(); if (registerTask == null) { return handledTasks; } else { registerTask.run(); handledTasks++; } } } private void registerHandlerNow(IoSocketHandler socketHandler, int ops) throws IOException { if (socketHandler.isOpen()) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + socketHandler.getId() + "] registering connection"); } try { socketHandler.getChannel().register(selector, ops, socketHandler); socketHandler.onRegisteredEvent(); handledRegistractions++; } catch (Exception e) { socketHandler.close(e); } } else { socketHandler.onRegisteredFailedEvent(new IOException("could not register handler " + socketHandler.getId() + " because the channel is closed")); } } /** * {@inheritDoc} */ public Set getRegistered() { Set registered = new HashSet(); Set keys = selector.keys(); for (SelectionKey key : keys) { IoSocketHandler socketHandler = (IoSocketHandler) key.attachment(); registered.add(socketHandler); } return registered; } /** * returns if the dispatcher is open * * @return true, if the dispatcher is open */ public boolean isOpen() { return isOpen.get(); } boolean isReadable(IoSocketHandler socketHandler) { SelectionKey key = getSelectionKey(socketHandler); if ((key != null) && ((key.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ)) { return true; } return false; } private boolean isReadable(SelectionKey key) { if ((key != null) && ((key.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ)) { return true; } return false; } private boolean isWriteable(SelectionKey key) { if ((key != null) && ((key.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)) { return true; } return false; } /** * statistic method which returns the number handled registrations * @return the number handled registrations */ public long getNumberOfHandledRegistrations() { return handledRegistractions; } /** * statistic method which returns the number of handled reads * * @return the number of handled reads */ public long getNumberOfHandledReads() { return handledReads; } /** * statistic method which returns the number of handled writes * * @return the number of handled writes */ public long getNumberOfHandledWrites() { return handledWrites; } long getReceiveRateBytesPerSec() { long rate = 0; long elapsed = System.currentTimeMillis() - lastRequestReceiveRate; if (receivedBytes == 0) { rate = 0; } else if (elapsed == 0) { rate = Long.MAX_VALUE; } else { rate = ((receivedBytes * 1000) / elapsed); } lastRequestReceiveRate = System.currentTimeMillis(); receivedBytes = 0; return rate; } long getSendRateBytesPerSec() { long rate = 0; long elapsed = System.currentTimeMillis() - lastRequestSendRate; if (sentBytes == 0) { rate = 0; } else if (elapsed == 0) { rate = Long.MAX_VALUE; } else { rate = ((sentBytes * 1000) / elapsed); } lastRequestSendRate = System.currentTimeMillis(); sentBytes = 0; return rate; } long getCountIdleTimeout() { return countIdleTimeouts; } long getCountConnectionTimeout() { return countConnectionTimeouts; } public int getPreallocatedReadMemorySize() { return memoryManager.getCurrentSizePreallocatedBuffer(); } boolean getReceiveBufferPreallocationMode() { return memoryManager.isPreallocationMode(); } void setReceiveBufferPreallocationMode(boolean mode) { memoryManager.setPreallocationMode(mode); } void setReceiveBufferPreallocatedMinSize(Integer minSize) { memoryManager.setPreallocatedMinBufferSize(minSize); } Integer getReceiveBufferPreallocatedMinSize() { if (memoryManager.isPreallocationMode()) { return memoryManager.getPreallocatedMinBufferSize(); } else { return null; } } Integer getReceiveBufferPreallocatedSize() { if (memoryManager.isPreallocationMode()) { return memoryManager.getPreallocationBufferSize(); } else { return null; } } void setReceiveBufferPreallocatedSize(Integer size) { memoryManager.setPreallocationBufferSize(size); } boolean getReceiveBufferIsDirect() { return memoryManager.isDirect(); } void setReceiveBufferIsDirect(boolean isDirect) { memoryManager.setDirect(isDirect); } /** * reset the statistics (number of ...) */ public void resetStatistics() { statisticsStartTime = System.currentTimeMillis(); handledRegistractions = 0; handledReads = 0; handledWrites = 0; } @Override public String toString() { return "open channels " + getRegistered().size(); } /** * returns the statistics record start time (will be reset by the {@link IoSocketDispatcher#resetStatistics()} method) * @return */ protected long getStatisticsStartTime() { return statisticsStartTime; } @Override String printRegistered() { StringBuilder sb = new StringBuilder(); for (IoSocketHandler handler : getRegistered()) { sb.append(handler.toString() + " (key: " + printSelectionKey(handler) + ")\r\n"); } return sb.toString(); } String printSelectionKey(IoSocketHandler socketHandler) { return ConnectionUtils.printSelectionKey(socketHandler.getChannel().keyFor(selector)); } /** * {@inheritDoc} */ public void close() throws IOException { // wake up selector, so that isOpen loop can be terminated if (isOpen.getAndSet(false)&& (selector != null)) { selector.wakeup(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy