package com.google.code.hs4j.network.nio.impl; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.code.hs4j.network.config.Configuration; import com.google.code.hs4j.network.core.EventType; import com.google.code.hs4j.network.core.Session; /** * Selector manager * * @author dennis * */ public class SelectorManager { private final Reactor[] reactorSet; private final AtomicInteger sets = new AtomicInteger(0); private final NioController controller; private final int dividend; /** * Reactor count which are ready */ private int reactorReadyCount; public SelectorManager(int selectorPoolSize, NioController controller, Configuration conf) throws IOException { if (selectorPoolSize <= 0) { throw new IllegalArgumentException("selectorPoolSize<=0"); } log.info("Creating " + selectorPoolSize + " rectors..."); reactorSet = new Reactor[selectorPoolSize]; this.controller = controller; for (int i = 0; i < selectorPoolSize; i++) { reactorSet[i] = new Reactor(this, conf, i); } dividend = reactorSet.length - 1; } private volatile boolean started; public int getSelectorCount() { return reactorSet == null ? 0 : reactorSet.length; } public synchronized void start() { if (started) { return; } started = true; for (Reactor reactor : reactorSet) { reactor.start(); } } Reactor getReactorFromSession(Session session) { Reactor reactor = (Reactor) session.getAttribute(REACTOR_ATTRIBUTE); if (reactor == null) { reactor = nextReactor(); final Reactor oldReactor = (Reactor) session.setAttributeIfAbsent( REACTOR_ATTRIBUTE, reactor); if (oldReactor != null) { reactor = oldReactor; } } return reactor; } /** * Find reactor by index * * @param index * @return */ public Reactor getReactorByIndex(int index) { if (index < 0 || index > reactorSet.length - 1) { throw new ArrayIndexOutOfBoundsException(); } return reactorSet[index]; } public synchronized void stop() { if (!started) { return; } started = false; for (Reactor reactor : reactorSet) { reactor.interrupt(); } } public static final String REACTOR_ATTRIBUTE = System.currentTimeMillis() + "_Reactor_Attribute"; /** * Register channel * * @param channel * @param ops * @param attachment * @return */ public final Reactor registerChannel(SelectableChannel channel, int ops, Object attachment) { awaitReady(); int index = 0; // Accept event used index 0 reactor if (ops == SelectionKey.OP_ACCEPT || ops == SelectionKey.OP_CONNECT) { index = 0; } else { index = sets.incrementAndGet() % dividend + 1; } final Reactor reactor = reactorSet[index]; reactor.registerChannel(channel, ops, attachment); return reactor; } void awaitReady() { synchronized (this) { while (!started || reactorReadyCount != reactorSet.length) { try { this.wait(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt();// reset interrupt status } } } } /** * Get next reactor * * @return */ public final Reactor nextReactor() { if (dividend > 0) { return reactorSet[sets.incrementAndGet() % dividend + 1]; } else { return reactorSet[0]; } } /** * Register session * * @param session * @param event */ public final void registerSession(Session session, EventType event) { if (session.isClosed() && event != EventType.UNREGISTER) { return; } Reactor reactor = (Reactor) session.getAttribute(REACTOR_ATTRIBUTE); if (reactor == null) { reactor = nextReactor(); final Reactor oldReactor = (Reactor) session.setAttributeIfAbsent( REACTOR_ATTRIBUTE, reactor); if (oldReactor != null) { reactor = oldReactor; } } reactor.registerSession(session, event); } public NioController getController() { return controller; } /** * Notify all reactor have been ready */ synchronized void notifyReady() { reactorReadyCount++; if (reactorReadyCount == reactorSet.length) { controller.notifyReady(); notifyAll(); } } private static final Logger log = LoggerFactory .getLogger(SelectorManager.class); public final boolean isStarted() { return started; } }