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

bear.main.phaser.ComputingGrid Maven / Gradle / Ivy

The newest version!
package bear.main.phaser;

import chaschev.lang.Functions2;
import chaschev.lang.Lists2;
import chaschev.lang.OpenBean;
import chaschev.util.CatchyRunnable;
import chaschev.util.Exceptions;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ArrayTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

import static com.google.common.base.Optional.of;

/**
 * Elegant sweep strategy: add listener for
 *
 * Table can be improved to represent only a computation frame:
 *  - present a default sweep strategy, add exclusions
 *  - provide a phase iterator
 *
 * @author Andrey Chaschev [email protected]
 */
public class ComputingGrid {
    private final int partiesCount;

    //todo this should be extracted into a builder
//    private transient int builderPhaseCount;

//    List> phases = new ArrayList<>();
    final List> parties;
    final List> phases ;

    final ArrayTable> table;

    private final Map columnKeyToIndex;
    private final Map rowKeyToIndex;


    public ComputingGrid(List> phases, Iterable columnKeys) {
        this.phases = phases;
        table = ArrayTable.create(
            Iterables.transform(phases, Functions2.field("phase")),
            columnKeys);

        partiesCount = table.columnKeyList().size();

        columnKeyToIndex = (Map) OpenBean.getFieldValue(table, "columnKeyToIndex");
        rowKeyToIndex = (Map) OpenBean.getFieldValue(table, "rowKeyToIndex");

        this.parties = new ArrayList>(partiesCount);

        int i = 0;

        for (C columnKey : columnKeys) {
            this.parties.add(new PhaseParty(i, columnKey, this));
            i++;
        }

        phaseEntered = new boolean[phases.size()];

        // can't load these lazily as futures are accessed from outside
        // will probably need to split out futures for lazy init or get the futures...
        for (Phase phase : phases) {
            addPhase(phase);
        }
    }

    public  List> phaseFutures(Phase phase, int relative) {
        return phaseFutures(phaseToRowIndex(phase.phase) + relative, null);
    }

    public  List> phaseFutures(Phase phase) {
        return phaseFutures(phase, 0);
    }

    public  List> phaseFutures(final int rowIndex, Class vClass) {
        return Lists2.computingList(partiesCount, new Function>() {
            public ListenableFuture apply(Integer colIndex) {
                return (ListenableFuture) table.at(rowIndex, colIndex).getFuture();
            }
        });
    }

    public Integer phaseToRowIndex(PHASE phase) {
        return rowKeyToIndex.get(phase);
    }

    public Integer partyToColumnIndex(C col) {
        return columnKeyToIndex.get(col);
    }

    public  ListenableFuture> aggregateSuccessful(Phase phase) {
        return Futures.successfulAsList(phaseFutures(phase));
    }


    public  ListenableFuture> aggregateSuccessful(int phase, Class vClass) {
        return Futures.successfulAsList(phaseFutures(phase, vClass));
    }

    ComputingGrid awaitTermination(){
        try {

            aggregateSuccessful(phaseToRowIndex(lastPhase()), Object.class).get();
            return this;
        } catch (Exception e) {
            throw Exceptions.runtime(e);
        }
    }

    private PHASE lastPhase() {
        ImmutableList row = table.rowKeyList();

        return row.get(row.size() - 1);
    }

    public  GridCell cell(int rowKey, C columnKey) {
        return table.get(rowKey, columnKey);
    }

    public  GridCell cell(Phase phase, C columnKey) {
        return (GridCell) table.at(phaseToRowIndex(phase.phase), partyToColumnIndex(columnKey));
    }

    public GridCell cellAt(int rowIndex, int columnIndex) {
        return table.at(rowIndex, columnIndex);
    }

    public  GridCell cellAt(int rowIndex, int columnIndex, Class vClass) {
        return (GridCell) table.at(rowIndex, columnIndex);
    }

    private final boolean[] phaseEntered;

    @Nonnull
    public Optional> phase(String name) {
        for (Phase phase : phases) {
            if(name.equals(phase.getName())) return (Optional) of(phase);
        }

        return Optional.absent();
    }

    public  SettableFuture future(String phaseName, String partyName, Class vClass) {
        return cell(phaseName, partyName, vClass).getFuture();
    }

