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

net.sf.hajdbc.util.concurrent.SynchronousExecutor Maven / Gradle / Ivy

There is a newer version: 3.6.61
Show newest version
/*
 * HA-JDBC: High-Availability JDBC
 * Copyright (C) 2012  Paul Ferraro
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package net.sf.hajdbc.util.concurrent;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

import net.sf.hajdbc.util.Reversed;

/**
 * Executor service that executes tasks in the caller thread.
 * 
 * @author Paul Ferraro
 */
public class SynchronousExecutor extends AbstractExecutorService
{
	private final ExecutorService executor;
	private final boolean reverse;
	
	public SynchronousExecutor(ExecutorService executor)
	{
		this(executor, false);
	}
	
	public SynchronousExecutor(ExecutorService executor, boolean reverse)
	{
		this.executor = executor;
		this.reverse = reverse;
	}
	
	/**
	 * @see java.util.concurrent.ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit)
	 */
	@Override
	public boolean awaitTermination(long time, TimeUnit unit) throws InterruptedException
	{
		return this.executor.awaitTermination(time, unit);
	}

	/**
	 * @see java.util.concurrent.ExecutorService#isShutdown()
	 */
	@Override
	public boolean isShutdown()
	{
		return this.executor.isShutdown();
	}

	/**
	 * @see java.util.concurrent.ExecutorService#isTerminated()
	 */
	@Override
	public boolean isTerminated()
	{
		return this.executor.isTerminated();
	}

	/**
	 * @see java.util.concurrent.ExecutorService#shutdown()
	 */
	@Override
	public void shutdown()
	{
		this.executor.shutdown();
	}

	/**
	 * @see java.util.concurrent.ExecutorService#shutdownNow()
	 */
	@Override
	public List shutdownNow()
	{
		return this.executor.shutdownNow();
	}

