com.github.rinde.rinsim.experiment.LocalComputer.java Source code

Java tutorial

Introduction

Here is the source code for com.github.rinde.rinsim.experiment.LocalComputer.java

Source

/*
 * Copyright (C) 2011-2016 Rinde van Lon, iMinds-DistriNet, KU Leuven
 *
 * 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 com.github.rinde.rinsim.experiment;

import static com.google.common.base.Verify.verifyNotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.rinde.rinsim.experiment.Experiment.Builder;
import com.github.rinde.rinsim.experiment.Experiment.SimArgs;
import com.github.rinde.rinsim.experiment.Experiment.SimulationResult;
import com.github.rinde.rinsim.experiment.PostProcessor.FailureStrategy;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

final class LocalComputer implements Computer {
    static final Logger LOGGER = LoggerFactory.getLogger(LocalComputer.class);
    static final long THREAD_SLEEP_TIME_MS = 10L;
    static final long MAX_WAIT_FOR_SHUTDOWN_S = 10L;

    LocalComputer() {
    }

    @Override
    public ExperimentResults compute(Builder builder, Set<SimArgs> inputs) {
        final ImmutableList.Builder<ExperimentRunner> runnerBuilder = ImmutableList.builder();
        for (final SimArgs args : inputs) {
            runnerBuilder.add(new ExperimentRunner(args));
        }

        final List<ExperimentRunner> runners = runnerBuilder.build();

        final int threads = Math.min(builder.numThreads, runners.size());
        final ListeningExecutorService executor;
        if (builder.showGui) {
            executor = MoreExecutors.newDirectExecutorService();
        } else {
            executor = MoreExecutors
                    .listeningDecorator(Executors.newFixedThreadPool(threads, new LocalThreadFactory()));
        }

        final List<SimulationResult> results = Collections.synchronizedList(new ArrayList<SimulationResult>());
        final ResultCollector resultCollector = new ResultCollector(executor, results, builder.resultListeners);

        try {
            for (final ExperimentRunner r : runners) {
                checkForError(executor, resultCollector);
                final ListenableFuture<SimulationResult> f = executor.submit(r);
                Futures.addCallback(f, resultCollector);
            }
            while (results.size() < inputs.size() && !resultCollector.hasError()) {
                Thread.sleep(THREAD_SLEEP_TIME_MS);
            }
            checkForError(executor, resultCollector);
        } catch (final InterruptedException e) {
            LOGGER.trace("Interrupt, shutting down the executor.");
            executor.shutdownNow();
            LOGGER.trace("Waiting for executor to shutdown.");
            try {
                final boolean executorStopped = executor.awaitTermination(MAX_WAIT_FOR_SHUTDOWN_S,
                        TimeUnit.SECONDS);
                if (executorStopped) {
                    LOGGER.trace("Executor is shutdown.");
                } else {
                    LOGGER.warn("Executor did not stop, timed out after {} seconds.", MAX_WAIT_FOR_SHUTDOWN_S);
                }
            } catch (final InterruptedException e1) {
                LOGGER.warn("Waiting for executor to shutdown is interrupted.");
            }
            return ExperimentResults.create(builder, ImmutableSet.<SimulationResult>of());
        }

        checkForError(executor, resultCollector);
        executor.shutdown();

        final ExperimentResults er = ExperimentResults.create(builder, ImmutableSet.copyOf(results));
        for (final ResultListener rl : builder.resultListeners) {
            rl.doneComputing(er);
        }
        return er;
    }

    static void checkForError(ListeningExecutorService executor, ResultCollector collector) {
        if (collector.hasError()) {
            LOGGER.info("Found error, shutting down. {}", collector.getFirstError());
            executor.shutdown();
            if (collector.getFirstError() instanceof RuntimeException) {
                throw (RuntimeException) collector.getFirstError();
            }
            throw new IllegalStateException(collector.getFirstError());
        }
    }

    static class LocalThreadFactory implements ThreadFactory {
        static final AtomicInteger THREAD_ID = new AtomicInteger(0);

        LocalThreadFactory() {
        }

        @Override
        public Thread newThread(@Nullable Runnable r) {
            return new Thread(r, "RinSim-exp-" + THREAD_ID.getAndIncrement());
        }
    }

    static class ResultCollector implements FutureCallback<SimulationResult> {
        final ListeningExecutorService executor;
        final List<SimulationResult> results;
        final List<ResultListener> resultListeners;

        volatile List<Throwable> throwables;

        ResultCollector(ListeningExecutorService ex, List<SimulationResult> res, List<ResultListener> listeners) {
            executor = ex;
            results = res;
            resultListeners = listeners;
            throwables = new ArrayList<>();
        }

        public boolean hasError() {
            return !throwables.isEmpty();
        }

        public Throwable getFirstError() {
            return throwables.get(0);
        }

        @Override
        public void onFailure(Throwable t) {
            throwables.add(t);
            executor.shutdownNow();
        }

        @Override
        public void onSuccess(@Nullable SimulationResult result) {
            final SimulationResult res = verifyNotNull(result);
            for (final ResultListener rl : resultListeners) {
                try {
                    rl.receive(res);
                } catch (final RuntimeException e) {
                    System.err.println("ResultListener " + rl + " failed to receive result.");
                    e.printStackTrace(System.err);
                }
            }
            if (res.getResultObject() == FailureStrategy.RETRY) {
                final ExperimentRunner newRunner = new ExperimentRunner(res.getSimArgs());
                Futures.addCallback(executor.submit(newRunner), this);
            } else {
                results.add(result);
            }
        }
    }

    static class ExperimentRunner implements Callable<SimulationResult> {
        private final SimArgs arguments;

        ExperimentRunner(SimArgs args) {
            arguments = args;
        }

        @Override
        public SimulationResult call() {
            final Object resultObject = Experiment.perform(arguments);
            final SimulationResult result = SimulationResult.create(arguments, resultObject);
            return result;
        }
    }
}