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

groovyx.gpars.dataflow.stream.DataflowStreamReadAdapter Maven / Gradle / Ivy

// GPars - Groovy Parallel Systems
//
// Copyright © 2008-2012  The original author or authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package groovyx.gpars.dataflow.stream;

import groovy.lang.Closure;
import groovyx.gpars.actor.impl.MessageStream;
import groovyx.gpars.dataflow.Dataflow;
import groovyx.gpars.dataflow.DataflowChannelListener;
import groovyx.gpars.dataflow.DataflowQueue;
import groovyx.gpars.dataflow.DataflowReadChannel;
import groovyx.gpars.dataflow.DataflowVariable;
import groovyx.gpars.dataflow.DataflowWriteChannel;
import groovyx.gpars.dataflow.Promise;
import groovyx.gpars.dataflow.SyncDataflowVariable;
import groovyx.gpars.dataflow.expression.DataflowExpression;
import groovyx.gpars.dataflow.impl.DataflowChannelEventListenerManager;
import groovyx.gpars.dataflow.impl.DataflowChannelEventOrchestrator;
import groovyx.gpars.dataflow.impl.ThenMessagingRunnable;
import groovyx.gpars.dataflow.operator.BinaryChoiceClosure;
import groovyx.gpars.dataflow.operator.ChainWithClosure;
import groovyx.gpars.dataflow.operator.ChoiceClosure;
import groovyx.gpars.dataflow.operator.CopyChannelsClosure;
import groovyx.gpars.dataflow.operator.FilterClosure;
import groovyx.gpars.dataflow.operator.SeparationClosure;
import groovyx.gpars.group.DefaultPGroup;
import groovyx.gpars.group.PGroup;
import groovyx.gpars.scheduler.Pool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static java.util.Arrays.asList;

/**
 * Adapts a DataflowStream to accommodate for the DataflowReadChannel interface.
 * To minimize the overhead and stay in-line with the DataflowStream semantics, the DataflowStreamReadAdapter class is not thread-safe
 * and should only be used from within a single thread.
 * If multiple threads need to read from a DataflowStream, they should each create their own wrapping DataflowStreamReadAdapter.
 *
 * @param  The type of messages to pass through the stream
 * @author Vaclav Pech
 */
@SuppressWarnings({"unchecked"})
public class DataflowStreamReadAdapter implements DataflowReadChannel {

    private StreamCore head;
    private StreamCore asyncHead;

    /**
     * Creates a new adapter
     *
     * @param stream The stream to wrap
     */
    public DataflowStreamReadAdapter(final StreamCore stream) {
        this.head = stream;
        this.asyncHead = head;

        this.head.addUpdateListener(new DataflowChannelListener() {
            @Override
            public void onMessage(final T message) {
                fireOnMessage(message);
            }
        });
    }

    public Iterator iterator() {
        return new FListIterator(head);
    }

    @SuppressWarnings("ObjectToString")
    @Override
    public final String toString() {
        return head.toString();
    }

    @Override
    public T getVal() throws InterruptedException {
        final T first = head.getFirst();
        moveHead();
        return first;
    }

    @Override
    public T getVal(final long timeout, final TimeUnit units) throws InterruptedException {
        final T value = head.getFirstDFV().getVal(timeout, units);
        if (value == null) {
            if (shouldReportTimeout()) {
                return null;
            } else {
                final T result = head.getFirstDFV().getVal();
                moveHead();
                return result;
            }
        } else {
            moveHead();
            return value;
        }
    }

    private boolean shouldReportTimeout() {
        final DataflowVariable firstDFV = head.getFirstDFV();
        if (!firstDFV.isBound()) return true;
        if (firstDFV instanceof SyncDataflowVariable) {
            return ((SyncDataflowVariable) firstDFV).awaitingParties();
        }
        return false;
    }

    @Override
    public void getValAsync(final MessageStream callback) {
        asyncHead.getFirstDFV().getValAsync(callback);
        moveAsyncHead();
    }

    @Override
    public void getValAsync(final Object attachment, final MessageStream callback) {
        asyncHead.getFirstDFV().getValAsync(attachment, callback);
        moveAsyncHead();
    }

