com.wolvereness.overmapped.lib.MultiProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.wolvereness.overmapped.lib.MultiProcessor.java

Source

/*
 * This file is part of wolvereness-commons.
 *
 * wolvereness-commons is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolvereness-commons is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with wolvereness-commons.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.wolvereness.overmapped.lib;

import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

import org.apache.commons.lang3.Validate;

import com.google.common.collect.ImmutableList;

public abstract class MultiProcessor {
    volatile boolean shutdown;

    public static MultiProcessor newMultiProcessor(final int threads, final ThreadFactory factory) {
        Validate.isTrue(threads >= 0, "Cannot have negative threads");
        return threads == 0 ? new SingletonProcessor() : new ProperProcessor(threads, factory);

    }

    public void shutdown() {
        shutdown = true;
    }

    final void checkShutdown() {
        if (MultiProcessor.this.shutdown)
            throw new IllegalStateException("Cannot submit tasks to shutdown processor");
    }

    public abstract <T> Future<T> submit(final Callable<T> task);
}

final class SingletonProcessor extends MultiProcessor {

    @Override
    public <T> Future<T> submit(final Callable<T> task) {
        super.checkShutdown();
        try {
            final T object = task.call();
            return new Future<T>() {
                @Override
                public boolean cancel(final boolean mayInterruptIfRunning) {
                    return false;
                }

                @Override
                public boolean isCancelled() {
                    return false;
                }

                @Override
                public boolean isDone() {
                    return true;
                }

                @Override
                public T get() throws InterruptedException, ExecutionException {
                    return object;
                }

                @Override
                public T get(final long timeout, final TimeUnit unit)
                        throws InterruptedException, ExecutionException, TimeoutException {
                    return object;
                }
            };
        } catch (final Exception exception) {
            final Exception ex = exception;
            return new Future<T>() {
                @Override
                public boolean cancel(final boolean mayInterruptIfRunning) {
                    return false;
                }

                @Override
                public boolean isCancelled() {
                    return false;
                }

                @Override
                public boolean isDone() {
                    return true;
                }

                @Override
                public T get() throws InterruptedException, ExecutionException {
                    throw new ExecutionException(ex);
                }

                @Override
                public T get(final long timeout, final TimeUnit unit)
                        throws InterruptedException, ExecutionException, TimeoutException {
                    throw new ExecutionException(ex);
                }
            };
        }
    }
}

final class ProperProcessor extends MultiProcessor {
    @SuppressWarnings("unchecked")
    static final AtomicIntegerFieldUpdater<Task<?>> STATE_UPDATER = (AtomicIntegerFieldUpdater<Task<?>>) (Object) AtomicIntegerFieldUpdater
            .newUpdater(Task.class, "state");

    class Task<T> implements Future<T> {
        private static final int READY = 0;
        private static final int PROCESSING = READY + 1;
        private static final int WAITING = PROCESSING + 1;
        private static final int DONE = WAITING + 1;
        private static final int EXCEPTION = DONE + 1;
        private static final int CANCELLED = EXCEPTION + 1;
        final Callable<T> callable;
        private Throwable exception;
        private T value;
        volatile int state;

        Task(final Callable<T> callable) {
            this.callable = callable;
        }

        @Override
        public boolean cancel(final boolean mayInterruptIfRunning) {
            return STATE_UPDATER.compareAndSet(this, READY, CANCELLED);
        }

        @Override
        public boolean isCancelled() {
            return state == CANCELLED;
        }

        @Override
        public boolean isDone() {
            final int state = this.state;
            return state == DONE || state == CANCELLED;
        }

        @Override
        public T get() throws InterruptedException, ExecutionException, CancellationException {
            while (true) {
                switch (this.state) {
                case DONE:
                    return value;
                case EXCEPTION:
                    throw new ExecutionException(exception);
                case CANCELLED:
                    throw new CancellationException();
                case PROCESSING:
                    if (!STATE_UPDATER.compareAndSet(this, PROCESSING, WAITING)) {
                        continue;
                    }
                    // We transition ourselves (without loop) from PROCESSING -> WAITING
                case WAITING:
                    impatientlyWait();
                    continue;
                case READY:
                    handle();
                    continue;
                default:
                    throw new AssertionError("Unexpected value of state: " + this.state);
                }
            }
        }

        void handle() {
            if (STATE_UPDATER.compareAndSet(this, READY, PROCESSING)) {
                calculate();
            }
        }

        private void calculate() {
            try {
                this.value = callable.call();
                if (!STATE_UPDATER.compareAndSet(this, PROCESSING, DONE)) {
                    notifyWaitting(DONE);
                }
            } catch (final Throwable ex) {
                this.exception = ex;
                if (!STATE_UPDATER.compareAndSet(this, PROCESSING, EXCEPTION)) {
                    notifyWaitting(EXCEPTION);
                }
            }
        }

        private void impatientlyWait() throws InterruptedException {
            final Queue<Task<?>> queue = ProperProcessor.this.queue;
            while (true) {
                if (state != WAITING)
                    return;

                final Task<?> t = queue.poll();
                if (t != null) {
                    t.handle();
                } else {
                    break;
                }
            }

            patientlyWait();
        }

        private synchronized void patientlyWait() throws InterruptedException {
            while (state == WAITING) {
                this.wait();
            }
        }

        private synchronized void notifyWaitting(final int newState) {
            this.state = newState;
            this.notifyAll();
        }

        @Override
        public T get(final long timeout, final TimeUnit unit)
                throws InterruptedException, ExecutionException, TimeoutException {
            throw new UnsupportedOperationException("Cannot time waiting for a MultiProcessor");
        }
    }

    private final BlockingQueue<Task<?>> queue = new LinkedBlockingQueue<Task<?>>();
    private final Collection<Thread> threads;

    public ProperProcessor(int threadCount, final ThreadFactory factory) {
        final ImmutableList.Builder<Thread> threads = ImmutableList.builder();
        while (threadCount-- >= 1) {
            final Thread thread = factory.newThread(new Runnable() {
                @Override
                public void run() {
                    final BlockingQueue<Task<?>> queue = ProperProcessor.this.queue;
                    try {
                        while (!shutdown) {
                            queue.take().handle();
                        }
                    } catch (final InterruptedException ex) {
                        if (!shutdown)
                            throw new IllegalStateException("Interrupted innappropriately", ex);
                    }
                }
            });
            threads.add(thread);
        }

        final Iterator<Thread> it = (this.threads = threads.build()).iterator();
        final Thread initial = it.next();
        if (it.hasNext()) {
            submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    while (it.hasNext()) {
                        it.next().start();
                    }
                    return null;
                }
            });
        }
        initial.start();
    }

    @Override
    public void shutdown() {
        if (shutdown)
            return;
        super.shutdown();

        final Queue<Task<?>> queue = this.queue;
        while (true) {
            final Task<?> task = queue.poll();
            if (task != null) {
                task.cancel(false);
            } else {
                break;
            }
        }
        for (final Thread thread : threads) {
            thread.interrupt();
        }
    }

    @Override
    public <T> Future<T> submit(final Callable<T> callable) {
        super.checkShutdown();
        final Task<T> task = new Task<T>(callable);
        queue.add(task);
        return task;
    }
}