    public  GridCell cell(String phaseName, String partyName, Class vClass) {
        Optional> phase = phase(phaseName);

        if(!phase.isPresent()) throw new IllegalArgumentException("didn't find phase: " + phaseName);

        Optional> party = party(partyName);

        if(!party.isPresent()) {
            throw new IllegalArgumentException("didn't find party: " + partyName);
        }

        return cellAt(phase.get().rowIndex, party.get().index);
    }

    @Nonnull
    private Optional> party(String partyName) {
        for (PhaseParty party : parties) {
            if(partyName.equals(party.getName())){
                return of(party);
            }
        }
        return Optional.absent();
    }

    public static interface PartyListener{
        void handle(Phase phase, PhaseParty party);
    }

    public static interface WhenAllFinished{
        void run(int failedParties, int okParties);
    }

    protected PartyListener phaseEnterListener;
    protected PartyListener partyFinishListener;

    protected WhenAllFinished whenAllFinished;

    private void checkPhaseEntered(Phase phase, PhaseParty party){
        if(phaseEnterListener == null || phaseEntered[phase.rowIndex]){
            return;
        }

        synchronized (phaseEntered){
            if(phaseEntered[phase.rowIndex]) return;

            phaseEntered[phase.rowIndex] = true;
        }

        phaseEnterListener.handle(phase, party);
    }

    public ComputingGrid startParties(ExecutorService service) {
        final AtomicInteger partiesArrived = new AtomicInteger();
        final AtomicInteger partiesFailed = new AtomicInteger();
        for (int i = 0; i < partiesCount; i++) {
            final int partyIndex = i;

            final PhaseParty party = parties.get(i);

            service.submit(new CatchyRunnable(new Runnable() {
                @Override
                public void run() {
                    Phase lastPhase = null;
                    try {
                        for (Phase phase : phases) {
                            lastPhase = phase;

                            GridCell cell = table.at(party.currentPhaseIndex, partyIndex)
                                .started();

                            checkPhaseEntered(phase, party);

                            Object result;

                            try {
                                result = cell.callable.call(party, party.currentPhaseIndex, phase);

                                party.lastResult = result;

                                cell.getFuture().set(result);

                                if (cell.whenDone != null) {
                                    cell.whenDone.act(result, party);
                                }
                            } catch (Exception e) {
                                party.setException(
                                    e instanceof GridException ? (GridException) e :
                                        new GridException(e, phase, party)
                                );
                                partiesFailed.incrementAndGet();

                                LoggerFactory.getLogger("log").error(e.toString(), e);

                                break;
                            } finally {
                                cell.finishedAtMs = System.currentTimeMillis();
                                party.currentPhaseIndex++;
                            }
                        }
                    } finally {
                        partiesArrived.incrementAndGet();

                        if(whenAllFinished != null && partiesArrived.get() == partiesCount){
                            whenAllFinished.run(partiesFailed.get(), partiesCount);
                        }

                        if(partyFinishListener != null){
                            partyFinishListener.handle(lastPhase, party);
                        }
                    }
                }
            }));
        }

        return this;
    }

    private  synchronized  void addPhase(Phase phase) {
        Integer rowIndex = phaseToRowIndex(phase.getPhase());
        if(rowIndex != null && table.at(rowIndex, 0) != null){
            return;
        }

        for (C columnKey : table.columnKeyList()) {
            table.put(phase.phase, columnKey, new GridCell());
        }

        ImmutableList list = table.columnKeyList();

        List> phaseCallables =  (List)phase.getParties(this);

        phase.rowIndex = rowIndex;

        for (int i = 0; i < list.size(); i++) {
            table.at(rowIndex, i).callable = (PhaseCallable) phaseCallables.get(i);
        }
    }

    public  SettableFuture previousResult(PhaseParty party, int phaseIndex, Class aClass) {
        return (SettableFuture) table.at(phaseIndex - 1, partyToColumnIndex(party.column)).getFuture();
    }

    public ComputingGrid setPhaseEnterListener(PartyListener partyListener) {
        this.phaseEnterListener = partyListener;
        return this;
    }

    public ComputingGrid setPartyFinishListener(PartyListener partyFinishListener) {
        this.partyFinishListener = partyFinishListener;
        return this;
    }

    public void setWhenAllFinished(WhenAllFinished whenAllFinished) {
        this.whenAllFinished = whenAllFinished;
    }

    public ImmutableList phases() {
        return table.rowKeyList();
    }

    public ImmutableList parties() {
        return table.columnKeyList();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy