| /******************************************************************************* |
| * 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()); |
| // } |
| // } |
| } |