    @Override
    public  Promise rightShift(final Closure closure) {
        return then(closure);
    }

    @Override
    public  void whenBound(final Closure closure) {
        asyncHead.getFirstDFV().whenBound(closure);
        moveAsyncHead();
    }

    /**
     * Schedule closure to be executed by pooled actor after data becomes available.
     * It is important to notice that even if the expression is already bound the execution of closure
     * will not happen immediately but will be scheduled.
     *
     * @param pool    The thread pool to use for task scheduling for asynchronous message delivery
     * @param closure closure to execute when data becomes available. The closure should take at most one argument.
     */
    @Override
    public  void whenBound(final Pool pool, final Closure closure) {
        asyncHead.getFirstDFV().whenBound(pool, closure);
        moveAsyncHead();
    }

    @Override
    public  void whenBound(final PGroup group, final Closure closure) {
        asyncHead.getFirstDFV().whenBound(group, closure);
        moveAsyncHead();
    }

    @Override
    public void whenBound(final MessageStream stream) {
        asyncHead.getFirstDFV().whenBound(stream);
        moveAsyncHead();
    }

    /**
     * Schedule closure to be executed after data became available.
     * It is important to notice that even if the expression is already bound the execution of closure
     * will not happen immediately but will be scheduled
     *
     * @param closure closure to execute when data becomes available. The closure should take at most one argument.
     * @return A promise for the results of the supplied closure. This allows for chaining of then() method calls.
     */
    @Override
    public final  Promise then(final Closure closure) {
        final DataflowVariable result = new DataflowVariable();
        whenBound(new ThenMessagingRunnable(result, closure));
        return result;
    }

    /**
     * Schedule closure to be executed after data becomes available.
     * It is important to notice that even if the expression is already bound the execution of closure
     * will not happen immediately but will be scheduled.
     *
     * @param pool    The thread pool to use for task scheduling for asynchronous message delivery
     * @param closure closure to execute when data becomes available. The closure should take at most one argument.
     * @return A promise for the results of the supplied closure. This allows for chaining of then() method calls.
     */
    @Override
    public  Promise then(final Pool pool, final Closure closure) {
        final DataflowVariable result = new DataflowVariable();
        whenBound(pool, new ThenMessagingRunnable(result, closure));
        return result;
    }

    /**
     * Schedule closure to be executed after data becomes available.
     * It is important to notice that even if the expression is already bound the execution of closure
     * will not happen immediately but will be scheduled.
     *
     * @param group   The PGroup to use for task scheduling for asynchronous message delivery
     * @param closure closure to execute when data becomes available. The closure should take at most one argument.
     * @return A promise for the results of the supplied closure. This allows for chaining of then() method calls.
     */
    @Override
    public  Promise then(final PGroup group, final Closure closure) {
        final DataflowVariable result = new DataflowVariable();
        whenBound(group, new ThenMessagingRunnable(result, closure));
        return result;
    }

    @Override
    public  void wheneverBound(final Closure closure) {
        head.wheneverBound(closure);
    }

    @Override
    public void wheneverBound(final MessageStream stream) {
        head.wheneverBound(stream);
    }

    @Override
    public final  DataflowReadChannel chainWith(final Closure closure) {
        return chainWith(Dataflow.retrieveCurrentDFPGroup(), closure);
    }

    @Override
    public final  DataflowReadChannel chainWith(final Pool pool, final Closure closure) {
        return chainWith(new DefaultPGroup(pool), closure);
    }

    @Override
    public  DataflowReadChannel chainWith(final PGroup group, final Closure closure) {
        final DataflowQueue result = new DataflowQueue();
        group.operator(this, result, new ChainWithClosure(closure));
        return result;
    }

    @Override
    public final  DataflowReadChannel chainWith(final Map params, final Closure closure) {
        return chainWith(Dataflow.retrieveCurrentDFPGroup(), params, closure);
    }

    @Override
    public final  DataflowReadChannel chainWith(final Pool pool, final Map params, final Closure closure) {
        return chainWith(new DefaultPGroup(pool), params, closure);
    }

