/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2009 by Trifork * * 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 erjang.driver; import java.io.IOException; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; import erjang.driver.NIOChannelInfo.Interest; /** * */ public class NIOSelector extends Thread { static Logger log = Logger.getLogger("erjang.driver"); static final NIOSelector INSTANCE = new NIOSelector(); private Selector selector; public NIOSelector() { setDaemon(true); try { selector = Selector.open(); } catch (IOException e) { e.printStackTrace(); } start(); } static public SelectionKey interest(SelectableChannel ch) { return ch.keyFor(INSTANCE.selector); } static enum SetOrClear { SET, CLEAR; } static class SelectMsg { SetOrClear set; NIOChannelInfo.Interest interest; } ArrayBlockingQueue<SelectMsg> mbox = new ArrayBlockingQueue<SelectMsg>(2); @Override public void run() { try { run0(); } catch (Throwable e) { System.err.println("Exception in select loop"); System.err.println("Exception in select loop: " + e.getClass()); System.err.println("Exception in select loop: " + e); log.severe("unhandled exception in Select loop: " +e); log.severe("unhandled exception in Select loop: " +e.getMessage()); e.printStackTrace(System.err); log.log(Level.FINE, "details: ", e); } } public void run0() { // List<NIOChannelInfo> cancellations = new ArrayList<NIOChannelInfo>(); select_loop: while (true) { SelectMsg msg; msg_loop: while ((msg = mbox.poll()) != null) { // System.err.println("SELECT_MSG: "+msg.set+" >> "+msg.interest); if (msg.set == SetOrClear.SET) { process_add_interest_request(msg.interest); } else { process_clear_interest_request(msg.interest); try { selector.selectNow(); } catch (IOException e) { e.printStackTrace(); // xx // } } } int num; if (false) { log.fine("select loop"); for (SelectionKey key : selector.keys()) { if (key.isValid()) { SelectableChannel ch = key.channel(); int interst = key.interestOps(); if (log.isLoggable(Level.FINE)) log.fine(Integer.toBinaryString(interst) + ":" + ch); } } } try { num = selector.select(10000); } catch (CancelledKeyException e) { // ignore this, as per // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4729342 } catch (ClosedSelectorException e) { e.printStackTrace(); return; } catch (IOException e) { e.printStackTrace(); } // now, process the readyset Set<SelectionKey> ready = selector.selectedKeys(); for (SelectionKey key : ready) { // System.err.println("READY: "+key.channel()+":"+Integer.toBinaryString(key.readyOps())); NIOChannelInfo req = (NIOChannelInfo) key.attachment(); if (req == null) { log.severe("Internal error, SelectionKey's attachement is null"); } else { req.ready(key); } } } } /** * @param remove * @param cancellations */ private void process_clear_interest_request(Interest interest) { SelectableChannel ch = interest.ch; SelectionKey key = ch.keyFor(selector); NIOHandler handler = interest.handler; if (key == null || !key.isValid()) { // TODO: maybe this should be considered an error? if (handler != null && interest.releaseNotify) { handler.released(ch); } } else { // get channel info NIOChannelInfo info = (NIOChannelInfo) key.attachment(); if (info == null) { // error! throw new InternalError(); } // tell info that we're note interested in this info.clear_interest(interest); // does that add up to, that we are not interested at all? if (info.updateInterestOpsFor(key) == 0) { // cancel the key key.cancel(); } } } /** * @param remove * @param active2 */ private void process_add_interest_request(Interest interest) { SelectableChannel ch = interest.ch; SelectionKey key = ch.keyFor(selector); NIOChannelInfo info = null; if (key != null && !key.isValid()) { try { selector.selectNow(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } key = null; } if (key == null || !key.isValid()) { try { key = ch.register(selector, interest.ops, info = new NIOChannelInfo(interest)); } catch (ClosedChannelException e) { interest.handler.exception(ch, e); } } else { info = (NIOChannelInfo) key.attachment(); info.add(interest); info.updateInterestOpsFor(key); } } static public void setInterest(SelectableChannel ch, int op, NIOHandler handler) { INSTANCE._addInterest(ch, op, handler); } void _addInterest(SelectableChannel ch, int op, NIOHandler handler) { SelectMsg msg = new SelectMsg(); msg.set = SetOrClear.SET; msg.interest = new NIOChannelInfo.Interest(ch, handler, op, false); try { // System.err.println("SELECT_MSG: "+msg.set+" ++>> "+msg.interest); mbox.put(msg); } catch (InterruptedException e) { e.printStackTrace(); } selector.wakeup(); } static public void clearInterest(SelectableChannel ch, int op, boolean releaseNotify, NIOHandler handler) { INSTANCE._removeInterest(ch, op, releaseNotify, handler); } void _removeInterest(SelectableChannel ch, int op, boolean releaseNotify, NIOHandler handler) { SelectMsg msg = new SelectMsg(); msg.set = SetOrClear.CLEAR; msg.interest = new NIOChannelInfo.Interest(ch, handler, op, releaseNotify); try { // System.err.println("SELECT_MSG: "+msg.set+" ++>> "+msg.interest); mbox.put(msg); } catch (InterruptedException e) { e.printStackTrace(); } selector.wakeup(); } }