package com.revolsys.parallel.channel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.revolsys.parallel.ThreadInterruptedException; import com.revolsys.parallel.ThreadUtil; public class MultiInputSelector { private int enabledChannels = 0; private int guardEnabledChannels = 0; private long maxWait; private final Object monitor = new Object(); private boolean scheduled; void closeChannel() { synchronized (this.monitor) { this.enabledChannels--; if (this.enabledChannels <= 0) { this.monitor.notifyAll(); } } } private int disableChannels(final List<? extends SelectableInput> channels) { int closedCount = 0; int selected = -1; for (int i = channels.size() - 1; i >= 0; i--) { final SelectableInput channel = channels.get(i); if (channel.disable()) { selected = i; } else if (channel.isClosed()) { closedCount++; } } if (closedCount == channels.size()) { throw new ClosedException(); } else { return selected; } } private int disableChannels(final List<? extends SelectableInput> channels, final List<Boolean> guard) { int closedCount = 0; int selected = -1; for (int i = channels.size() - 1; i >= 0; i--) { final SelectableInput channel = channels.get(i); if (guard.get(i) && channel.disable()) { selected = i; } else if (channel == null || channel.isClosed()) { closedCount++; } } if (closedCount == channels.size()) { throw new ClosedException(); } else { return selected; } } private boolean enableChannels(final List<? extends SelectableInput> channels) { this.enabledChannels = 0; this.scheduled = false; this.maxWait = Long.MAX_VALUE; int closedCount = 0; for (final SelectableInput channel : channels) { if (!channel.isClosed()) { if (channel.enable(this)) { this.enabledChannels++; return true; } else if (channel instanceof Timer) { final Timer timer = (Timer)channel; this.maxWait = Math.min(this.maxWait, timer.getWaitTime()); } } else { closedCount++; } } return closedCount == channels.size(); } private boolean enableChannels(final List<? extends SelectableInput> channels, final List<Boolean> guard) { this.enabledChannels = 0; this.scheduled = false; this.maxWait = Long.MAX_VALUE; int closedCount = 0; int activeChannelCount = 0; for (int i = 0; i < channels.size(); i++) { final SelectableInput channel = channels.get(i); if (guard.get(i)) { activeChannelCount++; if (!channel.isClosed()) { if (channel.enable(this)) { this.enabledChannels++; return true; } else if (channel instanceof Timer) { final Timer timer = (Timer)channel; this.maxWait = Math.min(this.maxWait, timer.getWaitTime()); } } else { closedCount++; } } } this.guardEnabledChannels = activeChannelCount - closedCount; return closedCount == activeChannelCount; } void schedule() { synchronized (this.monitor) { this.scheduled = true; this.monitor.notifyAll(); } } public synchronized int select(final List<? extends SelectableInput> channels) { return select(Long.MAX_VALUE, channels); } public synchronized int select(final List<? extends SelectableInput> channels, final boolean skip) { if (skip) { enableChannels(channels); return disableChannels(channels); } else { return select(channels); } } public synchronized int select(final List<? extends SelectableInput> channels, final List<Boolean> guard) { return select(channels, guard, Long.MAX_VALUE); } public synchronized int select(final List<? extends SelectableInput> channels, final List<Boolean> guard, final boolean skip) { if (skip) { enableChannels(channels, guard); return disableChannels(channels, guard); } else { return select(channels, guard); } } public synchronized int select(final List<? extends SelectableInput> channels, final List<Boolean> guard, final long msecs) { return select(channels, guard, msecs, 0); } public synchronized int select(final List<? extends SelectableInput> channels, final List<Boolean> guard, final long msecs, final int nsecs) { if (!enableChannels(channels, guard) && this.guardEnabledChannels > 0) { synchronized (this.monitor) { if (!this.scheduled) { try { ThreadUtil.pause(this.monitor, Math.min(msecs, this.maxWait), nsecs); } catch (final ThreadInterruptedException e) { throw new ClosedException(e); } } } } return disableChannels(channels, guard); } public synchronized int select(final long msecs, final int nsecs, final List<? extends SelectableInput> channels) { if (!enableChannels(channels)) { if (msecs + nsecs >= 0) { synchronized (this.monitor) { try { if (!this.scheduled) { ThreadUtil.pause(this.monitor, Math.min(msecs, this.maxWait), nsecs); } } catch (final ThreadInterruptedException e) { throw new ClosedException(e); } } } } return disableChannels(channels); } public synchronized int select(final long msecs, final int nsecs, final SelectableInput... channels) { return select(msecs, nsecs, Arrays.asList(channels)); } public synchronized int select(final long msecs, final List<? extends SelectableInput> channels) { return select(msecs, 0, channels); } public synchronized int select(final long msecs, final SelectableInput... channels) { return select(msecs, 0, channels); } public synchronized int select(final SelectableInput... channels) { return select(Long.MAX_VALUE, channels); } public synchronized int select(final SelectableInput[] channels, final boolean skip) { return select(Arrays.asList(channels), skip); } public synchronized int select(final SelectableInput[] channels, final boolean[] guard) { return select(channels, guard, Long.MAX_VALUE); } public synchronized int select(final SelectableInput[] channels, final boolean[] guard, final boolean skip) { final List<Boolean> guardList = new ArrayList<>(); for (final boolean enabled : guard) { guardList.add(enabled); } return select(Arrays.asList(channels), guardList, skip); } public synchronized int select(final SelectableInput[] channels, final boolean[] guard, final long msecs) { return select(channels, guard, msecs, 0); } public synchronized int select(final SelectableInput[] channels, final boolean[] guard, final long msecs, final int nsecs) { final List<Boolean> guardList = new ArrayList<>(); for (final boolean enabled : guard) { guardList.add(enabled); } return select(Arrays.asList(channels), guardList, msecs, nsecs); } public synchronized <T extends SelectableInput> T selectChannelInput(final List<T> channels) { final int index = select(Long.MAX_VALUE, channels); if (index == -1) { return null; } else { return channels.get(index); } } }