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

com.ibm.etcd.client.SerializingExecutor Maven / Gradle / Ivy

There is a newer version: 0.0.24
Show newest version
/*
 * Copyright 2017, 2018 IBM Corp. All Rights Reserved.
 *
 * 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.ibm.etcd.client;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;

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

/**
 * Delegates submitted tasks to the shared threadpool but ensures they
 * are executed in order and in serial
 * 
 */
public class SerializingExecutor implements Executor {
    
    private static final Logger logger = LoggerFactory.getLogger(SerializingExecutor.class);
    
	private final Executor sharedPool;
	private final Queue workQueue;
	private final boolean bounded;
	private volatile boolean scheduled = false;
	
	public SerializingExecutor(Executor parentPool) {
	    this(parentPool, 0);
	}
	
	public SerializingExecutor(Executor parentPool, int capacity) {
        if(parentPool == null) throw new NullPointerException();
        this.sharedPool = parentPool;
        this.bounded = capacity > 0;
        this.workQueue = bounded ? new ArrayBlockingQueue<>(capacity)
                : new ConcurrentLinkedQueue<>();
    }
	
	protected void logTaskUncheckedException(Throwable t) {
		logger.error("Uncaught task exception: "+t, t);
	}
	
	@SuppressWarnings("serial")
    class TaskRun extends ReentrantLock implements Runnable {
	    @Override public void run() {
            try {
                for(;;) {
                    Queue wq = workQueue;
                    Runnable next;
                    if((next = wq.poll()) == null) {
                        lock();
                        try {
                            scheduled = false;
                            if((next = wq.poll()) == null) return;
                            scheduled = true;
                        } finally {
                            unlock();
                        }
                    }
                    try {
                        next.run();
                    } catch(RuntimeException e) {
                        logTaskUncheckedException(e);
                    }
                }
            } catch(Throwable t) {
                dispatch();
                logTaskUncheckedException(t);
                throw t;
            }
        }
	}
	
	private final TaskRun runner = new TaskRun();
	
	@Override
	public void execute(Runnable command) {
	    if(bounded) try {
            ((ArrayBlockingQueue)workQueue).put(command);
        } catch (InterruptedException e) {
            throw new RuntimeException(e); //TODO TBD
        }
	    else workQueue.offer(command);
	        
		if(!scheduled) {
			boolean doit = false;
			runner.lock();
			try {
				if(!scheduled) {
					scheduled = true;
					doit = true;
				}
			} finally {
			    runner.unlock();
			}
			if(doit) dispatch();
		}
	}
	
	private void dispatch() {
		boolean ok = false;
		try {
			sharedPool.execute(runner);
			ok = true;
		} finally {
			if(!ok) {
			    runner.lock();
			    try {
				scheduled = false; // bad situation
			    } finally {
			        runner.unlock();
			    }
			}
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy