Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: f07026c03bcc055a11428e3e94d565815af45731 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
/*******************************************************************************
 * Copyright (c) 2011 Wind River Systems, Inc. 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.tcf.te.runtime.concurrent.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.core.runtime.Assert;
import org.eclipse.tcf.te.runtime.concurrent.Executors;
import org.eclipse.tcf.te.runtime.concurrent.interfaces.IExecutorUtilDelegate;
import org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor;
import org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor;
import org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager;
import org.eclipse.tcf.te.runtime.extensions.ExecutableExtensionProxy;
import org.eclipse.tcf.te.runtime.interfaces.IConditionTester;


/**
 * Utility class to provide helper methods to execute tasks at
 * a executor service asynchronous and synchronous.
 */
public final class ExecutorsUtil {

	/**
	 * Execution utility wait and dispatch utility extension point manager.
	 */
	protected static class ExecutorUtilDelegateExtensionPointManager extends AbstractExtensionPointManager<IExecutorUtilDelegate> {

		/* (non-Javadoc)
		 * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getExtensionPointId()
		 */
		@Override
		protected String getExtensionPointId() {
			return "org.eclipse.tcf.te.runtime.concurrent.executorUtilDelegates"; //$NON-NLS-1$
		}

		/* (non-Javadoc)
		 * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getConfigurationElementName()
		 */
		@Override
		protected String getConfigurationElementName() {
			return "executorUtilDelegate"; //$NON-NLS-1$
		}

		/**
		 * Returns the list of all contributed executor utility delegates.
		 *
		 * @return The list of contributed executor utility delegates, or an
		 *         empty array.
		 */
		public IExecutorUtilDelegate[] getExecutorUtilDelegates() {
			List<IExecutorUtilDelegate> contributions = new ArrayList<IExecutorUtilDelegate>();
			Collection<ExecutableExtensionProxy<IExecutorUtilDelegate>> proxies = getExtensions().values();
			for (ExecutableExtensionProxy<IExecutorUtilDelegate> proxy : proxies) {
				if (proxy.getInstance() != null&& !contributions.contains(proxy.getInstance())) {
					contributions.add(proxy.getInstance());
				}
			}

			return contributions.toArray(new IExecutorUtilDelegate[contributions.size()]);
		}

		/**
		 * Returns the executor utility delegate identified by its unique id. If
		 * no executor utility delegate with the specified id is registered,
		 * <code>null</code> is returned.
		 *
		 * @param id
		 *            The unique id of the executor utility delegate. Must not
		 *            be <code>null</code>
		 * @param newInstance
		 *            Specify <code>true</code> to get a new executor utility
		 *            delegate instance, <code>false</code> otherwise.
		 *
		 * @return The executor instance or <code>null</code>.
		 */
		public IExecutorUtilDelegate getExecutorUtilDelegate(String id, boolean newInstance) {
			Assert.isNotNull(id);

			IExecutorUtilDelegate executorUtilDelegate = null;
			if (getExtensions().containsKey(id)) {
				ExecutableExtensionProxy<IExecutorUtilDelegate> proxy = getExtensions().get(id);
				// Get the extension instance
				executorUtilDelegate = newInstance ? proxy.newInstance() : proxy.getInstance();
			}

			return executorUtilDelegate;
		}
	}

	// Reference to the executor service extension point manager
	private final static ExecutorUtilDelegateExtensionPointManager EXTENSION_POINT_MANAGER = new ExecutorUtilDelegateExtensionPointManager();

	// Reference to the used executor service.
	private final static ISingleThreadedExecutor EXECUTOR;
	// Reference to the used UI executor service (might be null if not available)
	private final static ISingleThreadedExecutor UI_EXECUTOR;

	/**
	 * Static constructor.
	 */
	static {
		EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tcf.te.runtime.concurrent.executors.singleThreaded"); //$NON-NLS-1$
		Assert.isNotNull(EXECUTOR);
		UI_EXECUTOR = (ISingleThreadedExecutor) Executors.getSharedExecutor("org.eclipse.tcf.te.ui.executors.SWTDisplay"); //$NON-NLS-1$
	}

	/**
	 * Shutdown the executor service used.
	 */
	public static void shutdown() {
		if (EXECUTOR instanceof ExecutorService) {
			((ExecutorService) EXECUTOR).shutdown();
		}
		if (UI_EXECUTOR instanceof ExecutorService) {
			((ExecutorService) UI_EXECUTOR).shutdown();
		}
	}

