View | Details | Raw Unified | Return to bug 343256 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/core/internal/events/BuildManager.java (-5 / +16 lines)
Lines 14-24 Link Here
14
 *******************************************************************************/
14
 *******************************************************************************/
15
package org.eclipse.core.internal.events;
15
package org.eclipse.core.internal.events;
16
16
17
import org.eclipse.core.resources.IProject;
18
import org.eclipse.core.runtime.jobs.ISchedulingRule;
19
20
import org.eclipse.core.resources.IBuildConfiguration;
21
22
import java.util.*;
17
import java.util.*;
23
import org.eclipse.core.internal.dtree.DeltaDataTree;
18
import org.eclipse.core.internal.dtree.DeltaDataTree;
24
import org.eclipse.core.internal.resources.*;
19
import org.eclipse.core.internal.resources.*;
Lines 169-174 Link Here
169
			// For incremental builds, grab a pointer to the current state before computing the delta
164
			// For incremental builds, grab a pointer to the current state before computing the delta
170
			currentTree = ((trigger == IncrementalProjectBuilder.FULL_BUILD) || clean) ? null : workspace.getElementTree();
165
			currentTree = ((trigger == IncrementalProjectBuilder.FULL_BUILD) || clean) ? null : workspace.getElementTree();
171
			int depth = -1;
166
			int depth = -1;
167
			ISchedulingRule rule = null;
