/*******************************************************************************
 * Copyright (c) 2012 Tilera Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     William R. Swanson (Tilera Corporation)
 *******************************************************************************/

package org.eclipse.cdt.visualizer.ui.util;

import java.util.concurrent.Callable;

/**
 * Runnable object that returns a result object.
 *
 * This is like Future<V> from the concurrent package,
 * but with a few less bells and whistles.
 *
 * Intended to be used, for example, as follows:
 *
 * RunnableWithResult<X> runnable = new RunnableWithResult<X>() {
 *   public X call() {
 *     ... do work, return an X object ...
 *   }
 * }
 *
 * Thread thread = new Thread(runnable);
 * thread.start();
 * X result = runnable.getResult(0);
 *
 * or, to run it on the UI thread...
 *
 * GUIUtils.execAndWait(runnable);
 * X result = runnable.getResult(0);
 *
 * Note: if you're invoking this from the UI thread,
 * it's important to use execAndWait(), so that the runnable
 * gets a turn on the event loop, otherwise you'll hang the UI!
 *
 */
public class RunnableWithResult<V> implements Runnable, Callable<V> {
	// --- members ---

	/** Result to return */
	protected V m_result = null;

	/** Whether run() has completed */
	protected boolean m_done = false;

	// --- constructors/destructors ---

	/** Constructor. */
	public RunnableWithResult() {
	}

	/** Dispose method. */
	public void dispose() {
		m_result = null;
	}

	// --- accessors ---

	/** Sets result value */
	public void setResult(V result) {
		m_result = result;
	}

	/** Gets result value. */
	public V getResult() {
		return m_result;
	}

	// --- Runnable implementation ---

	/** Run method.
	 *  Derived types should override call() rather than this method.
	 */
	@Override
	public void run() {
		m_done = false;
		setResult(call());
		m_done = true;
		synchronized (this) {
			notifyAll();
		}
	}

	// --- Callable implementation ---

	/** Method that returns the value.
	 *  Default implementation returns null.
	 */
	@Override
	public V call() {
		return null;
	}

	// --- methods ---

	/** Waits for result and returns it. */
	public V waitForResult() {
		return waitForResult(0, null);
	}

	/** Waits for result and returns it. */
	public V waitForResult(long timeout) {
		return waitForResult(timeout, null);
	}

	/** Waits for result and returns it.
	 *  Returns null if specified timeout is exceeded.
	 */
	public V waitForResult(long timeout, V defaultValue) {
		V result = defaultValue;
		try {
			if (timeout == 0) {
				// wait forever
				// (guard against spurious thread wakeup -- see wait() Javadoc)
				while (!m_done) {
					synchronized (this) {
						this.wait(0);
					}
				}
			} else {
				// wait until specified timeout
				// (guard against spurious thread wakeup -- see wait() Javadoc)
				long then = now();
				long waitstep = timeout / 10;
				if (waitstep == 0)
					waitstep = 1;
				do {
					synchronized (this) {
						this.wait(waitstep);
					}
				} while (!m_done && ((now() - then) < timeout));
			}
		} catch (InterruptedException e) {
		}
		if (m_done)
			result = getResult();
		return result;
	}

	/** Returns current time in milliseconds. */
	protected static long now() {
		return System.currentTimeMillis();
	}
}