    @Override
    public  DataflowReadChannel chainWith(final PGroup group, final Map params, final Closure closure) {
        final DataflowQueue result = new DataflowQueue();
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(result)));

        group.operator(parameters, new ChainWithClosure(closure));
        return result;
    }

    @Override
    public  DataflowReadChannel or(final Closure closure) {
        return chainWith(closure);
    }

    @Override
    public DataflowReadChannel filter(final Closure closure) {
        return chainWith(new FilterClosure(closure));
    }

    @Override
    public DataflowReadChannel filter(final Pool pool, final Closure closure) {
        return chainWith(pool, new FilterClosure(closure));
    }

    @Override
    public DataflowReadChannel filter(final PGroup group, final Closure closure) {
        return chainWith(group, new FilterClosure(closure));
    }

    @Override
    public DataflowReadChannel filter(final Map params, final Closure closure) {
        return chainWith(params, new FilterClosure(closure));
    }

    @Override
    public DataflowReadChannel filter(final Pool pool, final Map params, final Closure closure) {
        return chainWith(pool, params, new FilterClosure(closure));
    }

    @Override
    public DataflowReadChannel filter(final PGroup group, final Map params, final Closure closure) {
        return chainWith(group, params, new FilterClosure(closure));
    }

    @Override
    public void into(final DataflowWriteChannel target) {
        into(Dataflow.retrieveCurrentDFPGroup(), target);
    }

    @Override
    public void into(final Pool pool, final DataflowWriteChannel target) {
        into(new DefaultPGroup(pool), target);
    }

    @Override
    public void into(final PGroup group, final DataflowWriteChannel target) {
        group.operator(this, target, new ChainWithClosure(new CopyChannelsClosure()));
    }

    @Override
    public void into(final Map params, final DataflowWriteChannel target) {
        into(Dataflow.retrieveCurrentDFPGroup(), params, target);
    }

    @Override
    public void into(final Pool pool, final Map params, final DataflowWriteChannel target) {
        into(new DefaultPGroup(pool), params, target);
    }

    @Override
    public void into(final PGroup group, final Map params, final DataflowWriteChannel target) {
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(target)));
        group.operator(parameters, new ChainWithClosure(new CopyChannelsClosure()));
    }

    @Override
    public void or(final DataflowWriteChannel target) {
        into(target);
    }

    @Override
    public void split(final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(Dataflow.retrieveCurrentDFPGroup(), target1, target2);
    }

    @Override
    public void split(final Pool pool, final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(new DefaultPGroup(pool), target1, target2);
    }

    @Override
    public void split(final PGroup group, final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(group, asList(target1, target2));
    }

    @Override
    public void split(final List> targets) {
        split(Dataflow.retrieveCurrentDFPGroup(), targets);
    }

    @Override
    public void split(final Pool pool, final List> targets) {
        split(new DefaultPGroup(pool), targets);
    }

    @Override
    public void split(final PGroup group, final List> targets) {
        group.operator(asList(this), targets, new ChainWithClosure(new CopyChannelsClosure()));
    }

    @Override
    public void split(final Map params, final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(Dataflow.retrieveCurrentDFPGroup(), params, target1, target2);
    }

    @Override
    public void split(final Pool pool, final Map params, final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(new DefaultPGroup(pool), params, target1, target2);
    }

    @Override
    public void split(final PGroup group, final Map params, final DataflowWriteChannel target1, final DataflowWriteChannel target2) {
        split(group, params, asList(target1, target2));
    }

    @Override
    public void split(final Map params, final List> targets) {
        split(Dataflow.retrieveCurrentDFPGroup(), params, targets);
    }

    @Override
    public void split(final Pool pool, final Map params, final List> targets) {
        split(new DefaultPGroup(pool), params, targets);
    }

    @Override
    public void split(final PGroup group, final Map params, final List> targets) {
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(targets)));

        group.operator(parameters, new ChainWithClosure(new CopyChannelsClosure()));
    }

    @Override
    public DataflowReadChannel tap(final DataflowWriteChannel target) {
        return tap(Dataflow.retrieveCurrentDFPGroup(), target);
    }

    @Override
    public DataflowReadChannel tap(final Pool pool, final DataflowWriteChannel target) {
        return tap(new DefaultPGroup(pool), target);
    }

    @Override
    public DataflowReadChannel tap(final PGroup group, final DataflowWriteChannel target) {
        final DataflowQueue result = new DataflowQueue();
        group.operator(asList(this), asList(result, target), new ChainWithClosure(new CopyChannelsClosure()));
        return result;
    }

    @Override
    public DataflowReadChannel tap(final Map params, final DataflowWriteChannel target) {
        return tap(Dataflow.retrieveCurrentDFPGroup(), params, target);
    }

    @Override
    public DataflowReadChannel tap(final Pool pool, final Map params, final DataflowWriteChannel target) {
        return tap(new DefaultPGroup(pool), params, target);
    }

    @Override
    public DataflowReadChannel tap(final PGroup group, final Map params, final DataflowWriteChannel target) {
        final DataflowQueue result = new DataflowQueue();
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(result, target)));

        group.operator(parameters, new ChainWithClosure(new CopyChannelsClosure()));
        return result;
    }

    @Override
    public  DataflowReadChannel merge(final DataflowReadChannel other, final Closure closure) {
        return merge(asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final Pool pool, final DataflowReadChannel other, final Closure closure) {
        return merge(pool, asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final PGroup group, final DataflowReadChannel other, final Closure closure) {
        return merge(group, asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final List> others, final Closure closure) {
        return merge(Dataflow.retrieveCurrentDFPGroup(), others, closure);
    }

    @Override
    public  DataflowReadChannel merge(final Pool pool, final List> others, final Closure closure) {
        return merge(new DefaultPGroup(pool), others, closure);
    }

    @Override
    public  DataflowReadChannel merge(final PGroup group, final List> others, final Closure closure) {
        final DataflowQueue result = new DataflowQueue();
        final List> inputs = new ArrayList>();
        inputs.add(this);
        inputs.addAll(others);
        group.operator(inputs, asList(result), new ChainWithClosure(closure));
        return result;
    }

    @Override
    public  DataflowReadChannel merge(final Map params, final DataflowReadChannel other, final Closure closure) {
        return merge(params, asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final Pool pool, final Map params, final DataflowReadChannel other, final Closure closure) {
        return merge(pool, params, asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final PGroup group, final Map params, final DataflowReadChannel other, final Closure closure) {
        return merge(group, params, asList(other), closure);
    }

    @Override
    public  DataflowReadChannel merge(final Map params, final List> others, final Closure closure) {
        return merge(Dataflow.retrieveCurrentDFPGroup(), params, others, closure);
    }

    @Override
    public  DataflowReadChannel merge(final Pool pool, final Map params, final List> others, final Closure closure) {
        return merge(new DefaultPGroup(pool), params, others, closure);
    }

    @Override
    public  DataflowReadChannel merge(final PGroup group, final Map params, final List> others, final Closure closure) {
        final DataflowQueue result = new DataflowQueue();
        final Collection> inputs = new ArrayList>();
        inputs.add(this);
        inputs.addAll(others);
        final Map parameters = new HashMap(params);
        parameters.put("inputs", inputs);
        parameters.put("outputs", asList(result));
        group.operator(parameters, new ChainWithClosure(closure));
        return result;
    }

    @Override
    public void binaryChoice(final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        binaryChoice(Dataflow.retrieveCurrentDFPGroup(), trueBranch, falseBranch, code);
    }

    @Override
    public void binaryChoice(final Pool pool, final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        binaryChoice(new DefaultPGroup(pool), trueBranch, falseBranch, code);
    }

    @Override
    public void binaryChoice(final PGroup group, final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        group.operator(asList(this), asList(trueBranch, falseBranch), new BinaryChoiceClosure(code));
    }

    @Override
    public void binaryChoice(final Map params, final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        binaryChoice(Dataflow.retrieveCurrentDFPGroup(), params, trueBranch, falseBranch, code);
    }

    @Override
    public void binaryChoice(final Pool pool, final Map params, final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        binaryChoice(new DefaultPGroup(pool), params, trueBranch, falseBranch, code);
    }

    @Override
    public void binaryChoice(final PGroup group, final Map params, final DataflowWriteChannel trueBranch, final DataflowWriteChannel falseBranch, final Closure code) {
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(trueBranch, falseBranch)));

        group.operator(parameters, new BinaryChoiceClosure(code));
    }

    @Override
    public void choice(final List> outputs, final Closure code) {
        choice(Dataflow.retrieveCurrentDFPGroup(), outputs, code);
    }

    @Override
    public void choice(final Pool pool, final List> outputs, final Closure code) {
        choice(new DefaultPGroup(pool), outputs, code);
    }

    @Override
    public void choice(final PGroup group, final List> outputs, final Closure code) {
        group.operator(asList(this), outputs, new ChoiceClosure(code));
    }

    @Override
    public void choice(final Map params, final List> outputs, final Closure code) {
        choice(Dataflow.retrieveCurrentDFPGroup(), params, outputs, code);
    }

    @Override
    public void choice(final Pool pool, final Map params, final List> outputs, final Closure code) {
        choice(new DefaultPGroup(pool), params, outputs, code);
    }

    @Override
    public void choice(final PGroup group, final Map params, final List> outputs, final Closure code) {
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(outputs)));

        group.operator(parameters, new ChoiceClosure(code));
    }

    @Override
    public void separate(final List> outputs, final Closure> code) {
        separate(Dataflow.retrieveCurrentDFPGroup(), outputs, code);
    }

    @Override
    public void separate(final Pool pool, final List> outputs, final Closure> code) {
        separate(new DefaultPGroup(pool), outputs, code);
    }

    @Override
    public void separate(final PGroup group, final List> outputs, final Closure> code) {
        group.operator(asList(this), outputs, new SeparationClosure(code));
    }

    @Override
    public void separate(final Map params, final List> outputs, final Closure> code) {
        separate(Dataflow.retrieveCurrentDFPGroup(), params, outputs, code);
    }

    @Override
    public void separate(final Pool pool, final Map params, final List> outputs, final Closure> code) {
        separate(new DefaultPGroup(pool), params, outputs, code);
    }

    @Override
    public void separate(final PGroup group, final Map params, final List> outputs, final Closure> code) {
        final Map parameters = new HashMap(params);
        parameters.put("inputs", asList(this));
        parameters.put("outputs", asList(asList(outputs)));

        group.operator(parameters, new SeparationClosure(code));
    }

    private volatile DataflowChannelEventOrchestrator eventManager;

    @Override
    public synchronized DataflowChannelEventListenerManager getEventManager() {
        if (eventManager != null) return eventManager;
        eventManager = new DataflowChannelEventOrchestrator();
        return eventManager;
    }

    private void fireOnMessage(final T value) {
        if (eventManager != null) {
            eventManager.fireOnMessage(value);
        }
    }

    @Override
    public boolean isBound() {
        return head.getFirstDFV().isBound();
    }

    /**
     * Returns the current size of the buffer
     *
     * @return Number of DFVs in the queue
     */
    @Override
    public int length() {
        StreamCore current = head;
        int length = 0;
        while (current.getFirstDFV().isBound()) {
            length += 1;
            current = (StreamCore) current.getRest();
        }
        return length;
    }

    @Override
    public DataflowExpression poll() throws InterruptedException {
        final DataflowVariable firstDFV = head.getFirstDFV();
        if (firstDFV.isBound()) {
            moveHead();
            return firstDFV;
        } else return null;
    }

    protected final List> allUnprocessedDFVs() throws InterruptedException {
        final List> values = new ArrayList>();
        StreamCore currentHead = asyncHead;
        while (currentHead != null) {
            values.add(currentHead.getFirstDFV());
            currentHead = (StreamCore) currentHead.rest.get();
        }
        return values;
    }

    private void moveHead() {
        asyncHead = (StreamCore) asyncHead.getRest();
        head = (StreamCore) head.getRest();
    }

    private void moveAsyncHead() {
        moveHead();
    }
}