/**
* 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.nio.channels.CancelledKeyException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.HashMap;
import java.util.Map;
/**
* This class holds information per selectable channel in an IOSelector.
* Instances of this class are stored inside the SelectionKey.attachment.
*
*
*
*/
class NIOChannelInfo {
static class Interest {
SelectableChannel ch;
NIOHandler handler;
int ops;
boolean releaseNotify;
@Override
public String toString() {
return "{ch="+ch+"; ops="+Integer.toBinaryString(ops)+"; release="+releaseNotify+"}";
}
/**
* @param op
* @param timeout2
*/
public Interest(SelectableChannel ch, NIOHandler handler, int ops,
boolean releaseNotify) {
this.ch = ch;
this.handler = handler;
this.ops = ops;
this.releaseNotify = releaseNotify;
}
/**
* @param old
* @return
*/
public Interest combine(Interest old) {
return new Interest(ch, handler, ops|old.ops, releaseNotify||old.releaseNotify);
}
}
public static final int ALL_OPS = SelectionKey.OP_ACCEPT
| SelectionKey.OP_CONNECT | SelectionKey.OP_READ
| SelectionKey.OP_WRITE;
// TODO: use an optimized data structure for this
Map<NIOHandler, Interest> interest = new HashMap<NIOHandler, Interest>();
/**
* @param req
*/
public NIOChannelInfo(Interest interest) {
add(interest);
}
/**
* @param req
*/
public void add(Interest ops) {
Interest old = interest.get(ops.handler);
if (old == null) {
interest.put(ops.handler, ops);
} else {
interest.put(ops.handler, ops.combine(old));
}
}
/**
* Called in top of main select loop, when a client has requested that
* select interest be removed.
*
* @param ioRequest
*/
public void clear_interest(Interest req) {
NIOHandler handler = req.handler;
boolean releaseNotify = req.releaseNotify;
SelectableChannel ch = req.ch;
Interest old = interest.get(handler);
if (old == null) {
if (releaseNotify && handler != null) {
handler.released(ch);
}
} else {
// normal case, there has actually been listening
// going on for this handler
old.releaseNotify |= releaseNotify;
old.ops &= ~req.ops;
// is he still interested in some ops?
if (old.ops == 0 && !old.releaseNotify) {
interest.remove(handler);
}
}
}
/**
* @param key
*/
public int updateInterestOpsFor(SelectionKey key) {
int ops = computeOps();
key.interestOps(ops);
return ops;
}
/**
* @return
*/
private int computeOps() {
int ops = 0;
for (Interest i : interest.values()) {
ops |= i.ops;
}
return ops;
}
/**
* Called in main select loop, after select when we know that
* there is something ready with this channel...
*
* @param key
*/
public void ready(SelectionKey key) {
final SelectableChannel ch = key.channel();
int readyOps;
try {
readyOps = key.readyOps();
} catch (CancelledKeyException e) {
// TODO: releaseNotify ?
return; // just ignore this condition
}
Interest[] ents = interest.values().toArray(
new Interest[interest.size()]);
// walk thru each handler registered for this channel...
for (Interest want : ents) {
int gotOps = want.ops & readyOps;
// did he get what he wanted?
if (gotOps != 0) {
NIOHandler handler = want.handler;
if (handler != null) {
handler.ready(ch, gotOps);
}
// remove interest in the operations we got
want.ops &= ~gotOps;
if (want.ops == 0 && !want.releaseNotify) {
interest.remove(handler);
}
}
}
try {
key.interestOps(computeOps());
if (interest.isEmpty()) {
key.cancel();
}
} catch (CancelledKeyException e) {
// ok
}
}
/**
*
*/
public void cancelled() {
// are we done, so we can close?
for (Interest i : interest.values()) {
if (i.releaseNotify && i.handler != null) {
i.handler.released(i.ch);
}
}
// is that worth it?
interest.clear();
}
}