/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.concurrent;

import edu.emory.mathcs.util.concurrent.*;
import edu.emory.mathcs.backport.java.util.concurrent.Executor;

/**
 * The executor that delegates to other executor (usually pooling executor) but
 * ensures that tasks scheduled from a single thread are executed sequentially.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */

public class ThreadSerializingExecutor implements Executor {
    final Executor pool;
    final ThreadLocal queue = new ThreadLocal() {
        protected Object initialValue() {
            return new TaskQueue();
        }

    };

    /**
     * Creates a new thread serializing executor that delegates to the
     * specified executor.
     * @param pool the delegating executor
     */
    public ThreadSerializingExecutor(Executor pool) {
        this.pool = pool;
    }

    public void execute(Runnable runnable) {
        TaskQueue tlqueue = (TaskQueue)this.queue.get();
        Runnable task = tlqueue.put(runnable);
        if (task != null) {
            pool.execute(task);
        }
    }

    private static class TaskQueue {
        Node last;
        public Runnable put(Runnable runnable) {
            final Node node = new Node(runnable);
            synchronized (this) {
                Node prevlast = this.last;
                this.last = node;
                if (prevlast != null) {
                    // sequence in progress
                    prevlast.next = node;
                    return null;
                }
                else {
                    // new sequence
                    return new Runnable() {
                        public void run() {
                            Node current = node;
                            while (true) {
                                current.runnable.run();
                                synchronized (TaskQueue.this) {
                                    current = current.next;
                                    if (current == null) {
                                        // mark that sequence completed
                                        TaskQueue.this.last = null;
                                        return;
                                    }
                                }
                            }
                        }
                    };
                }
            }
        }

        private final class Node {
            final Runnable runnable;
            Node next;
            Node(Runnable runnable) { this.runnable = runnable; }
        }
    }
}