	/**
	 * Checks if the current thread is identical with the executor thread.
	 *
	 * @return <code>True</code> if the current thread is the executor thread,
	 *         <code>false</code> otherwise.
	 */
	public static boolean isExecutorThread() {
		return EXECUTOR != null ? EXECUTOR.isExecutorThread() : false;
	}

	/**
	 * Checks if the current thread is identical with the UI executor thread.
	 *
	 * @return <code>True</code> if the current thread is the UI executor
	 *         thread, <code>false</code> otherwise.
	 */
	public static boolean isUIExecutorThread() {
		return UI_EXECUTOR != null ? UI_EXECUTOR.isExecutorThread() : false;
	}

	/**
	 * Schedule the given {@link Runnable} for invocation within the used
	 * executor thread.
	 *
	 * @param runnable
	 *            The <code>java.lang.Runnable</code> to execute within the
	 *            executor thread.
	 */
	public static void execute(Runnable runnable) {
		if (runnable != null) {
			if (EXECUTOR instanceof ExecutorService) {
				if (!((ExecutorService) EXECUTOR).isShutdown()
						&& !((ExecutorService) EXECUTOR).isTerminated()) {
					EXECUTOR.execute(runnable);
				}
			} else {
				EXECUTOR.execute(runnable);
			}
		}
	}


	/**
	 * Schedule the given {@link Runnable} for invocation within the used
	 * executor thread and blocks the caller until the runnable got executed.
	 * <p>
	 * <b>Note:</b> The method is using {@link #wait()} to block the calling
	 *              thread. Therefore the method cannot be called from within
	 *              the executor thread itself.
	 *
	 * @param runnable
	 *            The <code>java.lang.Runnable</code> to execute within the
	 *            executor thread.
	 */
	public static void executeWait(final Runnable runnable) {
		Assert.isTrue(!EXECUTOR.isExecutorThread());
		if (runnable == null) return;

		final AtomicBoolean invoked = new AtomicBoolean(false);

		// Wrap the original runnable in another runnable
		// to notify ourself
		Runnable r = new Runnable() {
			@Override
			public void run() {
				try {
					runnable.run();
				} finally {
					invoked.set(true);
					synchronized(runnable) {
						runnable.notifyAll();
					}
				}
			}
		};

		if (EXECUTOR instanceof ExecutorService) {
			if (!((ExecutorService) EXECUTOR).isShutdown()
					&& !((ExecutorService) EXECUTOR).isTerminated()) {
				EXECUTOR.execute(r);
			}
		} else {
			EXECUTOR.execute(r);
		}

		synchronized(runnable) {
			try {
				if (!invoked.get()) runnable.wait();
			} catch (InterruptedException e) {
				/* ignored on purpose */
			}
		}
	}

	/**
	 * Schedule the given {@link Runnable} to run the current platform display
	 * thread and blocks the caller until the runnable got executed.
	 *
	 * @param runnable
	 *            The <code>java.lang.Runnable</code> to execute within the
	 *            UI thread.
	 */
	public static void executeInUI(Runnable runnable) {
		if (runnable != null) {
			if (UI_EXECUTOR instanceof ExecutorService) {
				if (!((ExecutorService) UI_EXECUTOR).isShutdown()
						&& !((ExecutorService) UI_EXECUTOR).isTerminated()) {
					UI_EXECUTOR.execute(runnable);
				}
			} else {
				if (UI_EXECUTOR != null) {
					UI_EXECUTOR.execute(runnable);
				}
			}
		}
	}

	/**
	 * Schedule the given {@link Runnable} to run the current platform display
	 * thread and blocks the caller until the runnable got executed.
	 *
	 * @param runnable
	 *            The <code>java.lang.Runnable</code> to execute within the
	 *            UI thread.
	 */
	public static void executeInUIWait(final Runnable runnable) {
		if (runnable == null) return;

		final AtomicBoolean invoked = new AtomicBoolean(false);

		// Wrap the original runnable in another runnable
		// to set the invoked flag
		Runnable r = new Runnable() {
			@Override
			public void run() {
				try {
					runnable.run();
				} finally {
					invoked.set(true);
				}
			}
		};

		if (UI_EXECUTOR instanceof ExecutorService) {
			if (!((ExecutorService) UI_EXECUTOR).isShutdown()
					&& !((ExecutorService) UI_EXECUTOR).isTerminated()) {
				UI_EXECUTOR.execute(r);
			}
		} else {
			if (UI_EXECUTOR != null) {
				UI_EXECUTOR.execute(r);
			} else {
				invoked.set(true);
			}
		}

		waitAndExecute(0, new IConditionTester() {
			@Override
			public boolean isConditionFulfilled() {
				return invoked.get();
			}
			@Override
			public void cleanup() {
			}
		});
	}

