Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 96fb9016207fbd26a67f0fa39cea8fa26e2238d8 (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
/*******************************************************************************
 * Copyright (c) 2013 Google, Inc and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * 	   Sergey Prigogin (Google) - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.ui;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;

import org.eclipse.cdt.ui.CUIPlugin;

/**
 * Synchronously executes a {@link Job} while allowing user to cancel it if it takes too long.
 */
public final class BusyCursorJobRunner {
	/**
	 * Adapts a {@link Job} to be an {@link IRunnableWithProgress}.
	 */
	private static class JobRunnableWithProgressAdapter implements IRunnableWithProgress {
		private final Job job;

		/**
		 * Creates the {@link IRunnableWithProgress} from the {@link Job}.
		 */
		public JobRunnableWithProgressAdapter(Job job) {
			this.job = job;
		}

		@Override
		public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
			IStatus result;
			try {
				monitor.beginTask(job.getName(), IProgressMonitor.UNKNOWN);
				result = executeAndWait(job, monitor);
			} catch (RuntimeException e) {
				throw new InvocationTargetException(e);
			}

			switch (result.getSeverity()) {
			case IStatus.CANCEL:
				throw new InterruptedException();

			case IStatus.ERROR:
				if (result.getException() instanceof OperationCanceledException) {
					throw new InterruptedException();
				}
				throw new InvocationTargetException(new CoreException(result));
			}
		}
	}

	/**
	 * Runs the given job and waits for it to finish. If executing in the UI thread, sets the cursor
	 * to busy while the job is being executed.
	 *
	 * @param job the job to execute
	 * @return the status reflecting the result of the job execution
	 */
	public static IStatus execute(Job job) {
		boolean inUiThread = Thread.currentThread() == Display.getDefault().getThread();
		if (inUiThread) {
			return busyCursorWhile(job);
		}
		return executeAndWait(job, new NullProgressMonitor());
	}

	private static IStatus busyCursorWhile(Job job) {
		try {
			IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
			progressService.busyCursorWhile(new JobRunnableWithProgressAdapter(job));
		} catch (InterruptedException e) {
			return Status.CANCEL_STATUS; // Operation was cancelled.
		} catch (InvocationTargetException e) {
			Throwable targetException = e.getTargetException();
			if (targetException instanceof CoreException) {
				return ((CoreException) targetException).getStatus();
			}
			return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, e.getMessage(), e);
		}
		return Status.OK_STATUS;
	}

	private static IStatus executeAndWait(final Job job, IProgressMonitor monitor) {
		final IStatus[] statusHolder = new IStatus[1];

		IJobManager jobManager = Job.getJobManager();
		JobChangeAdapter listener = new JobChangeAdapter() {
			@Override
			public void done(IJobChangeEvent event) {
				super.done(event);
				if (event.getJob() == job) {
					synchronized (statusHolder) {
						statusHolder[0] = event.getResult();
						statusHolder.notifyAll();
					}
				}
			}
		};
		jobManager.addJobChangeListener(listener);
		job.schedule();

		try {
			synchronized (statusHolder) {
				while (statusHolder[0] == null) {
					try {
						statusHolder.wait(100);
						if (monitor.isCanceled()) {
							job.cancel();
						}
					} catch (InterruptedException e) {
						job.cancel();
						return Status.CANCEL_STATUS;
					}
				}

				return statusHolder[0];
			}
		} finally {
			monitor.done();
			jobManager.removeJobChangeListener(listener);
		}
	}

	private BusyCursorJobRunner() {
	}
}

Back to the top