blob: 2d2363f44abd340acd598846360178c38218631e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 Wind River Systems and others.
* 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.concurrent;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.core.runtime.Platform;
/**
* Instrumented base class for
* <ul>
* <li>Runnable/Callable objects that are to be submitted to a DsfExecutor
* <li>objects that have a primary execution method (resembling
* <code>Runnable.run</code>) and that tend to be exercised from a DSF Executor
* and/or that submit work to a DSF executor.
* </ul>
*
* <p>
* Derivative classes benefit from additional fields that can be of help when
* debugging a DSF session. Derivatives that implement Runnable/Callable and are
* fed to DSF executors additionally benefit from tracing (when turned on by the
* user). A trace message is generated when the Runnable/Callable is submitted
* to the DsfExecutor.
*
* <p>
* Note that DSF executors need not be fed instances of this type. It is
* perfectly fine for clients to call the DSF executor with a plain vanilla
* Runnable/Callable, but such objects will obviously not benefit from the
* instrumentation.
*
* <p>
* When this base class is used to instrument a Runnable/Callable that is
* destined for a DSF executor, no additional work is imposed on the derived
* class. In all other cases, the subclass is responsible for calling
* {@link #setSubmitted()} from its primary execution method (e.g.,
* {@link RequestMonitor#done()}
*
* All fields and methods in this class are for tracing and debugging purposes
* only.
*
* @since 1.0
*/
@ThreadSafe
public class DsfExecutable {
/**
* Flag indicating that tracing of the DSF executor is enabled. It enables
* storing of the "creator" information as well as tracing of disposed
* runnables that have not been submitted to the executor.
*/
static boolean DEBUG_EXECUTOR = false;
/**
* Flag indicating that monitor objects should be instrumented. A monitor is
* an object that is usually constructed as an anonymous inner classes and
* is used when making an asynchronous call--one that needs to return some
* result or at least notify its caller when it has completed. These objects
* usually end up getting chained together at runtime, forming what is
* effectively a very disjointed code path. When this trace option is
* enabled, these objects are given a String field at construction time that
* contains the instantiation backtrace. This turns out to be a fairly
* dependable alternative to the standard program stack trace, which is of
* virtually no help when debugging asynchronous, monitor-assisted code.
*/
static boolean DEBUG_MONITORS = false;
/**
* Flag indicating that assertions are enabled. It enables storing of the
* "creator" executable for debugging purposes.
*/
static boolean ASSERTIONS_ENABLED = false;
static {
assert (ASSERTIONS_ENABLED = true) == true;
DEBUG_EXECUTOR = DsfPlugin.DEBUG && Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$
DEBUG_MONITORS = DsfPlugin.DEBUG && Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/monitors")); //$NON-NLS-1$
}
/**
* Stack trace indicating where this object was created.
*/
final StackTraceWrapper fCreatedAt;
/**
* If this object was created by a runnable/callable that was submitted to a
* DsfExecutor, this field holds a reference to its tracing wrapper.
* Otherwise, this field is null.
*/
final DefaultDsfExecutor.TracingWrapper fCreatedBy;
/**
* If this object is a Runnable/Callable, this flag indicates whether this
* object was ever submitted to a DsfExecutor for execution. If this is not
* a Runnable/Callable, then this indicates whether the primary execution
* method of the object was ever invoked. The subclass is required to
* explicitly call this method at the start of that primary method.
*/
private volatile boolean fSubmitted = false;
public DsfExecutable() {
// Use assertion flag (-ea) to jre to avoid affecting performance when not debugging.
if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR || DEBUG_MONITORS) {
// Find the runnable/callable that is currently running.
DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread());
if (executor != null) {
fCreatedBy = executor.fCurrentlyExecuting;
} else {
fCreatedBy = null;
}
// Get the stack trace and find the first method that is not a
// constructor of this object.
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
Class<?> thisClass = getClass();
Set<String> classNamesSet = new HashSet<String>();
while(thisClass != null) {
classNamesSet.add(thisClass.getName());
thisClass = thisClass.getSuperclass();
}
int i;
for (i = 3; i < stackTrace.length; i++) {
if ( !classNamesSet.contains(stackTrace[i].getClassName()) ) break;
}
fCreatedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - i]);
System.arraycopy(stackTrace, i, fCreatedAt.fStackTraceElements, 0, fCreatedAt.fStackTraceElements.length);
} else {
fCreatedAt = null;
fCreatedBy = null;
}
}
/**
* Was this object submitted for execution?
*
* See {@link #setSubmitted()}
*/
public boolean getSubmitted() {
return fSubmitted;
}
/**
* Mark that this object was submitted for execution.
*
* <p>
* More specifically, if this object is a runnable/callable, this method is
* called right before the execute/call method is invoked by a DSF executor.
* If the object is not a runnable/callable, then this method is called
* right before a DsfExecutor invokes a callable/runnable that will invoke
* the target method of this object.
*/
public void setSubmitted() {
fSubmitted = true;
}
/**
* Returns whether this object is always expected to be executed.
* We output a trace message if we are garbage collected without having been
* executed...that is unless this method returns false.
*
* Subclasses should override this method and return false if instances of
* it aren't meant to always be executed, thus avoiding unnecessary trace
* output.
*
* @return true if this object should always be executed
*/
protected boolean isExecutionRequired() {
return true;
}
// Bug 306982
// Disable the use of finalize() method in DSF runnables tracing to avoid
// a performance penalty in garbage collection.
//
// /**
// * Checks to see if the object was executed before being garbage collected.
// * If not, and it's expected to have been, then output a trace message to
// * that effect.
// *
// * @see java.lang.Object#finalize()
// */
// @Override
// protected void finalize() {
// if (DEBUG_EXECUTOR && !fSubmitted && isExecutionRequired()) {
// StringBuilder traceBuilder = new StringBuilder();
//
// // Record the time
// traceBuilder.append(DsfPlugin.getDebugTime());
// traceBuilder.append(' ');
//
// final String refstr = LoggingUtils.toString(this, false);
// traceBuilder.append("DSF executable was never executed: ").append(refstr); //$NON-NLS-1$
// final String tostr = LoggingUtils.trimTrailingNewlines(this.toString());
// if (!tostr.equals(refstr)) {
// traceBuilder.append(" ["); //$NON-NLS-1$
// traceBuilder.append(tostr);
// traceBuilder.append(']');
// }
// traceBuilder.append("\nCreated at:\n"); //$NON-NLS-1$
// traceBuilder.append(fCreatedAt);
//
// DsfPlugin.debug(traceBuilder.toString());
// }
// }
}