	/**
	 * @see java.util.concurrent.Executor#execute(java.lang.Runnable)
	 */
	@Override
	public void execute(Runnable task)
	{
		task.run();
	}

	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
	 */
	@Override
	public Future submit(Runnable task)
	{
		return this.submit(Executors.callable(task));
	}

	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable, java.lang.Object)
	 */
	@Override
	public  Future submit(Runnable task, T result)
	{
		return this.submit(Executors.callable(task, result));
	}

	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable)
	 */
	@Override
	public  Future submit(Callable task)
	{
		return new EagerFuture(task);
	}

	/**
	 * Executes the specified tasks serially, until the first successful result, then the remainder using the executor with which this executor was created.
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#invokeAll(java.util.Collection)
	 */
	@Override
	public  List> invokeAll(Collection> tasks) throws InterruptedException
	{
		return this.invokeAll(tasks, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
	}

	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#invokeAll(java.util.Collection, long, java.util.concurrent.TimeUnit)
	 */
	@Override
	public  List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException
	{
		if (tasks.isEmpty()) return Collections.emptyList();

		long end = (timeout == Long.MAX_VALUE) ? 0 : System.currentTimeMillis() + unit.toMillis(timeout);
		boolean synchronous = !this.reverse;
		int remaining = tasks.size();
		LinkedList> futures = new LinkedList>();
		
		for (Callable task: this.reverse ? new Reversed>(new ArrayList>(tasks)) : tasks)
		{
			remaining -= 1;

			if (synchronous)
			{
				Future future = this.reverse ? new LazyFuture(task) : new EagerFuture(task);
				
				if (this.reverse)
				{
					futures.addFirst(future);
				}
				else
				{
					futures.addLast(future);
				}
				
				// Execute remaining tasks in parallel, if there are multiple
				if (remaining > 1)
				{
					synchronous = false;
				}
			}
			else
			{
				Future future = this.executor.submit(task);
				if (this.reverse)
				{
					futures.addFirst(future);
				}
				else
				{
					futures.addLast(future);
				}
				
				if (this.reverse && (remaining == 1))
				{
					synchronous = true;
				}
			}
		}
		
		try
		{
			// Wait until all tasks have finished
			for (Future future: this.reverse ? new Reversed>(futures) : futures)
			{
				if (!future.isDone())
				{
					try
					{
						if (end == 0)
						{
							future.get();
						}
						else
						{
							long now = System.currentTimeMillis();
							if (now < end)
							{
								future.get(end - now, TimeUnit.MILLISECONDS);
							}
						}
					}
					catch (ExecutionException e)
					{
						// Ignore
					}
					catch (CancellationException e)
					{
						// Ignore
					}
				}
			}
		}
		catch (TimeoutException e)
		{
			// Ignore
		}
		finally
		{
			// If interrupted, cancel any unfinished tasks
			for (Future future: this.reverse ? new Reversed>(futures) : futures)
			{
				if (!future.isDone())
				{
					future.cancel(true);
				}
			}
		}
		
		return futures;
	}
	
	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection)
	 */
	@Override
	public  T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException
	{
		return this.getAny(this.invokeAll(tasks));
	}

	/**
	 * {@inheritDoc}
	 * @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection, long, java.util.concurrent.TimeUnit)
	 */
	@Override
	public  T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException
	{
		return this.getAny(this.invokeAll(tasks, timeout, unit));
	}

	private  T getAny(List> futures) throws InterruptedException, ExecutionException
	{
		if (futures.isEmpty()) throw new IllegalArgumentException();
		
		return futures.get(this.reverse ? (futures.size() - 1) : 0).get();
	}
	
	/**
	 * Future that doesn't execute its task until get(...).
	 */
	private static class LazyFuture implements Future
	{
		private enum State
		{
			NEW, CANCELLED, DONE;
		}
		private final Callable task;
		private volatile T result;
		private volatile ExecutionException exception;
		private final AtomicReference state = new AtomicReference(State.NEW);
		private final CountDownLatch latch = new CountDownLatch(1);
		
		LazyFuture(Callable task)
		{
			this.task = task;
		}

		/**
		 * {@inheritDoc}
		 * @see java.util.concurrent.Future#cancel(boolean)
		 */
		@Override
		public boolean cancel(boolean interrupt)
		{
			return this.state.compareAndSet(State.NEW, State.CANCELLED);
		}

		/**
		 * {@inheritDoc}
		 * @see java.util.concurrent.Future#get()
		 */
		@Override
		public T get() throws ExecutionException, InterruptedException
		{
			return this.get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
		}

		/**
		 * {@inheritDoc}
		 * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
		 */
		@Override
		public T get(long time, TimeUnit unit) throws ExecutionException, InterruptedException
		{
			if (this.state.compareAndSet(State.NEW, State.DONE))
			{
				try
				{
					this.result = this.task.call();
				}
				catch (Throwable e)
				{
					this.exception = new ExecutionException(e);
				}
				this.latch.countDown();
			}
			
			if (this.state.get() == State.CANCELLED)
			{
				throw new CancellationException();
			}
			
			if (time == Long.MAX_VALUE)
			{
				this.latch.await();
			}
			else
			{
				this.latch.await(time, unit);
			}
			
			if (this.exception != null)
			{
				throw this.exception;
			}
			
			return this.result;
		}

		/**
		 * {@inheritDoc}
		 * @see java.util.concurrent.Future#isCancelled()
		 */
		@Override
		public boolean isCancelled()
		{
			return this.state.get() == State.CANCELLED;
		}

		/**
		 * {@inheritDoc}
		 * @see java.util.concurrent.Future#isDone()
		 */
		@Override
		public boolean isDone()
		{
			return this.state.get() == State.DONE;
		}
	}

	/**
	 * Light-weight future implementation that executes its task on construction.
	 * @param 
	 */
	private static class EagerFuture implements Future
	{
		private T result;
		private ExecutionException exception;
		
		EagerFuture(Callable task)
		{
			try
			{
				this.result = task.call();
			}
			catch (Throwable e)
			{
				this.exception = new ExecutionException(e);
			}
		}
		
		@Override
		public boolean cancel(boolean mayInterruptIfRunning)
		{
			return false;
		}

		@Override
		public T get() throws ExecutionException
		{
			if (this.exception != null) throw this.exception;
			
			return this.result;
		}

		@Override
		public T get(long time, TimeUnit unit) throws ExecutionException
		{
			return this.get();
		}

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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy