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

org.vesalainen.nio.channels.MultiProviderSelector Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 tkv
 *
 * 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 3 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, see .
 */
package org.vesalainen.nio.channels;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import org.vesalainen.util.concurrent.ConcurrentArraySet;
import org.vesalainen.util.logging.JavaLogging;

/**
 * a bridge between selectors from different providers.
 * 
 * 

Note! You cannot use SelectableChannel register method. Instead use * MultiProviderSelector.register. * @author tkv */ public class MultiProviderSelector extends AbstractSelector { private Map map = new HashMap<>(); private Set keys = new ConcurrentArraySet<>(); private final Set unmodifiableKeys = Collections.unmodifiableSet(keys); private Set selectedKeys = new ConcurrentArraySet<>(); private final Map keyMap = new HashMap<>(); private final ReentrantLock lock = new ReentrantLock(); private final JavaLogging log; private final ExecutorService executor = Executors.newCachedThreadPool(); private final List callables = new ArrayList<>(); public MultiProviderSelector() { super(MultiSelectorProvider.provider()); log = new JavaLogging(this.getClass()); } @Override public Selector wakeup() { log.fine("wakeup(%s", this); for (Selector selector : map.values()) { selector.wakeup(); } return this; } @Override protected void implCloseSelector() throws IOException { log.fine("close(%s", this); for (Selector selector : map.values()) { selector.close(); } map = null; keys = null; selectedKeys = null; } @Override protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) { log.fine("register(%s)", this); lock.lock(); try { SelectionKey sk = null; SelectorProvider provider = ch.provider(); Selector selector = map.get(provider); if (selector == null) { selector = provider.openSelector(); map.put(provider, selector); sk = ch.register(selector, ops); callables.add(new SelectorWrapper(selector)); } else { sk = ch.register(selector, ops); } MultiProviderSelectionKey mpsk = new MultiProviderSelectionKey(this, sk); mpsk.attach(att); keys.add(mpsk); keyMap.put(sk, mpsk); return mpsk; } catch (IOException ex) { throw new IllegalArgumentException(ex); } finally { lock.unlock(); } } @Override public Set keys() { return unmodifiableKeys; } @Override public Set selectedKeys() { return selectedKeys; } @Override public int selectNow() throws IOException { int count = 0; lock.lock(); try { handleCancelled(); for (Selector selector : map.values()) { selector.selectNow(); Set sks = selector.selectedKeys(); for (SelectionKey sk : sks) { MultiProviderSelectionKey mpsk = keyMap.get(sk); if (mpsk != null) { if (!selectedKeys.contains(mpsk)) { selectedKeys.add(mpsk); count++; } } else { log.warning("%s: MultiProviderSelectionKey=null", sk); } } sks.clear(); } log.finest("selectNow return keyCount= %d", count); return count; } finally { lock.unlock(); } } @Override public int select(long timeout) throws IOException { log.fine("select: enter select(%d)", timeout); int sn = selectNow(); if (sn > 0) { log.fine("select: satisfied with selectNow() returns %d keys", sn); return sn; } int keyCount = 0; try { lock.lock(); try { handleCancelled(); Iterator iterator = callables.iterator(); while (iterator.hasNext()) { SelectorWrapper sw = iterator.next(); if (sw.selector.keys().isEmpty()) { log.fine("removing selector %s because it has no keys anymore", sw.selector); map.remove(sw.selector.provider()); iterator.remove(); } } } finally { lock.unlock(); } log.fine("selector: invoking %d selectors", callables.size()); Selector selector = executor.invokeAny(callables, timeout, TimeUnit.MILLISECONDS); log.fine("selector: %s success with %d keys", selector, selector.selectedKeys().size()); Set sks = selector.selectedKeys(); for (SelectionKey sk : sks) { MultiProviderSelectionKey mpsk = keyMap.get(sk); if (mpsk != null) { if (!selectedKeys.contains(mpsk)) { selectedKeys.add(mpsk); keyCount++; } } else { log.warning("%s: MultiProviderSelectionKey=null", sk); } } sks.clear(); return keyCount; } catch (InterruptedException ex) { throw new IllegalArgumentException(ex); } catch (ExecutionException | TimeoutException ex) { return 0; } } private void handleCancelled() { lock.lock(); try { Set cancelledKeys = cancelledKeys(); synchronized(cancelledKeys) { Iterator iterator = cancelledKeys.iterator(); while (iterator.hasNext()) { MultiProviderSelectionKey sk = (MultiProviderSelectionKey) iterator.next(); log.fine("select: cancel(%s)", sk); deregister(sk); if (!keys.remove(sk)) { log.warning("%s not in %s", sk, keys); } if (keyMap.remove(sk.getRealSelectionKey()) == null) { log.warning("%s not in %s", sk.getRealSelectionKey(), keyMap); } sk.doCancel(); iterator.remove(); } } } finally { lock.unlock(); } } @Override public int select() throws IOException { return select(-1); } private class SelectorWrapper extends JavaLogging implements Callable { Selector selector; public SelectorWrapper(Selector selector) { this.selector = selector; setLogger(this.getClass()); } @Override public Selector call() throws Exception { try { log.fine("selector: start %s", selector); selector.select(); log.fine("selector: end %s", selector); return selector; } catch (Exception ex) { log.log(Level.INFO, ex, "%s", ex.getMessage()); throw ex; } } @Override public String toString() { return "SelectorWrapper{" + "selector=" + selector + '}'; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy