1
/*******************************************************************************
2
* Copyright (c) 2006, 2008 Wind River Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Wind River Systems - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.dsf.concurrent;
13
import java.io.ByteArrayOutputStream;
14
import java.io.IOException;
15
import java.io.PrintStream;
16
import java.util.HashMap;
18
import java.util.concurrent.Callable;
19
import java.util.concurrent.Future;
20
import java.util.concurrent.ScheduledFuture;
21
import java.util.concurrent.ScheduledThreadPoolExecutor;
22
import java.util.concurrent.ThreadFactory;
23
import java.util.concurrent.TimeUnit;
25
import org.eclipse.cdt.dsf.internal.DsfPlugin;
26
import org.eclipse.core.runtime.ILog;
27
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.core.runtime.Platform;
29
import org.eclipse.core.runtime.Status;
32
* Default implementation of a DSF executor interfaces, based on the
33
* standard java.util.concurrent.ThreadPoolExecutor.
38
public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
39
implements DsfExecutor
42
* Instance counter for DSF executors. Used in the executor's thread name.
44
private static int fgInstanceCounter = 0;
47
* Name of the executor, used in the executor's thread name.
52
* Instance number of this executor, used with the executor name.
54
private int fInstanceNumber;
56
/** Thread factory that creates the single thread to be used for this executor */
57
static class DsfThreadFactory implements ThreadFactory {
58
private String fThreadName;
59
DsfThreadFactory(String name) {
64
public Thread newThread(Runnable r) {
65
assert fThread == null; // Should be called only once.
66
fThread = new Thread(new ThreadGroup(fThreadName), r, fThreadName, 0);
71
public DefaultDsfExecutor() {
72
this("DSF Executor"); //$NON-NLS-1$
76
* Creates a new DSF Executor with the given name.
77
* @param name Name used to create executor's thread.
79
public DefaultDsfExecutor(String name) {
80
super(1, new DsfThreadFactory(name + " - " + fgInstanceCounter)); //$NON-NLS-1$
82
fInstanceNumber = fgInstanceCounter++;
84
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
85
// If tracing, pre-start the dispatch thread, and add it to the map.
86
prestartAllCoreThreads();
87
fThreadToExecutorMap.put(((DsfThreadFactory)getThreadFactory()).fThread, DefaultDsfExecutor.this);
91
public boolean isInExecutorThread() {
92
return Thread.currentThread().equals( ((DsfThreadFactory)getThreadFactory()).fThread );
95
protected String getName() {
99
static void logException(Throwable t) {
100
DsfPlugin plugin = DsfPlugin.getDefault();
101
if (plugin == null) return;
103
ILog log = plugin.getLog();
106
IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Uncaught exception in DSF executor thread", t)); //$NON-NLS-1$
108
// Print out the stack trace to console if assertions are enabled.
109
if(ASSERTIONS_ENABLED) {
110
ByteArrayOutputStream outStream = new ByteArrayOutputStream(512);
111
PrintStream printStream = new PrintStream(outStream);
113
printStream.write("Uncaught exception in session executor thread: ".getBytes()); //$NON-NLS-1$
114
} catch (IOException e2) {}
115
t.printStackTrace(new PrintStream(outStream));
116
System.err.println(outStream.toString());
121
// Utilities used for tracing.
123
protected static boolean DEBUG_EXECUTOR = false;
124
protected static String DEBUG_EXECUTOR_NAME = ""; //$NON-NLS-1$
125
protected static boolean ASSERTIONS_ENABLED = false;
127
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
128
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$
129
DEBUG_EXECUTOR_NAME = DsfPlugin.DEBUG
130
? Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executorName") : ""; //$NON-NLS-1$ //$NON-NLS-2$
131
assert (ASSERTIONS_ENABLED = true) == true;
135
* This map is used by DsfRunnable/Query/DsfCallable to track by which executor
136
* an executable object was created.
137
* <br>Note: Only used when tracing.
139
static Map<Thread, DefaultDsfExecutor> fThreadToExecutorMap = new HashMap<Thread, DefaultDsfExecutor>();
142
* Currently executing runnable/callable.
143
* <br>Note: Only used when tracing.
145
TracingWrapper fCurrentlyExecuting;
148
* Counter number saved by each tracing runnable when executed
149
* <br>Note: Only used when tracing.
151
int fSequenceCounter;
154
* Wrapper for runnables/callables, is used to store tracing information
155
* <br>Note: Only used when tracing.
157
abstract class TracingWrapper {
158
/** Sequence number of this runnable/callable */
159
int fSequenceNumber = -1;
161
/** Trace of where the runnable/callable was submitted to the executor */
162
StackTraceWrapper fSubmittedAt = null;
164
/** Reference to the runnable/callable that submitted this runnable/callable to the executor */
165
TracingWrapper fSubmittedBy = null;
168
* @param offset the number of items in the stack trace not to be printed
170
TracingWrapper(int offset) {
171
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
172
// guard against the offset being greater than the stack trace
173
offset = Math.min(offset, stackTrace.length);
174
fSubmittedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - offset]);
175
System.arraycopy(stackTrace, offset - 1, fSubmittedAt.fStackTraceElements, 0, fSubmittedAt.fStackTraceElements.length);
176
if (isInExecutorThread() && fCurrentlyExecuting != null) {
177
fSubmittedBy = fCurrentlyExecuting;
181
void traceExecution() {
182
fSequenceNumber = fSequenceCounter++;
183
fCurrentlyExecuting = this;
185
// Write to console only if tracing is enabled (as opposed to tracing or assertions).
186
if (DEBUG_EXECUTOR && ("".equals(DEBUG_EXECUTOR_NAME) || fName.equals(DEBUG_EXECUTOR_NAME))) { //$NON-NLS-1$
187
StringBuilder traceBuilder = new StringBuilder();
190
traceBuilder.append(DsfPlugin.getDebugTime());
191
traceBuilder.append(' ');
193
// Record the executor #
194
traceBuilder.append('#');
195
traceBuilder.append(fSequenceNumber);
197
// Record the executor name
198
traceBuilder.append('(');
199
traceBuilder.append(fName);
200
traceBuilder.append(" - "); //$NON-NLS-1$
201
traceBuilder.append(fInstanceNumber);
202
traceBuilder.append(')');
203
traceBuilder.append(' ');
205
// Append executable class name
206
traceBuilder.append(getExecutable().getClass().getName());
208
// Add executable's toString().
209
traceBuilder.append("\n "); //$NON-NLS-1$
210
traceBuilder.append(getExecutable().toString());
212
// Append "create by" info.
213
if (getExecutable() instanceof DsfExecutable) {
214
DsfExecutable dsfExecutable = (DsfExecutable)getExecutable();
215
if (dsfExecutable.fCreatedAt != null || dsfExecutable.fCreatedBy != null) {
216
traceBuilder.append("\n created "); //$NON-NLS-1$
217
if (dsfExecutable.fCreatedBy != null) {
218
traceBuilder.append(" by #"); //$NON-NLS-1$
219
traceBuilder.append(dsfExecutable.fCreatedBy.fSequenceNumber);
221
if (dsfExecutable.fCreatedAt != null) {
222
traceBuilder.append("\n at "); //$NON-NLS-1$
223
traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[0].toString());
224
for (int i = 1; i < dsfExecutable.fCreatedAt.fStackTraceElements.length && i < 3; i++) {
225
traceBuilder.append("\n "); //$NON-NLS-1$
226
traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[i].toString());
233
traceBuilder.append("\n submitted"); //$NON-NLS-1$
234
if (fSubmittedBy != null) {
235
traceBuilder.append(" by #"); //$NON-NLS-1$
236
traceBuilder.append(fSubmittedBy.fSequenceNumber);
238
traceBuilder.append("\n at "); //$NON-NLS-1$
239
traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString());
240
for (int i = 1; i < fSubmittedAt.fStackTraceElements.length && i < 3; i++) {
241
traceBuilder.append("\n "); //$NON-NLS-1$
242
traceBuilder.append(fSubmittedAt.fStackTraceElements[i].toString());
244
traceBuilder.append(" at "); //$NON-NLS-1$
245
traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString());
247
// Finally write out to console
248
DsfPlugin.debug(traceBuilder.toString());
252
abstract protected Object getExecutable();
256
class TracingWrapperRunnable extends TracingWrapper implements Runnable {
257
final Runnable fRunnable;
258
public TracingWrapperRunnable(Runnable runnable, int offset) {
260
if (runnable == null) throw new NullPointerException();
261
fRunnable = runnable;
263
// Check if executable wasn't executed already.
264
if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) {
265
assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$
266
((DsfExecutable)fRunnable).setSubmitted();
271
protected Object getExecutable() { return fRunnable; }
276
// Finally invoke the runnable code.
279
} catch (RuntimeException e) {
280
// If an exception was thrown in the Runnable, trace it.
281
// Because there is no one else to catch it, it is a
282
// programming error.
289
public class TracingWrapperCallable<T> extends TracingWrapper implements Callable<T> {
290
final Callable<T> fCallable;
291
public TracingWrapperCallable(Callable<T> callable, int offset) {
293
if (callable == null) throw new NullPointerException();
294
fCallable = callable;
298
protected Object getExecutable() { return fCallable; }
300
public T call() throws Exception {
303
// Finally invoke the runnable code.
304
// Note that callables can throw exceptions that can be caught
305
// by clients that invoked them using ExecutionException.
306
return fCallable.call();
311
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
312
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
313
if ( !(callable instanceof TracingWrapper) ) {
314
callable = new TracingWrapperCallable<V>(callable, 6);
317
return super.schedule(callable, delay, unit);
320
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
321
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
322
if ( !(command instanceof TracingWrapper) ) {
323
command = new TracingWrapperRunnable(command, 6);
326
return super.schedule(command, delay, unit);
330
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
331
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
332
command = new TracingWrapperRunnable(command, 6);
334
return super.scheduleAtFixedRate(command, initialDelay, period, unit);
338
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
339
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
340
command = new TracingWrapperRunnable(command, 6);
342
return super.scheduleWithFixedDelay(command, initialDelay, delay, unit);
346
public void execute(Runnable command) {
347
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
348
command = new TracingWrapperRunnable(command, 6);
350
super.execute(command);
354
public Future<?> submit(Runnable command) {
355
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
356
command = new TracingWrapperRunnable(command, 6);
358
return super.submit(command);
362
public <T> Future<T> submit(Callable<T> callable) {
363
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
364
callable = new TracingWrapperCallable<T>(callable, 6);
366
return super.submit(callable);
370
public <T> Future<T> submit(Runnable command, T result) {
371
if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) {
372
command = new TracingWrapperRunnable(command, 6);
374
return super.submit(command, result);