/******************************************************************************* * Copyright (c) 2015, 2016 Wind River Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.tcf.core.concurrent; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.tcf.te.tcf.core.activator.CoreBundleActivator; /** * Utility class that governs the execution of an asynchronous operation. The class is agnostic on how * the operation is actually carried out. */ public class TCFOperationMonitor<T> { private static final long UNRESPONSIVE_TIMEOUT = 60*1000; private IStatus fStatus; private T fValue; private long fCheckedTime; private boolean fPropagateCancel; private List<IProgressMonitor> fProgressMonitors = new ArrayList<IProgressMonitor>(); /** * Create a new operation monitor. */ public TCFOperationMonitor() { this(true); } /** * @param propagateProgressMonitorCancel whether to cancel the operation when all the progress monitors * of the waiting callers are canceled. * @see #waitDone(IProgressMonitor) * @see #waitDone(IProgressMonitor, long) * @see #waitDone(IProgressMonitor, long, long) */ public TCFOperationMonitor(boolean propagateProgressMonitorCancel) { fPropagateCancel = propagateProgressMonitorCancel; fCheckedTime = System.currentTimeMillis(); } /** * Wait for the operation to finish, using default timeouts. */ public IStatus waitDone(IProgressMonitor monitor) { return waitDone(monitor, Long.MAX_VALUE, UNRESPONSIVE_TIMEOUT); } /** * Wait for the operation to finish, using the specified timeout. * @param timeoutms maximum time to wait for the result to be set. When this timeout expires * {@link Status#CANCEL_STATUS} is returned. */ public IStatus waitDone(IProgressMonitor monitor, long timeoutms) { return waitDone(monitor, timeoutms, UNRESPONSIVE_TIMEOUT); } /** * Wait for the operation to finish, using the specified timeouts. * @param timeoutms maximum time to wait for the result to be set. When this timeout expires * {@link Status#CANCEL_STATUS} is returned. * @param unresponsiveTimeoutms maximum time the executing task is allowed not to check for * cancellation. When this timeout expires {@link Status#CANCEL_STATUS} is returned. */ public synchronized IStatus waitDone(IProgressMonitor monitor, long timeoutms, long unresponsiveTimeoutms) { if (monitor == null) monitor = new NullProgressMonitor(); fProgressMonitors.add(monitor); try { long startTime = System.currentTimeMillis(); while (true) { if (fStatus != null) { return fStatus; } if (monitor.isCanceled()) { return cancelWait(monitor); } long time = System.currentTimeMillis(); if (time - startTime > timeoutms || time - fCheckedTime > unresponsiveTimeoutms) { return cancelWait(monitor); } try { wait(100); } catch (InterruptedException e) { return cancelWait(monitor); } } } finally { fProgressMonitors.remove(monitor); } } private IStatus cancelWait(IProgressMonitor monitor) { if (fPropagateCancel && fProgressMonitors.size() == 1) { fStatus = Status.CANCEL_STATUS; } return Status.CANCEL_STATUS; } /** * Returns the status of the operation, or <code>null</code> when the operation is still in progress. */ public IStatus getStatus() { return fStatus; } /** * Returns the value of this result object, or <code>null</code> when it was not set. */ public T getValue() { return fValue; } /** * Return whether a thread is waiting for the completion of the operation. */ public synchronized boolean hasWaiters() { return !fProgressMonitors.isEmpty(); } /** * Method for reporting the completion of the operation. */ public synchronized IStatus setDone(IStatus status, T result) { if (fStatus == null) { fStatus = status; fValue = result; notifyAll(); } return fStatus; } /** * Method for reporting the successful completion of the operation. */ public IStatus setDone(T result) { return setDone(Status.OK_STATUS, result); } /** * Method for reporting the completion of the operation with an error. */ public IStatus setError(String msg, Throwable th) { return setDone(createStatus(msg, th), null); } /** * Method for reporting the completion of the operation with an error. */ public IStatus setError(IStatus status) { return setDone(status, null); } private IStatus createStatus(String msg, Throwable th) { if (th != null) { String msg2= th.getLocalizedMessage(); if (msg2 != null) { msg = msg == null ? msg2 : msg + ": " + msg2; //$NON-NLS-1$ } } return new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), msg, th); } /** * Method for reporting cancellation of the operation. */ public IStatus setCancelled() { return setDone(Status.CANCEL_STATUS, null); } /** * Method for checking whether the operation has been cancelled. Calling this method indicates * that the operation is still responsive. */ public synchronized boolean checkCancelled() { if (fStatus != null) { return true; } fCheckedTime = System.currentTimeMillis(); return false; } }