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

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

There is a newer version: 10.0.6
Show newest version
/*
 * 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, State>>> selector = new EnhancedAtomicReference<>();
    private final boolean alwaysOpen;

    public ReceivePortGroup(final Collection> ports, final boolean alwaysOpen) {
        this.alwaysOpen = alwaysOpen;
        soloEffect.set(soloEffectDefault);
        final Map, State> newStates = new HashMap<>();
        for (final ReceivePort port : ImmutableList.copyOf(ports)) {
            newStates.put(port, new State(modeDefault, soloDefault));
        }
        states.set(ImmutableMap.copyOf(newStates)); // RO
    }

    public ReceivePortGroup(final Collection> ports) {
        this(ImmutableList.copyOf(ports), alwaysOpenDefault);
    }

    public ReceivePortGroup(final ReceivePort... 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, State>> curr = selector.get();
        Selector currSelector = curr.getFirst();
        Map, 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, State>> curr = selector.get();
        final Map, State> currStates =
            curr != null ? curr.getSecond() : states.get();
        final Map, 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> 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 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 port, final Map, 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 port, final Map, 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, State>>() {
                @Override
                public Map, State> apply(final Map, State> currStates) {
                    final Map, State> newStates = new HashMap<>(currStates);
                    for (final ReceivePort 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, State>>() {
                @Override
                public Map, State> apply(final Map, State> currStates) {
                    final Map, State> newStates = new HashMap<>(currStates);
                    for (final ReceivePort port : itemsCopy)
                        newStates.remove(port);
                    return ImmutableMap.copyOf(newStates); // RO
                }
            });
            changedCh.send(ping);
        }
    }

    private void removeClosed() {
        states.swap(new Function, State>, Map, State>>() {
            @Override
            public Map, State> apply(final Map, State> currStates) {
                final Map, State> newStates = new HashMap<>(currStates);
                for (final ReceivePort 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, 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, State>>() {
            @Override
            public Map, State> apply(final Map, State> currStates) {
                final Map, State> newStates = new HashMap<>(currStates);
                for (final ReceivePort 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, State>>() {
                @Override
                public Map, State> apply(final Map, 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);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy