Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
co.paralleluniverse.strands.channels.ReceivePortGroup Maven / Gradle / Ivy
Go to download
The core library for Fibers on Java, compatible with Java 11-16. Forked from puniverse/quasar
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.strands.channels;
import co.paralleluniverse.common.util.Pair;
import co.paralleluniverse.concurrent.util.EnhancedAtomicReference;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.Timeout;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
*
* @author pron
* @author circlespainter
*/
public class ReceivePortGroup implements Mix {
private static final Object ping = new Object();
private static final Predicate soloP =
new Predicate() {
@Override
public boolean apply(Mix.State s) {
return (s != null && s.solo);
}
};
private final static Mode modeDefault = Mode.NORMAL;
private final static boolean soloDefault = false;
private final static SoloEffect soloEffectDefault = SoloEffect.PAUSE_OTHERS;
private final static boolean alwaysOpenDefault = false;
private final static Channel changedCh = Channels.newChannel(1, Channels.OverflowPolicy.DISPLACE, false, true);
private final EnhancedAtomicReference soloEffect = new EnhancedAtomicReference<>();
private final EnhancedAtomicReference, State>> states = new EnhancedAtomicReference<>();
private final EnhancedAtomicReference, Map extends ReceivePort extends M>, State>>> selector = new EnhancedAtomicReference<>();
private final boolean alwaysOpen;
public ReceivePortGroup(final Collection extends ReceivePort extends M>> ports, final boolean alwaysOpen) {
this.alwaysOpen = alwaysOpen;
soloEffect.set(soloEffectDefault);
final Map, State> newStates = new HashMap<>();
for (final ReceivePort extends M> port : ImmutableList.copyOf(ports)) {
newStates.put(port, new State(modeDefault, soloDefault));
}
states.set(ImmutableMap.copyOf(newStates)); // RO
}
public ReceivePortGroup(final Collection extends ReceivePort extends M>> ports) {
this(ImmutableList.copyOf(ports), alwaysOpenDefault);
}
public ReceivePortGroup(final ReceivePort extends M>... ports) {
this(ImmutableList.copyOf(ports), alwaysOpenDefault);
}
public ReceivePortGroup(final boolean alwaysOpen) {
this(ImmutableList.>of(), alwaysOpen);
}
public ReceivePortGroup() {
this(ImmutableList.>of(), alwaysOpenDefault);
}
@Override
public M tryReceive() {
try {
return receive(0, TimeUnit.NANOSECONDS);
} catch (Throwable t) {
// This shouldn't happen
throw new AssertionError(t);
}
}
@Override
public M receive() throws SuspendExecution, InterruptedException {
return receive(-1, null);
}
@Override
public M receive(final Timeout timeout) throws SuspendExecution, InterruptedException {
return receive(timeout.nanosLeft(), TimeUnit.NANOSECONDS);
}
@Override
public M receive(final long timeout, final TimeUnit unit) throws InterruptedException, SuspendExecution {
if (isClosed())
return null;
setupSelector();
// Init time bookkeeping in case we have to wait when performing a timed receive
final long start = timeout > 0 ? System.nanoTime() : 0;
long left = unit != null ? unit.toNanos(timeout) : 0;
final long deadline = start + left;
long now;
// Mutable in case of closed ports
Pair, Map extends ReceivePort extends M>, State>> curr = selector.get();
Selector currSelector = curr.getFirst();
Map extends ReceivePort extends M>, State> currStates = curr.getSecond();
SelectAction sa;
M ret = null;
while (ret == null) {
if (unit == null)
// Untimed select
sa = currSelector.select();
else if (left > 0) {
// Timed select
sa = currSelector.select(left, TimeUnit.NANOSECONDS);
}
else
// trySelect
sa = currSelector.trySelect();
if (sa != null) {
if (sa.port() != null) {
// One port has been selected
if (sa.message() == null || changedCh.equals(sa.port())) {
// Configuration update or port closed, recalculate state
if (isClosed())
return null;
setupSelector();
curr = selector.get();
currSelector = curr.getFirst();
currStates = curr.getSecond();
} else if (!isMuted(sa.port(), currStates))
// It's not muted, it's not the special channel and it's not a null message => return it
return sa.message();
else {
// If it's muted throw away the value and retry
now = System.nanoTime();
final long prevLeft = left;
left = deadline - now;
if (prevLeft > 0 && left <= 0)
// It was a timed select but it has expired, stop trying
return null;
else
// Reset selector and retry
currSelector.reset();
}
} else
throw new AssertionError(); // This should never happen
} else
// Either timeout for timed select, or none ready during trySelect
return null;
}
throw new AssertionError(); // This should never happen
}
@Override
public void close() {
throw new UnsupportedOperationException();
}
@Override
public boolean isClosed() {
removeClosed();
return !alwaysOpen && states.get().isEmpty();
}
private void setupSelector() {
// Freeze for this call
final Pair, Map extends ReceivePort extends M>, State>> curr = selector.get();
final Map extends ReceivePort extends M>, State> currStates =
curr != null ? curr.getSecond() : states.get();
final Map extends ReceivePort extends M>, State> newStates = states.get();
if (currStates == newStates && curr != null && curr.getFirst() != null) {
// State has not changed, just reset the selector
curr.getFirst().reset();
} else {
// State has chnaged, update the selector
final Set extends ReceivePort extends M>> newPorts = newStates.keySet();
// Build a new selector containing receive actions for all non-paused ports
final List> mutedActions = new ArrayList<>(newPorts.size());
final List> enabledActions = new ArrayList<>(newPorts.size());
for (final ReceivePort extends M> port : newPorts) {
if (!isPaused(port, newStates)) {
if (isMuted(port, newStates))
mutedActions.add(Selector.receive(port));
else
enabledActions.add(Selector.receive(port));
}
}
final List> actions = new ArrayList<>(newPorts.size());
actions.add(Selector.receive(changedCh)); // Always receive change pings
actions.addAll(mutedActions);
actions.addAll(enabledActions);
// Priority to change signal, then to muted so they get elimintated first, then normal ones
selector.set(new Pair(new Selector<>(true, actions), newStates));
}
}
@SuppressWarnings("element-type-mismatch")
private boolean isMuted(final Port extends M> port, final Map extends ReceivePort extends M>, State> s) {
return
!s.get(port).solo
&&
(s.get(port).mode.equals(Mix.Mode.MUTE)
|| (soloEffect.get().equals(Mix.SoloEffect.MUTE_OTHERS)
&& Iterables.any(s.values(), soloP)));
}
private boolean isPaused(ReceivePort extends M> port, final Map extends ReceivePort extends M>, State> s) {
return
!s.get(port).solo
&&
(s.get(port).mode.equals(Mix.Mode.PAUSE)
|| (soloEffect.get().equals(Mix.SoloEffect.PAUSE_OTHERS)
&& Iterables.any(s.values(), soloP)));
}
@Override
public > void add(final T... items) throws SuspendExecution, InterruptedException {
if (items != null && items.length > 0) {
final List itemsCopy = ImmutableList.copyOf(items); // Freeze for this call
states.swap(new Function, State>, Map extends ReceivePort extends M>, State>>() {
@Override
public Map extends ReceivePort extends M>, State> apply(final Map extends ReceivePort extends M>, State> currStates) {
final Map, State> newStates = new HashMap<>(currStates);
for (final ReceivePort extends M> port : itemsCopy)
newStates.put(port, new State(Mode.NORMAL, false));
return ImmutableMap.copyOf(newStates); // RO
}
});
changedCh.send(ping);
}
}
@Override
public > void remove(final T... items) throws SuspendExecution, InterruptedException {
if (items == null || items.length == 0)
states.set(ImmutableMap.of()); // Reset
else {
final List itemsCopy = ImmutableList.copyOf(items); // Freeze for this call
states.swap(new Function, State>, Map extends ReceivePort extends M>, State>>() {
@Override
public Map extends ReceivePort extends M>, State> apply(final Map extends ReceivePort extends M>, State> currStates) {
final Map, State> newStates = new HashMap<>(currStates);
for (final ReceivePort extends M> port : itemsCopy)
newStates.remove(port);
return ImmutableMap.copyOf(newStates); // RO
}
});
changedCh.send(ping);
}
}
private void removeClosed() {
states.swap(new Function, State>, Map extends ReceivePort extends M>, State>>() {
@Override
public Map extends ReceivePort extends M>, State> apply(final Map extends ReceivePort extends M>, State> currStates) {
final Map, State> newStates = new HashMap<>(currStates);
for (final ReceivePort extends M> port : currStates.keySet()) {
if (port.isClosed())
newStates.remove(port);
}
return ImmutableMap.copyOf(newStates); // RO
}
});
}
@Override
public > Map getState(final T... items) {
if (items == null || items.length == 0)
return (Map) ImmutableMap.copyOf(states.get());
List itemsCopy = ImmutableList.copyOf(items); // Freeze for this call
final Map extends ReceivePort extends M>, State> currStates = states.get();
final Map ret = new HashMap<>(itemsCopy.size());
for (final T p : itemsCopy)
ret.put(p, currStates.get(p));
return ret;
}
@Override
public > void setState(final State state, final T... items) throws SuspendExecution, InterruptedException {
final ImmutableList itemsCopy = ImmutableList.copyOf(items);
states.swap(new Function, State>, Map extends ReceivePort extends M>, State>>() {
@Override
public Map extends ReceivePort extends M>, State> apply(final Map extends ReceivePort extends M>, State> currStates) {
final Map, State> newStates = new HashMap<>(currStates);
for (final ReceivePort extends M> port : (items != null && items.length > 0) ? itemsCopy : ImmutableList.copyOf(currStates.keySet()))
if (newStates.containsKey(port))
newStates.put (
port,
new State (
state.mode != null ? state.mode : currStates.get(port).mode,
state.solo != null ? state.solo : currStates.get(port).solo
)
);
return ImmutableMap.copyOf(newStates); // RO
}
});
changedCh.send(ping);
}
@Override
public > void setState(final Map newStates) throws SuspendExecution, InterruptedException {
if (newStates != null) {
final Map newStatesSnapshot = ImmutableMap.copyOf(newStates); // Freeze
states.swap(new Function, State>, Map extends ReceivePort extends M>, State>>() {
@Override
public Map extends ReceivePort extends M>, State> apply(final Map extends ReceivePort extends M>, State> currStates) {
final Map, State> updatedStates = new HashMap<>(currStates);
for (final Map.Entry e : newStatesSnapshot.entrySet()) {
final T p = e.getKey();
final State newS = e.getValue();
if (newStatesSnapshot.containsKey(e.getKey()))
updatedStates.put (
e.getKey(),
newS != null ?
new State (
newS.mode != null ? newS.mode : currStates.get(p).mode,
newS.solo != null ? newS.solo : currStates.get(p).solo
) :
new State(modeDefault, soloDefault)
);
}
return ImmutableMap.copyOf(updatedStates); // RO
}
});
changedCh.send(ping);
}
}
@Override
public SoloEffect getSoloEffect() {
return soloEffect.get();
}
@Override
public void setSoloEffect(final SoloEffect effect) throws SuspendExecution, InterruptedException {
soloEffect.set(effect);
changedCh.send(ping);
}
}