/*
* Minha.pt: middleware testing platform.
* Copyright (c) 2011-2014, Universidade do Minho.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package pt.minha.models.local.nio;
import java.io.IOException;
import java.nio.channels.CancelledKeyException;
import java.util.HashSet;
import java.util.Set;
import pt.minha.kernel.simulation.Event;
import pt.minha.models.fake.java.nio.channels.SelectableChannel;
import pt.minha.models.fake.java.nio.channels.SelectionKey;
import pt.minha.models.fake.java.nio.channels.Selector;
import pt.minha.models.fake.java.nio.channels.spi.AbstractSelectableChannel;
import pt.minha.models.fake.java.nio.channels.spi.AbstractSelector;
import pt.minha.models.fake.java.nio.channels.spi.SelectorProvider;
import pt.minha.models.global.io.BlockingHelper;
import pt.minha.models.local.lang.SimulationThread;
public class SelectorImpl extends AbstractSelector {
private SelectorProvider provider;
private Set<SelectionKey> keys, selectedKeys, canceled;
private Set<SelectionKeyImpl> ready;
private BlockingHelper selectors = new BlockingHelper() {
@Override
public boolean isReady() {
return !ready.isEmpty();
}
};
private boolean wakeup;
protected SelectorImpl(SelectorProvider provider) {
this.provider = provider;
keys = new HashSet<SelectionKey>();
selectedKeys = new HashSet<SelectionKey>();
ready = new HashSet<SelectionKeyImpl>();
canceled = new HashSet<SelectionKey>();
}
@Override
public SelectorProvider provider() {
return provider;
}
@Override
public Set<SelectionKey> keys() {
return keys;
}
@Override
public Set<SelectionKey> selectedKeys() {
return selectedKeys;
}
@Override
public int selectNow() throws IOException {
return select(-1);
}
@Override
public int select(long timeout) throws IOException {
try {
SimulationThread.stopTime(0);
Set<SelectionKeyImpl> previous = ready;
ready = new HashSet<SelectorImpl.SelectionKeyImpl>();
for(SelectionKeyImpl key: previous)
key.retest();
SimulationThread current = SimulationThread.currentSimulationThread();
if (current.getInterruptedStatus(false))
wakeup = true;
long deadline = current.getTimeline().getTime() + timeout*1000000;
while((timeout==0 || deadline>current.getTimeline().getTime()) && !wakeup && !selectors.isReady()) {
selectors.queue(SimulationThread.currentSimulationThread().getWakeup());
if (timeout>0)
SimulationThread.currentSimulationThread().getWakeup().schedule(deadline-current.getTimeline().getTime());
if (SimulationThread.currentSimulationThread().pause(true, false))
wakeup = true;
selectors.cancel(SimulationThread.currentSimulationThread().getWakeup());
}
wakeup = false;
ready.removeAll(canceled);
keys.removeAll(canceled);
selectedKeys.removeAll(canceled);
int oldsize = selectedKeys.size();
selectedKeys.addAll(ready);
return selectedKeys.size()-oldsize;
} finally {
SimulationThread.startTime(0);
}
}
@Override
public int select() throws IOException {
return select(0);
}
@Override
public Selector wakeup() {
wakeup = true;
selectors.wakeup();
return this;
}
public SelectionKey register(AbstractSelectableChannel cb, int operation, Object attachment) {
SelectionKeyImpl key = new SelectionKeyImpl(cb);
key.interestOps(operation);
key.attach(attachment);
keys.add(key);
return key;
}
private void selected(SelectionKeyImpl key) {
ready.add(key);
selectors.wakeup();
}
private void canceled(SelectionKeyImpl key) {
canceled.add(key);
}
class SelectionKeyImpl extends SelectionKey {
private Event wakeup;
private int interest, ready;
private Object attachment;
private AbstractSelectableChannel channel;
private boolean cancelled;
public SelectionKeyImpl(AbstractSelectableChannel channel) {
wakeup = new Event(SimulationThread.currentSimulationThread().getTimeline()) {
@Override
public void run() {
retest();
}
};
this.channel = channel;
}
void retest() {
ready=0;
test(OP_READ);
test(OP_WRITE);
test(OP_CONNECT);
test(OP_ACCEPT);
if (ready!=0)
selected(this);
}
@Override
public Selector selector() {
return SelectorImpl.this;
}
@Override
public int interestOps() {
checkCancel();
return interest;
}
private void test(int op) {
if ((interest & op)==0)
return;
if (channel.helperFor(op).isReady())
ready |= op;
channel.helperFor(op).queue(wakeup);
}
private void update(int op, int ops) {
if ((interest & op)==0 && (ops & op)!=0) {
if (channel.helperFor(op).isReady())
ready |= op;
channel.helperFor(op).queue(wakeup);
}
if ((interest & op)!=0 && (ops &op)==0)
channel.helperFor(op).cancel(wakeup);
}
@Override
public SelectionKey interestOps(int ops) {
checkCancel();
update(OP_READ, ops);
update(OP_WRITE, ops);
update(OP_CONNECT, ops);
update(OP_ACCEPT, ops);
interest = ops;
if (ready!=0)
selected(SelectionKeyImpl.this);
return this;
}
@Override
public int readyOps() {
checkCancel();
return ready;
}
@Override
public Object attach(Object attachment) {
Object previous = this.attachment;
this.attachment = attachment;
return previous;
}
@Override
public Object attachment() {
return attachment;
}
@Override
public SelectableChannel channel() {
return channel;
}
@Override
public void cancel() {
interestOps(0);
cancelled = true;
canceled(SelectionKeyImpl.this);
}
private void checkCancel() {
if (cancelled)
throw new CancelledKeyException();
}
@Override
public boolean isValid() {
return !cancelled;
}
@Override
public boolean isReadable() {
return (ready&OP_READ)!=0;
}
@Override
public boolean isWritable() {
return (ready&OP_WRITE)!=0;
}
@Override
public boolean isAcceptable() {
return (ready&OP_ACCEPT)!=0;
}
@Override
public boolean isConnectable() {
return (ready&OP_CONNECT)!=0;
}
public String toString() {
return "SK("+interest+", "+ready+", "+channel+")";
}
}
@Override
public void close() throws IOException {
}
}