172
			try {
168
			try {
173
				//short-circuit if none of the projects this builder cares about have changed.
169
				//short-circuit if none of the projects this builder cares about have changed.
174
				if (!needsBuild(currentBuilder, trigger)) {
170
				if (!needsBuild(currentBuilder, trigger)) {
Lines 177-182 Link Here
177
					monitor.done();
173
					monitor.done();
178
					return;
174
					return;
179
				}
175
				}
176
				rule = builder.getRule(trigger, args);
180
				String name = currentBuilder.getLabel();
177
				String name = currentBuilder.getLabel();
181
				String message;
178
				String message;
182
				if (name != null)
179
				if (name != null)
Lines 185-197 Link Here
185
					message = NLS.bind(Messages.events_invoking_1, builder.getProject().getFullPath());
182
					message = NLS.bind(Messages.events_invoking_1, builder.getProject().getFullPath());
186
				monitor.subTask(message);
183
				monitor.subTask(message);
187
				hookStartBuild(builder, trigger);
184
				hookStartBuild(builder, trigger);
185
				// Make the current tree immutable before releasing the WS lock
186
				if (rule != null && currentTree != null)
187
					workspace.newWorkingTree();
188
				//release workspace lock while calling builders
188
				//release workspace lock while calling builders
189
				depth = getWorkManager().beginUnprotected();
189
				depth = getWorkManager().beginUnprotected();
190
				// Acquire the rule required for running this builder
191
				if (rule != null) {
192
					Job.getJobManager().beginRule(rule, monitor);
193
					// Now that we've acquired the rule, changes may have been made concurrently, ensure we're pointing at the 
194
					// correct currentTree so delta contains concurrent changes made in areas guarded by the scheduling rule
195
					if (currentTree != null)
196
						currentTree = workspace.getElementTree();
197
				}
190
				//do the build
198
				//do the build
191
				SafeRunner.run(getSafeRunnable(trigger, args, status, monitor));
199
				SafeRunner.run(getSafeRunnable(trigger, args, status, monitor));
192
			} finally {
200
			} finally {
201
				// Re-acquire the WS lock, then release the scheduling rule
193
				if (depth >= 0)
202
				if (depth >= 0)
194
					getWorkManager().endUnprotected(depth);
203
					getWorkManager().endUnprotected(depth);
204
				if (rule != null)
205
					Job.getJobManager().endRule(rule);
195
				// Be sure to clean up after ourselves.
206
				// Be sure to clean up after ourselves.
196
				if (clean || currentBuilder.wasForgetStateRequested()) {
207
				if (clean || currentBuilder.wasForgetStateRequested()) {
197
					currentBuilder.setLastBuiltTree(null);
208
					currentBuilder.setLastBuiltTree(null);
(-)src/org/eclipse/core/internal/resources/Workspace.java (-2 / +18 lines)
Lines 163-169 Link Here
163
	 * reads and concurrent writes, write access to the tree is governed
163
	 * reads and concurrent writes, write access to the tree is governed
164
	 * by {@link WorkManager}.
164
	 * by {@link WorkManager}.
165
	 */
165
	 */
166
	protected ElementTree tree;
166
	protected volatile ElementTree tree;
167
167
168
	/**
168
	/**
169
	 * This field is used to control access to the workspace tree during
169
	 * This field is used to control access to the workspace tree during
Lines 440-446 Link Here
440
	 */
440
	 */
441
	private void buildInternal(IBuildConfiguration[] configs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException {
441
	private void buildInternal(IBuildConfiguration[] configs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException {
442
		monitor = Policy.monitorFor(monitor);
442
		monitor = Policy.monitorFor(monitor);
443
		final ISchedulingRule rule = getRuleFactory().buildRule();
443
		// Bug 343256 use a relaxed scheduling rule if the config we're building uses a relaxed rule.
444
		// Otherwise fall-back to WR.
445
		boolean relaxed = false;
446
		if (Job.getJobManager().currentRule() == null && configs.length > 0) {
447
			relaxed = true;
448
			for (IBuildConfiguration config : configs) {
449
				ISchedulingRule requested = getBuildManager().getRule(config, trigger, null, null);
450
				if (requested != null && requested.contains(getRoot())) {
451
					relaxed = false;
452
					break;
453
				}
454
			}
455
		}
456
457
		// PRE + POST_BUILD, and the build itself are allowed to modify resources, so require the current thread's scheduling rule
458
		// to either contain the WR or be null. Therefore, if not null, ensure it contains the WR rule...
459
		final ISchedulingRule rule = relaxed ? null : getRuleFactory().buildRule();
444
		try {
460
		try {
445
			monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
461
			monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
446
			try {
462
			try {
(-)src/org/eclipse/core/tests/internal/builders/RelaxedSchedRuleBuilderTest.java (-3 / +268 lines)
Lines 10-22 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.core.tests.internal.builders;
11
package org.eclipse.core.tests.internal.builders;
12
12
13
import java.util.Map;
13
import java.io.ByteArrayInputStream;
14
import java.util.*;
14
import junit.framework.Test;
15
import junit.framework.Test;
15
import junit.framework.TestSuite;
16
import junit.framework.TestSuite;
16
import org.eclipse.core.resources.*;
17
import org.eclipse.core.resources.*;
17
import org.eclipse.core.runtime.*;
18
import org.eclipse.core.runtime.*;
18
import org.eclipse.core.runtime.jobs.ISchedulingRule;
19
import org.eclipse.core.runtime.jobs.*;
19
import org.eclipse.core.runtime.jobs.Job;
20
import org.eclipse.core.tests.harness.TestBarrier;
20
import org.eclipse.core.tests.harness.TestBarrier;
21
import org.eclipse.core.tests.internal.builders.TestBuilder.BuilderRuleCallback;
21
import org.eclipse.core.tests.internal.builders.TestBuilder.BuilderRuleCallback;
22
22
Lines 205-208 Link Here
205
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
205
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
206
	}
206
	}
207
207
208
	private HashSet<ISchedulingRule> getRulesAsSet(ISchedulingRule rule) {
209
		HashSet<ISchedulingRule> rules = new HashSet<ISchedulingRule>();
210
		if (rule == null)
211
			return rules;
212
		if (rule instanceof MultiRule)
213
			rules.addAll(Arrays.asList(((MultiRule) rule).getChildren()));
214
		else
215
			rules.add(rule);
216
		return rules;
217
	}
218
219
	/**
220
	 * As the builder is run with a relaxed scheduling rule, we ensure that any changes made before
221
	 * the build is actually run are present in the delta.
222
	 * Acquiring the scheduling rule must be done outside of the WS lock, so this tests that
223
	 * a change which sneaks in during the window or the build thread acquiring its scheduling 
224
	 * rule, is correctly present in the builder's delta.
225
	 * @throws Exception
226
	 */
227
	public void testBuilderDeltaUsingRelaxedRuleBug343256() throws Exception {
228
		String name = "testBuildDeltaUsingRelaxedRuleBug343256";
229
		setAutoBuilding(false);
230
		final IProject project = getWorkspace().getRoot().getProject(name);
231
		final IFile foo = project.getFile("foo");
232
		create(project, false);
233
234
		IProjectDescription desc = project.getDescription();
235
		desc.setBuildSpec(new ICommand[] {createCommand(desc, EmptyDeltaBuilder.BUILDER_NAME, "Project1Build1")});
236
		project.setDescription(desc, getMonitor());
237
238
		// Ensure the builder is instantiated
239
		project.build(IncrementalProjectBuilder.FULL_BUILD, getMonitor());
240
241
		final TestBarrier tb1 = new TestBarrier(TestBarrier.STATUS_WAIT_FOR_START);
242
243
		// Create a builder set a null scheduling rule
244
		EmptyDeltaBuilder builder = EmptyDeltaBuilder.getInstance();
245
246
		// Set the rule call-back
247
		builder.setRuleCallback(new BuilderRuleCallback() {
248
249
			boolean called = false;
250
251
			public ISchedulingRule getRule(String name, IncrementalProjectBuilder builder, int trigger, Map args) {
252
				// Remove once Bug 331187 is fixed.
253
				// Currently #getRule is called twice when building a specific build configuration (so as to minimized change in 
254
				// 3.7 end-game.  As this test is trying to provoke a bug in the window between fetching a rule and applying it
255
				// to the build, we don't want to run the first time #getRule is called (in Workspace#build)
256
				if (!called) {
257
					called = true;
258
					return project;
259
				}
260
				// REMOVE
261
				tb1.setStatus(TestBarrier.STATUS_START);
262
				tb1.waitForStatus(TestBarrier.STATUS_WAIT_FOR_RUN);
263
				try {
264
					// Give the resource modification time be queued
265
					Thread.sleep(10);
266
				} catch (InterruptedException e) {
267
					fail();
268
				}
269
				return project;
270
			}
271
272
			public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
273
				// shared scheduling rule
274
				assertTrue(Job.getJobManager().currentRule().equals(project));
275
				// assert that the delta contains the file foo
276
				IResourceDelta delta = getDelta(project);
277
				assertNotNull("1.1", delta);
278
				assertTrue("1.2", delta.getAffectedChildren().length == 1);
279
				assertTrue("1.3", delta.getAffectedChildren()[0].getResource().equals(foo));
280
				tb1.setStatus(TestBarrier.STATUS_DONE);
281
				return super.build(kind, args, monitor);
282
			}
283
		});
284
285
		// Run the incremental build
286
		Job j = new Job("IProject.build()") {
287
			protected IStatus run(IProgressMonitor monitor) {
288
				try {
289
					getWorkspace().build(new IBuildConfiguration[] {project.getActiveBuildConfig()}, IncrementalProjectBuilder.INCREMENTAL_BUILD, true, monitor);
290
				} catch (CoreException e) {
291
					fail();
292
				}
293
				return Status.OK_STATUS;
294
			}
295
		};
296
		j.schedule();
297
298
		// Wait for the build to transition to getRule
299
		tb1.waitForStatus(TestBarrier.STATUS_START);
300
		// Modify a file in the project
301
		j = new Job("IProject.build()") {
302
			protected IStatus run(IProgressMonitor monitor) {
303
				tb1.setStatus(TestBarrier.STATUS_WAIT_FOR_RUN);
304
				ensureExistsInWorkspace(foo, new ByteArrayInputStream(new byte[0]));
305
				return Status.OK_STATUS;
306
			}
307
		};
308
		j.schedule();
309
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
310
	}
311
312
	/**
313
	 * Tests for regression in running the build with reduced scheduling rules.
314
	 * @throws Exception
315
	 */
316
	public void testBug343256() throws Exception {
317
		String name = "testBug343256";
318
		setAutoBuilding(false);
319
		final IProject project = getWorkspace().getRoot().getProject(name);
320
		create(project, false);
321
322
		IProjectDescription desc = project.getDescription();
323
		desc.setBuildSpec(new ICommand[] {createCommand(desc, EmptyDeltaBuilder.BUILDER_NAME, "Project1Build1"), createCommand(desc, EmptyDeltaBuilder2.BUILDER_NAME, "Project1Build2")});
324
		project.setDescription(desc, getMonitor());
325
326
		// Ensure the builder is instantiated
327
		project.build(IncrementalProjectBuilder.CLEAN_BUILD, getMonitor());
328
329
		final TestBarrier tb1 = new TestBarrier(TestBarrier.STATUS_WAIT_FOR_START);
330
		final TestBarrier tb2 = new TestBarrier(TestBarrier.STATUS_WAIT_FOR_START);
331
332
		// Scheduling rules to returng from #getRule
333
		final ISchedulingRule[] getRules = new ISchedulingRule[2];
334
		final ISchedulingRule[] buildRules = new ISchedulingRule[2];
335
336
		// Create a builder set a null scheduling rule
337
		EmptyDeltaBuilder builder = EmptyDeltaBuilder.getInstance();
338
		EmptyDeltaBuilder2 builder2 = EmptyDeltaBuilder2.getInstance();
339
340
		// Set the rule call-back
341
		builder.setRuleCallback(new BuilderRuleCallback() {
342
			public ISchedulingRule getRule(String name, IncrementalProjectBuilder builder, int trigger, Map args) {
343
				tb1.waitForStatus(TestBarrier.STATUS_START);
344
				return getRules[0];
345
			}
346
347
			public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
348
				HashSet<ISchedulingRule> h1 = getRulesAsSet(Job.getJobManager().currentRule());
349
				HashSet<ISchedulingRule> h2 = getRulesAsSet(buildRules[0]);
350
				// shared scheduling rule
351
				assertTrue(h1.equals(h2));
352
				tb1.setStatus(TestBarrier.STATUS_DONE);
353
				return super.build(kind, args, monitor);
354
			}
355
		});
356
		// Set the rule call-back
357
		builder2.setRuleCallback(new BuilderRuleCallback() {
358
			public ISchedulingRule getRule(String name, IncrementalProjectBuilder builder, int trigger, Map<String, String> args) {
359
				tb2.waitForStatus(TestBarrier.STATUS_START);
360
				return getRules[1];
361
			}
362
363
			public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
364
				HashSet<ISchedulingRule> h1 = getRulesAsSet(Job.getJobManager().currentRule());
365
				HashSet<ISchedulingRule> h2 = getRulesAsSet(buildRules[1]);
366
				assertTrue(h1.equals(h2));
367
				tb2.setStatus(TestBarrier.STATUS_DONE);
368
				return super.build(kind, args, monitor);
369
			}
370
		});
371
372
		Job j;
373
374
		// Enable for Bug 331187 
375
		//		// IProject.build()
376
		//		j = new Job("IProject.build()") {
377
		//			protected IStatus run(IProgressMonitor monitor) {
378
		//				try {
379
		//					project.build(IncrementalProjectBuilder.FULL_BUILD, monitor);
380
		//				} catch (CoreException e) {
381
		//					fail(e.toString());
382
		//				}
383
		//				return Status.OK_STATUS;
384
		//			}
385
		//		};
386
		//		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
387
388
		//		// IWorkspace.build()
389
		//		j = new Job("IWorkspace.build()") {
390
		//			protected IStatus run(IProgressMonitor monitor) {
391
		//				try {
392
		//					getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor);
393
		//				} catch (CoreException e) {
394
		//					fail(e.toString());
395
		//				}
396
		//				return Status.OK_STATUS;
397
		//			}
398
		//		};
399
		//		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
400
401
		// IWorkspace.build(IBuildConfiguration[],...)
402
		j = new Job("IWorkspace.build(IBuildConfiguration[],...)") {
403
			protected IStatus run(IProgressMonitor monitor) {
404
				try {
405
					getWorkspace().build(new IBuildConfiguration[] {project.getActiveBuildConfig()}, IncrementalProjectBuilder.FULL_BUILD, true, monitor);
406
				} catch (CoreException e) {
407
					fail(e.toString());
408
				}
409
				return Status.OK_STATUS;
410
			}
411
		};
412
		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
413
414
		// Test Auto-build
415
		//		j = new Job("Auto-build") {
416
		//			protected IStatus run(IProgressMonitor monitor) {
417
		//				try {
418
		//					getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, getMonitor());
419
		//				} catch (CoreException e) {
420
		//					fail(e.toString());
421
		//				}
422
		//				return Status.OK_STATUS;
423
		//			}
424
		//		};
425
		//		// Auto-build
426
		//		setAutoBuilding(true);
427
		//		// Wait for the build to transition
428
		//		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
429
	}
430
431
	/**
432
	 * Helper method do invoke a set of tests on Bug343256 using the different sets of builder API
433
	 */
434
	private void invokeTestBug343256(IProject project, ISchedulingRule[] getRules, ISchedulingRule[] buildRules, TestBarrier tb1, TestBarrier tb2, Job j) {
435
		// Test 1 - build project sched rule
436
		getRules[0] = getRules[1] = project;
437
		buildRules[0] = buildRules[1] = new MultiRule(new ISchedulingRule[] {getRules[0]});
438
		tb1.setStatus(TestBarrier.STATUS_START);
439
		tb2.setStatus(TestBarrier.STATUS_START);
440
		j.schedule();
441
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
442
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
443
444
		// Test 2 - build null rule
445
		getRules[0] = getRules[1] = null;
446
		buildRules[0] = buildRules[1] = null;
447
		tb1.setStatus(TestBarrier.STATUS_START);
448
		tb2.setStatus(TestBarrier.STATUS_START);
449
		j.schedule();
450
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
451
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
452
453
		// Test 3 - build mixed projects
454
		getRules[0] = buildRules[0] = project;
455
		getRules[1] = buildRules[1] = getWorkspace().getRoot().getProject("other");
456
		tb1.setStatus(TestBarrier.STATUS_START);
457
		tb2.setStatus(TestBarrier.STATUS_START);
458
		j.schedule();
459
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
460
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
461
462
		// Test 4 - build project + null
463
		getRules[0] = buildRules[0] = project;
464
		getRules[1] = buildRules[1] = null;
465
		// TODO: Fixed in Bug 331187 ; BuildManager#getRule is pessimistic when there's a mixed resource and null rule
466
		buildRules[0] = buildRules[1] = getWorkspace().getRoot();
467
		tb1.setStatus(TestBarrier.STATUS_START);
468
		tb2.setStatus(TestBarrier.STATUS_START);
469
		j.schedule();
470
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
471
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
472
	}
208
}
473
}
(-)src/org/eclipse/core/tests/internal/builders/TestBuilder.java (+8 lines)
Lines 26-31 Link Here
26
	 * A test specific call-back which can be ticked on #getRule(...) & #build(...)
26
	 * A test specific call-back which can be ticked on #getRule(...) & #build(...)
27
	 */
27
	 */
28
	public static class BuilderRuleCallback {
28
	public static class BuilderRuleCallback {
29
		private IncrementalProjectBuilder builder;
30
29
		public BuilderRuleCallback() {
31
		public BuilderRuleCallback() {
30
		}
32
		}
31
33
Lines 42-47 Link Here
42
		public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
44
		public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
43
			return new IProject[0];
45
			return new IProject[0];
44
		}
46
		}
47
48
		public IResourceDelta getDelta(IProject project) {
49
			return builder.getDelta(project);
50
		}
45
	}
51
	}
46
52
47
	/**
53
	/**
Lines 101-106 Link Here
101
		logPluginLifecycleEvent(getBuildId());
107
		logPluginLifecycleEvent(getBuildId());
102
		if (ruleCallBack == null)
108
		if (ruleCallBack == null)
103
			return new IProject[0];
109
			return new IProject[0];
110
		ruleCallBack.builder = this;
104
		return ruleCallBack.build(kind, args, monitor);
111
		return ruleCallBack.build(kind, args, monitor);
105
	}
112
	}
106
113
Lines 111-116 Link Here
111
	public ISchedulingRule getRule(int trigger, Map<String, String> args) {
118
	public ISchedulingRule getRule(int trigger, Map<String, String> args) {
112
		if (ruleCallBack == null)
119
		if (ruleCallBack == null)
113
			return super.getRule(trigger, args);
120
			return super.getRule(trigger, args);
121
		ruleCallBack.builder = this;
114
		return ruleCallBack.getRule(name, this, trigger, args);
122
		return ruleCallBack.getRule(name, this, trigger, args);
115
	}
123
	}
116
124

Return to bug 343256