	/**
	 * Waits either for the given condition tester to signal that the condition,
	 * the caller want's to wait for, has been completely fulfilled or till the
	 * timeout runs out. If the specified condition tester is <code>null</code>,
	 * the method will always wait till the timeout occurs. In case
	 * <code>timeout == 0</code> and <code>conditionTester == null</code>, the
	 * method returns immediately with the return value <code>true</code>!
	 *
	 * @param timeout
	 *            The timeout to wait in milliseconds. <code>0</code> means
	 *            infinite wait time!
	 * @param conditionTester
	 *            The condition tester to use for checking the interrupt
	 *            condition.
	 *
	 * @return <code>false</code> if the exit reason if that the waiting
	 *         condition has been fulfilled, <code>true</code> if the exit
	 *         reason is the timeout!
	 */
	public static boolean waitAndExecute(final long timeout, final IConditionTester conditionTester) {
		// both parameter are null, return immediately!
		if (conditionTester == null && timeout == 0)
			return true;

		// we assume that the exit reason will be the timeout
		boolean exitReason = true;

		// Remember the executors utility delegate down the road. As long
		// we don't leave the waitAndExecute method, the thread cannot change.
		IExecutorUtilDelegate lastDelegate = null;

		// Remember the start time to calculate the timeout
		final long startTime = System.currentTimeMillis();
		// keep going till either the condition tester or the timeout will
		// break the loop!
		while (true) {
			if (conditionTester != null && conditionTester.isConditionFulfilled()) {
				// the exit reason is the condition tester!
				exitReason = false;
				break;
			}
			if (timeout != 0 && ((System.currentTimeMillis() - startTime) >= timeout)) {
				// timeout occurred, just break the loop
				break;
			}
			// none of the break conditions are fulfilled, so wait a little bit
			// before testing again.
			if (isExecutorThread()) {
				// We are in the executor thread. Keep the command dispatching running.
				if (EXECUTOR instanceof INestableExecutor) {
					((INestableExecutor) EXECUTOR).readAndExecute();
					Thread.yield();
				} else {
					throw new IllegalStateException("waitAndExecute called from within a non-nestable executor service!"); //$NON-NLS-1$
				}
			}
			// Check if we are in the UI executor thread
			else if (isUIExecutorThread()) {
				// We are in the executor thread. Keep the command dispatching
				// running.
				if (UI_EXECUTOR instanceof INestableExecutor) {
					((INestableExecutor) UI_EXECUTOR).readAndExecute();
					Thread.yield();
				} else {
					throw new IllegalStateException("waitAndExecute called from within a non-nestable UI executor service!"); //$NON-NLS-1$
				}
			}
			// Check if we have a delegate contribution which is handling
			// the current thread.
			else {
				boolean foundHandlingDelegate = false;

				if (lastDelegate == null) {
					// Get all registered delegates
					IExecutorUtilDelegate[] delegates = EXTENSION_POINT_MANAGER.getExecutorUtilDelegates();
					for (IExecutorUtilDelegate delegate : delegates) {
						// Does the delegate handles the current thread?
						if (delegate.isHandledExecutorThread()) {
							foundHandlingDelegate = true;
							lastDelegate = delegate;
							// Read and dispatch one event
							delegate.readAndDispatch();
							break;
						}
					}
				} else {
					foundHandlingDelegate = true;
					// Read and dispatch one event
					lastDelegate.readAndDispatch();
				}

				if (!foundHandlingDelegate) {
					// Not in any executor thread, put the current thread to sleep
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) { /* ignored on purpose */ }
				}
			}
		}

		// give the condition tester the chance to cleanup
		if (conditionTester != null) {
			conditionTester.cleanup();
		}

		return exitReason;
	}
}

Back to the top