summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHendrik Eeckhaut2011-09-28 04:23:33 (EDT)
committer Knut Wannheden2011-10-10 05:21:48 (EDT)
commit7bcaa8c2c6d52c9724334d3ad1720310f1596720 (patch)
treebec9c9d897accee559028cf44d4cdb86f7485922
parent02999f02ed972a04d41312f58a545f7276c0a470 (diff)
downloadorg.eclipse.xtext-7bcaa8c2c6d52c9724334d3ad1720310f1596720.zip
org.eclipse.xtext-7bcaa8c2c6d52c9724334d3ad1720310f1596720.tar.gz
org.eclipse.xtext-7bcaa8c2c6d52c9724334d3ad1720310f1596720.tar.bz2
Parallel resource loading
https://bugs.eclipse.org/bugs/show_bug.cgi?id=358714
-rw-r--r--plugins/org.eclipse.xtext.builder/META-INF/MANIFEST.MF2
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/clustering/ClusteringBuilderState.java784
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/AbstractResourceLoader.java94
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/IResourceLoader.java131
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ParallelResourceLoader.java172
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ResourceLoaderProviders.java67
-rw-r--r--plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/SerialResourceLoader.java58
-rw-r--r--plugins/org.eclipse.xtext.ui.shared/src/org/eclipse/xtext/ui/shared/internal/SharedModule.java37
-rw-r--r--tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/Bug349445Test.java13
-rw-r--r--tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/PersistableResourceDescriptionsTest.java5
10 files changed, 1000 insertions, 363 deletions
diff --git a/plugins/org.eclipse.xtext.builder/META-INF/MANIFEST.MF b/plugins/org.eclipse.xtext.builder/META-INF/MANIFEST.MF
index 6dd9f9e..915d87a 100644
--- a/plugins/org.eclipse.xtext.builder/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.xtext.builder/META-INF/MANIFEST.MF
@@ -16,7 +16,7 @@ Export-Package: org.eclipse.xtext.builder,
org.eclipse.xtext.builder.impl.javasupport;x-friends:="org.eclipse.xtext.builder.tests,org.eclipse.xtext.ui.shared",
org.eclipse.xtext.builder.internal;x-friends:="org.eclipse.xtext.builder.tests,org.eclipse.xtext.ui.shared",
org.eclipse.xtext.builder.nature;x-friends:="org.eclipse.xtext.builder.tests,org.eclipse.xtext.ui.shared",
- org.eclipse.xtext.builder.preferences
+ org.eclipse.xtext.builder.resourceloader, org.eclipse.xtext.builder.preferences
Require-Bundle: org.eclipse.xtext;bundle-version="2.0.0",
org.eclipse.xtext.util;bundle-version="2.0.0",
org.eclipse.emf.ecore;visibility:=reexport,
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/clustering/ClusteringBuilderState.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/clustering/ClusteringBuilderState.java
index 6f8f146..fce1d2e 100644
--- a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/clustering/ClusteringBuilderState.java
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/clustering/ClusteringBuilderState.java
@@ -1,348 +1,436 @@
-/*******************************************************************************
- * Copyright (c) 2010 itemis AG (http://www.itemis.eu) 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
- *******************************************************************************/
-package org.eclipse.xtext.builder.clustering;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-
-import org.apache.log4j.Logger;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.common.util.WrappedException;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.xtext.EcoreUtil2;
-import org.eclipse.xtext.builder.builderState.AbstractBuilderState;
-import org.eclipse.xtext.builder.builderState.BuilderStateUtil;
-import org.eclipse.xtext.builder.builderState.ResourceDescriptionsData;
-import org.eclipse.xtext.builder.builderState.impl.ResourceDescriptionImpl;
-import org.eclipse.xtext.builder.impl.BuildData;
-import org.eclipse.xtext.resource.IResourceDescription;
-import org.eclipse.xtext.resource.IResourceDescription.Delta;
-import org.eclipse.xtext.resource.IResourceDescriptions;
-import org.eclipse.xtext.resource.IResourceServiceProvider;
-import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionDelta;
-import org.eclipse.xtext.util.CancelIndicator;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-import com.google.inject.Inject;
-
-/**
- * @author Sebastian Zarnekow - Initial contribution and API
- * @author Thomas Wolf <thomas.wolf@paranor.ch> - Refactored the build phases and documentation
- */
-public class ClusteringBuilderState extends AbstractBuilderState {
-
- /** Class-wide logger. */
- private static final Logger LOGGER = Logger.getLogger(ClusteringBuilderState.class);
-
- private static final int CLUSTER_SIZE = 20;
-
- @Inject
- private IResourceServiceProvider.Registry managerRegistry;
-
- @Inject
- private IResourceClusteringPolicy clusteringPolicy;
-
- /**
- * Actually do the build.
- *
- * @param resourceSet
- * The resource set
- * @param toBeUpdated
- * The URIs of all the resources that have physically changed
- * @param toBeRemoved
- * The URIs of all the resources that have been physically deleted
- * @param newMap
- * A map collecting the new resource descriptions as they are to be persisted. (The new index after the build.)
- * Initially contains the old resource descriptions.
- * @param monitor
- * The progress monitor
- * @return A list of deltas describing all changes made by the build.
- */
- @Override
- protected Collection<Delta> doUpdate(BuildData buildData, ResourceDescriptionsData newData, IProgressMonitor monitor) {
- final SubMonitor progress = SubMonitor.convert(monitor, 100);
-
-
- // Step 1: Clean the set of deleted URIs. If any of them are also added, they're not deleted.
- final Set<URI> toBeDeleted = buildData.getAndRemoveToBeDeleted();
-
- // Step 2: Create a new state (old state minus the deleted resources). This, by virtue of the flag
- // NAMED_BUILDER_SCOPE in the resource set's load options
- // and a Guice binding, is the index that is used during the build; i.e., linking during the build will
- // use this. Once the build is completed, the persistable index is reset to the contents of newState by
- // virtue of the newMap, which is maintained in synch with this.
- ResourceSet resourceSet = buildData.getResourceSet();
- final CurrentDescriptions newState = new CurrentDescriptions(resourceSet, newData);
-
- // Step 3: Create a queue; write new temporary resource descriptions for the added or updated resources so that we can link
- // subsequently; put all the added or updated resources into the queue.
- writeNewResourceDescriptions(buildData, this, newState, progress.newChild(20));
-
- if (progress.isCanceled()) {
- throw new OperationCanceledException();
- }
-
- // Step 4: Create a URI set of resources not yet in the delta. This is used for queuing; whenever a resource is
- // queued for processing, its URI is removed from this set. queueAffectedResources will consider only resources
- // in this set as potential candidates.
- for (final URI uri : toBeDeleted) {
- newData.removeDescription(uri);
- }
- final Set<URI> allRemainingURIs = Sets.newLinkedHashSet(newData.getAllURIs());
- allRemainingURIs.removeAll(buildData.getToBeUpdated());
- for(URI remainingURI: buildData.getAllRemainingURIs()) {
- allRemainingURIs.remove(remainingURI);
- }
- // TODO: consider to remove any entry from upstream projects and independent projects
- // from the set of remaining uris (template method or service?)
- // this should reduce the number of to-be-checked descriptions significantly
- // for common setups (large number of reasonable sized projects)
-
- // Our return value. It contains all the deltas resulting from this build.
- final Set<Delta> allDeltas = Sets.newHashSet();
-
- // Step 5: Put all resources depending on a deleted resource into the queue. Also register the deltas in allDeltas.
- if (!toBeDeleted.isEmpty()) {
- for (final URI uri : toBeDeleted) {
- final IResourceDescription oldDescription = this.getResourceDescription(uri);
- if (oldDescription != null) {
- allDeltas.add(new DefaultResourceDescriptionDelta(oldDescription, null));
- }
- }
- }
- // Add all pending deltas to all deltas (may be scheduled java deltas)
- Collection<Delta> pendingDeltas = buildData.getAndRemovePendingDeltas();
- allDeltas.addAll(pendingDeltas);
- queueAffectedResources(allRemainingURIs, this, newState, allDeltas, buildData, progress.newChild(1));
-
- // Step 6: Iteratively got through the queue. For each resource, create a new resource description and queue all depending
- // resources that are not yet in the delta. Validate resources. Do this in chunks.
- final SubMonitor subProgress = progress.newChild(80);
- CancelIndicator cancelMonitor = new CancelIndicator() {
- public boolean isCanceled() {
- return progress.isCanceled();
- }
- };
-
- int index = 1;
- Queue<URI> queue = buildData.getURIQueue();
- while (!queue.isEmpty()) {
- subProgress.setWorkRemaining(queue.size() + 2);
- // TODO: How to properly do progress indication with an unknown amount of work? Somehow, the progress bar doesn't
- // advance anymore after this...
- final List<Delta> newDeltas = new ArrayList<Delta>(CLUSTER_SIZE);
- final List<Delta> changedDeltas = new ArrayList<Delta>(CLUSTER_SIZE);
- while (!queue.isEmpty()) {
- if (subProgress.isCanceled()) {
- throw new OperationCanceledException();
- }
- if (!clusteringPolicy.continueProcessing(resourceSet, queue.peek(), newDeltas.size())) {
- break;
- }
- final URI changedURI = queue.poll();
- if (!toBeDeleted.contains(changedURI)) {
- subProgress.subTask("Updating resource description for " + changedURI.lastSegment() + " (" + index + " of " + (index + queue.size()) + ")");
- // Load the resource and create a new resource description
- Resource resource = null;
- Delta newDelta = null;
- try {
- final IResourceDescription.Manager manager = getResourceDescriptionManager(changedURI);
- if (manager != null) {
- resource = resourceSet.getResource(changedURI, true);
- // Resolve links here!
- EcoreUtil2.resolveLazyCrossReferences(resource, cancelMonitor);
- final IResourceDescription description = manager.getResourceDescription(resource);
- final IResourceDescription copiedDescription = BuilderStateUtil.create(description);
- newDelta = manager.createDelta(
- this.getResourceDescription(changedURI), copiedDescription);
- }
- } catch (final WrappedException ex) {
- LOGGER.error("Error loading resource from: " + changedURI.toString(), ex); //$NON-NLS-1$
- if (resource != null) {
- resourceSet.getResources().remove(resource);
- }
- final IResourceDescription oldDescription = this.getResourceDescription(changedURI);
- final IResourceDescription newDesc = newState.getResourceDescription(changedURI);
- ResourceDescriptionImpl indexReadyDescription = newDesc != null ? BuilderStateUtil.create(newDesc) : null;
- if ((oldDescription != null || indexReadyDescription != null) && oldDescription != indexReadyDescription) {
- newDelta = new DefaultResourceDescriptionDelta(oldDescription, indexReadyDescription);
- }
- }
- if (newDelta != null) {
- newDeltas.add(newDelta);
- if (newDelta.haveEObjectDescriptionsChanged())
- changedDeltas.add(newDelta);
- // Make the new resource description known and update the map.
- newState.register(newDelta);
- }
- subProgress.worked(1);
- index++;
- }
- }
- queueAffectedResources(allRemainingURIs, this, newState, changedDeltas, buildData, subProgress.newChild(1));
- // Validate now.
- updateMarkers(resourceSet, ImmutableList.<Delta>copyOf(newDeltas), subProgress.newChild(1));
- allDeltas.addAll(newDeltas);
- // Release memory
- if (!queue.isEmpty())
- clearResourceSet(resourceSet);
- }
- return allDeltas;
- }
-
- /**
- * Create new resource descriptions for a set of resources given by their URIs.
- *
- * @param resourceSet
- * The resource set
- * @param oldState
- * The old index
- * @param newState
- * The new index
- * @param toBeUpdated
- * URIs of all resources physically changed (initial delta)
- * @param monitor
- * The progress monitor used for user feedback
- * @return A list of all the URIs of toBeUpdated that could be indeed loaded and for which new description were written.
- */
- protected void writeNewResourceDescriptions(
- BuildData buildData,
- IResourceDescriptions oldState,
- CurrentDescriptions newState,
- final IProgressMonitor monitor) {
- int index = 1;
- Collection<URI> toBeUpdated = buildData.getToBeUpdated();
- ResourceSet resourceSet = buildData.getResourceSet();
- final int n = toBeUpdated.size();
- final SubMonitor subMonitor = SubMonitor.convert(monitor, "Write new resource descriptions", n); // TODO: NLS
- for (final URI uri : toBeUpdated) {
- if (subMonitor.isCanceled()) {
- throw new OperationCanceledException();
- }
- subMonitor.subTask("Writing new resource description for " + uri.lastSegment() + " (" + index + " of " + n + ")"); // TODO: NLS
- Resource resource = null;
- try {
- resource = resourceSet.getResource(uri, true);
-
- final IResourceDescription.Manager manager = getResourceDescriptionManager(uri);
- if (manager != null) {
- // We don't care here about links, we really just want the exported objects so that we can link in the
- // next phase.
- final IResourceDescription description = manager.getResourceDescription(resource);
- final IResourceDescription copiedDescription = new CopiedResourceDescription(description);
- // We also don't care what kind of Delta we get here; it's just a temporary transport vehicle. That interface
- // could do with some clean-up, too, because all we actually want to do is register the new resource
- // description, not the delta.
- newState.register(new DefaultResourceDescriptionDelta(oldState.getResourceDescription(uri), copiedDescription));
- buildData.queueURI(uri);
- }
- } catch (final WrappedException ex) {
- if (resourceSet.getURIConverter().exists(uri, Collections.emptyMap())) {
- LOGGER.error("Error loading resource from: " + uri.toString(), ex); //$NON-NLS-1$
- }
- if (resource != null) {
- resourceSet.getResources().remove(resource);
- }
- final IResourceDescription oldDescription = oldState.getResourceDescription(uri);
- if (oldDescription != null) {
- newState.register(new DefaultResourceDescriptionDelta(oldDescription, null));
- }
- // If we couldn't load it, there's no use trying again: do not add it to the queue
- }
-
- subMonitor.worked(1);
- index++;
- if (index % CLUSTER_SIZE == 0) {
- resourceSet.getResources().clear();
- }
- }
- clearResourceSet(resourceSet);
- }
-
- /**
- * Clears the content of the resource set without sending notifications.
- * This avoids unnecessary, explicit unloads.
- */
- protected void clearResourceSet(ResourceSet resourceSet) {
- boolean wasDeliver = resourceSet.eDeliver();
- try {
- resourceSet.eSetDeliver(false);
- resourceSet.getResources().clear();
- } finally {
- resourceSet.eSetDeliver(wasDeliver);
- }
- }
-
- /**
- * Put all resources that depend on some changes onto the queue of resources to be processed.
- * Updates notInDelta by removing all URIs put into the queue.
- *
- * @param notInDelta
- * URIs of unchanged and unaffected resources
- * @param oldState
- * State before the build
- * @param newState
- * The current state
- * @param deltas
- * The changes
- * @param queue
- * The queue of resources yet to be built
- * @param monitor
- * The progress monitor used for user feedback
- */
- protected void queueAffectedResources(
- Set<URI> allRemainingURIs,
- IResourceDescriptions oldState,
- CurrentDescriptions newState,
- Collection<Delta> deltas,
- BuildData buildData,
- final IProgressMonitor monitor) {
- if (deltas.isEmpty()) {
- return;
- }
- final SubMonitor progress = SubMonitor.convert(monitor, allRemainingURIs.size());
- Iterator<URI> iter = allRemainingURIs.iterator();
- while (iter.hasNext()) {
- if (progress.isCanceled()) {
- throw new OperationCanceledException();
- }
- final URI candidateURI = iter.next();
- final IResourceDescription candidateDescription = oldState.getResourceDescription(candidateURI);
- final IResourceDescription.Manager manager = getResourceDescriptionManager(candidateURI);
- if (candidateDescription == null || manager == null) {
- // If there is no description in the old state, there's no need to re-check this over and over.
- iter.remove();
- } else {
- if (manager.isAffected(deltas, candidateDescription, newState)) {
- buildData.queueURI(candidateURI);
- iter.remove();
- }
- }
- progress.worked(1);
- }
- }
-
- protected IResourceDescription.Manager getResourceDescriptionManager(URI uri) {
- IResourceServiceProvider resourceServiceProvider = managerRegistry.getResourceServiceProvider(uri);
- if (resourceServiceProvider == null) {
- return null;
- }
- return resourceServiceProvider.getResourceDescriptionManager();
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2010 itemis AG (http://www.itemis.eu) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.clustering;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.xtext.EcoreUtil2;
+import org.eclipse.xtext.builder.builderState.AbstractBuilderState;
+import org.eclipse.xtext.builder.builderState.BuilderStateUtil;
+import org.eclipse.xtext.builder.builderState.ResourceDescriptionsData;
+import org.eclipse.xtext.builder.builderState.impl.ResourceDescriptionImpl;
+import org.eclipse.xtext.builder.impl.BuildData;
+import org.eclipse.xtext.builder.resourceloader.IResourceLoader;
+import org.eclipse.xtext.builder.resourceloader.IResourceLoader.LoadOperation;
+import org.eclipse.xtext.builder.resourceloader.IResourceLoader.LoadOperationException;
+import org.eclipse.xtext.resource.IResourceDescription;
+import org.eclipse.xtext.resource.IResourceDescription.Delta;
+import org.eclipse.xtext.resource.IResourceDescriptions;
+import org.eclipse.xtext.resource.IResourceServiceProvider;
+import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionDelta;
+import org.eclipse.xtext.util.CancelIndicator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+/**
+ * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Thomas Wolf <thomas.wolf@paranor.ch> - Refactored the build phases and documentation
+ * @author Lieven Lemiengre <lieven.lemiengre@sigasi.com> - Parallel resource loading
+ */
+public class ClusteringBuilderState extends AbstractBuilderState {
+
+ public static final String RESOURCELOADER_CROSS_LINKING = "org.eclipse.xtext.builder.resourceloader.crossLinking";
+
+ public static final String RESOURCELOADER_GLOBAL_INDEX = "org.eclipse.xtext.builder.resourceloader.globalIndex";
+
+ /** Class-wide logger. */
+ private static final Logger LOGGER = Logger.getLogger(ClusteringBuilderState.class);
+
+ @Inject
+ private IResourceServiceProvider.Registry managerRegistry;
+
+ @Inject
+ private IResourceClusteringPolicy clusteringPolicy;
+
+ @Inject
+ @Named(RESOURCELOADER_GLOBAL_INDEX)
+ private IResourceLoader globalIndexResourceLoader;
+
+ @Inject
+ @Named(RESOURCELOADER_CROSS_LINKING)
+ private IResourceLoader crossLinkingResourceLoader;
+
+ /**
+ * Actually do the build.
+ *
+ * @param resourceSet
+ * The resource set
+ * @param toBeUpdated
+ * The URIs of all the resources that have physically changed
+ * @param toBeRemoved
+ * The URIs of all the resources that have been physically deleted
+ * @param newMap
+ * A map collecting the new resource descriptions as they are to be persisted. (The new index after the build.)
+ * Initially contains the old resource descriptions.
+ * @param monitor
+ * The progress monitor
+ * @return A list of deltas describing all changes made by the build.
+ */
+ @Override
+ protected Collection<Delta> doUpdate(BuildData buildData, ResourceDescriptionsData newData, IProgressMonitor monitor) {
+ final SubMonitor progress = SubMonitor.convert(monitor, 100);
+
+
+ // Step 1: Clean the set of deleted URIs. If any of them are also added, they're not deleted.
+ final Set<URI> toBeDeleted = buildData.getAndRemoveToBeDeleted();
+
+ // Step 2: Create a new state (old state minus the deleted resources). This, by virtue of the flag
+ // NAMED_BUILDER_SCOPE in the resource set's load options
+ // and a Guice binding, is the index that is used during the build; i.e., linking during the build will
+ // use this. Once the build is completed, the persistable index is reset to the contents of newState by
+ // virtue of the newMap, which is maintained in synch with this.
+ ResourceSet resourceSet = buildData.getResourceSet();
+ final CurrentDescriptions newState = new CurrentDescriptions(resourceSet, newData);
+
+ // Step 3: Create a queue; write new temporary resource descriptions for the added or updated resources so that we can link
+ // subsequently; put all the added or updated resources into the queue.
+ writeNewResourceDescriptions(buildData, this, newState, progress.newChild(20));
+
+ if (progress.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+
+ // Step 4: Create a URI set of resources not yet in the delta. This is used for queuing; whenever a resource is
+ // queued for processing, its URI is removed from this set. queueAffectedResources will consider only resources
+ // in this set as potential candidates.
+ for (final URI uri : toBeDeleted) {
+ newData.removeDescription(uri);
+ }
+ final Set<URI> allRemainingURIs = Sets.newLinkedHashSet(newData.getAllURIs());
+ allRemainingURIs.removeAll(buildData.getToBeUpdated());
+ for(URI remainingURI: buildData.getAllRemainingURIs()) {
+ allRemainingURIs.remove(remainingURI);
+ }
+ // TODO: consider to remove any entry from upstream projects and independent projects
+ // from the set of remaining uris (template method or service?)
+ // this should reduce the number of to-be-checked descriptions significantly
+ // for common setups (large number of reasonable sized projects)
+
+ // Our return value. It contains all the deltas resulting from this build.
+ final Set<Delta> allDeltas = Sets.newHashSet();
+
+ // Step 5: Put all resources depending on a deleted resource into the queue. Also register the deltas in allDeltas.
+ if (!toBeDeleted.isEmpty()) {
+ for (final URI uri : toBeDeleted) {
+ final IResourceDescription oldDescription = this.getResourceDescription(uri);
+ if (oldDescription != null) {
+ allDeltas.add(new DefaultResourceDescriptionDelta(oldDescription, null));
+ }
+ }
+ }
+ // Add all pending deltas to all deltas (may be scheduled java deltas)
+ Collection<Delta> pendingDeltas = buildData.getAndRemovePendingDeltas();
+ allDeltas.addAll(pendingDeltas);
+ queueAffectedResources(allRemainingURIs, this, newState, allDeltas, buildData, progress.newChild(1));
+
+ LoadOperation loadOperation = null;
+ try {
+ Queue<URI> queue = buildData.getURIQueue();
+ loadOperation = crossLinkingResourceLoader.create(resourceSet);
+ loadOperation.load(queue);
+
+ // Step 6: Iteratively got through the queue. For each resource, create a new resource description and queue all depending
+ // resources that are not yet in the delta. Validate resources. Do this in chunks.
+ final SubMonitor subProgress = progress.newChild(80);
+ CancelIndicator cancelMonitor = new CancelIndicator() {
+ public boolean isCanceled() {
+ return progress.isCanceled();
+ }
+ };
+
+ int index = 1;
+ while (!queue.isEmpty()) {
+ subProgress.setWorkRemaining(queue.size() + 2);
+ // TODO: How to properly do progress indication with an unknown amount of work? Somehow, the progress bar doesn't
+ // advance anymore after this...
+ final List<Delta> newDeltas = Lists.newArrayList();
+ final List<Delta> changedDeltas = Lists.newArrayList();
+ while (!queue.isEmpty()) {
+ if (subProgress.isCanceled()) {
+ loadOperation.cancel();
+ throw new OperationCanceledException();
+ }
+ if (!clusteringPolicy.continueProcessing(resourceSet, null, newDeltas.size())) {
+ break;
+ }
+
+ URI changedURI = null;
+ Resource resource = null;
+ Delta newDelta = null;
+
+ try {
+ // Load the resource and create a new resource description
+ Resource loadResult = loadOperation.next();
+ changedURI = loadResult.getURI();
+ resource = addResource(loadResult, resourceSet);
+
+ subProgress.subTask("Updating resource description for " + changedURI.lastSegment() + " (" + index + " of " + (index + queue.size()) + ")");
+ queue.remove(changedURI);
+ if(toBeDeleted.contains(changedURI)) {
+ break;
+ }
+
+ final IResourceDescription.Manager manager = getResourceDescriptionManager(changedURI);
+ if (manager != null) {
+ // Resolve links here!
+ EcoreUtil2.resolveLazyCrossReferences(resource, cancelMonitor);
+ final IResourceDescription description = manager.getResourceDescription(resource);
+ final IResourceDescription copiedDescription = BuilderStateUtil.create(description);
+ newDelta = manager.createDelta(this.getResourceDescription(changedURI), copiedDescription);
+ }
+ } catch (final WrappedException ex) {
+ if(ex instanceof LoadOperationException) {
+ changedURI = ((LoadOperationException) ex).getUri();
+ }
+ if(changedURI == null) {
+ LOGGER.error("Error loading resource", ex); //$NON-NLS-1$
+ } else {
+ queue.remove(changedURI);
+ if(toBeDeleted.contains(changedURI)) break;
+ LOGGER.error("Error loading resource from: " + changedURI.toString(), ex); //$NON-NLS-1$
+ if (resource != null) {
+ resourceSet.getResources().remove(resource);
+ }
+ final IResourceDescription oldDescription = this.getResourceDescription(changedURI);
+ final IResourceDescription newDesc = newState.getResourceDescription(changedURI);
+ ResourceDescriptionImpl indexReadyDescription = newDesc != null ? BuilderStateUtil.create(newDesc) : null;
+ if ((oldDescription != null || indexReadyDescription != null) && oldDescription != indexReadyDescription) {
+ newDelta = new DefaultResourceDescriptionDelta(oldDescription, indexReadyDescription);
+ }
+ }
+ }
+ if (newDelta != null) {
+ newDeltas.add(newDelta);
+ if (newDelta.haveEObjectDescriptionsChanged())
+ changedDeltas.add(newDelta);
+ // Make the new resource description known and update the map.
+ newState.register(newDelta);
+ }
+ subProgress.worked(1);
+ index++;
+ }
+
+ loadOperation.cancel();
+
+ queueAffectedResources(allRemainingURIs, this, newState, changedDeltas, buildData, subProgress.newChild(1));
+
+ if(queue.size() > 0) {
+ loadOperation = crossLinkingResourceLoader.create(resourceSet);
+ loadOperation.load(queue);
+ }
+
+ // Validate now.
+ updateMarkers(resourceSet, ImmutableList.<Delta>copyOf(newDeltas), subProgress.newChild(1));
+ allDeltas.addAll(newDeltas);
+ // Release memory
+ if (!queue.isEmpty())
+ clearResourceSet(resourceSet);
+ }
+ } finally {
+ if(loadOperation != null) loadOperation.cancel();
+ }
+ return allDeltas;
+ }
+
+ /**
+ * Create new resource descriptions for a set of resources given by their URIs.
+ *
+ * @param resourceSet
+ * The resource set
+ * @param oldState
+ * The old index
+ * @param newState
+ * The new index
+ * @param toBeUpdated
+ * URIs of all resources physically changed (initial delta)
+ * @param monitor
+ * The progress monitor used for user feedback
+ * @return A list of all the URIs of toBeUpdated that could be indeed loaded and for which new description were written.
+ */
+ protected void writeNewResourceDescriptions(
+ BuildData buildData,
+ IResourceDescriptions oldState,
+ CurrentDescriptions newState,
+ final IProgressMonitor monitor) {
+ int index = 0;
+ Collection<URI> toBeUpdated = buildData.getToBeUpdated();
+ ResourceSet resourceSet = buildData.getResourceSet();
+ final int n = toBeUpdated.size();
+ final SubMonitor subMonitor = SubMonitor.convert(monitor, "Write new resource descriptions", n); // TODO: NLS
+
+ LoadOperation loadOperation = null;
+ try {
+ loadOperation = globalIndexResourceLoader.create(resourceSet);
+ loadOperation.load(toBeUpdated);
+
+ while (loadOperation.hasNext()) {
+ if (subMonitor.isCanceled()) {
+ loadOperation.cancel();
+ throw new OperationCanceledException();
+ }
+
+ if (!clusteringPolicy.continueProcessing(resourceSet, null, index)) {
+ clearResourceSet(resourceSet);
+ }
+
+ URI uri = null;
+ Resource resource = null;
+ try {
+ Resource loadResult = loadOperation.next();
+ uri = loadResult.getURI();
+ resource = addResource(loadResult, resourceSet);
+
+ subMonitor.subTask("Writing new resource description for " + uri.lastSegment() + " (" + index++ + " of " + n + ")"); // TODO: NLS
+
+ final IResourceDescription.Manager manager = getResourceDescriptionManager(uri);
+ if (manager != null) {
+ // We don't care here about links, we really just want the exported objects so that we can link in the
+ // next phase.
+ final IResourceDescription description = manager.getResourceDescription(resource);
+ final IResourceDescription copiedDescription = new CopiedResourceDescription(description);
+ // We also don't care what kind of Delta we get here; it's just a temporary transport vehicle. That interface
+ // could do with some clean-up, too, because all we actually want to do is register the new resource
+ // description, not the delta.
+ newState.register(new DefaultResourceDescriptionDelta(oldState.getResourceDescription(uri), copiedDescription));
+ buildData.queueURI(uri);
+ }
+ } catch (final WrappedException ex) {
+ if(ex instanceof LoadOperationException) {
+ uri = ((LoadOperationException) ex).getUri();
+ }
+ if (uri == null) {
+ LOGGER.error("Error loading resource", ex); //$NON-NLS-1$
+ } else {
+ if (resourceSet.getURIConverter().exists(uri, Collections.emptyMap())) {
+ LOGGER.error("Error loading resource from: " + uri.toString(), ex); //$NON-NLS-1$
+ }
+ if (resource != null) {
+ resourceSet.getResources().remove(resource);
+ }
+ final IResourceDescription oldDescription = oldState.getResourceDescription(uri);
+ if (oldDescription != null) {
+ newState.register(new DefaultResourceDescriptionDelta(oldDescription, null));
+ }
+ }
+ // If we couldn't load it, there's no use trying again: do not add it to the queue
+ }
+
+ subMonitor.worked(1);
+ }
+ } finally {
+ if(loadOperation != null) loadOperation.cancel();
+ }
+ }
+
+ /**
+ * Clears the content of the resource set without sending notifications.
+ * This avoids unnecessary, explicit unloads.
+ */
+ protected void clearResourceSet(ResourceSet resourceSet) {
+ boolean wasDeliver = resourceSet.eDeliver();
+ try {
+ resourceSet.eSetDeliver(false);
+ resourceSet.getResources().clear();
+ } finally {
+ resourceSet.eSetDeliver(wasDeliver);
+ }
+ }
+
+ /**
+ * Adds a resource to the ResourceSet if the ResourceSet doesn't contain it yet.
+ *
+ * @param resource the resource
+ * @param resourceSet the resource set
+ * @return the resource
+ */
+ protected Resource addResource(Resource resource, ResourceSet resourceSet) {
+ URI uri = resource.getURI();
+ Resource r = resourceSet.getResource(uri, false);
+ if (r == null) {
+ resourceSet.getResources().add(resource);
+ return resource;
+ } else {
+ return r;
+ }
+ }
+
+ /**
+ * Put all resources that depend on some changes onto the queue of resources to be processed.
+ * Updates notInDelta by removing all URIs put into the queue.
+ *
+ * @param notInDelta
+ * URIs of unchanged and unaffected resources
+ * @param oldState
+ * State before the build
+ * @param newState
+ * The current state
+ * @param deltas
+ * The changes
+ * @param queue
+ * The queue of resources yet to be built
+ * @param monitor
+ * The progress monitor used for user feedback
+ */
+ protected void queueAffectedResources(
+ Set<URI> allRemainingURIs,
+ IResourceDescriptions oldState,
+ CurrentDescriptions newState,
+ Collection<Delta> deltas,
+ BuildData buildData,
+ final IProgressMonitor monitor) {
+ if (deltas.isEmpty()) {
+ return;
+ }
+ final SubMonitor progress = SubMonitor.convert(monitor, allRemainingURIs.size());
+ Iterator<URI> iter = allRemainingURIs.iterator();
+ while (iter.hasNext()) {
+ if (progress.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+ final URI candidateURI = iter.next();
+ final IResourceDescription candidateDescription = oldState.getResourceDescription(candidateURI);
+ final IResourceDescription.Manager manager = getResourceDescriptionManager(candidateURI);
+ if (candidateDescription == null || manager == null) {
+ // If there is no description in the old state, there's no need to re-check this over and over.
+ iter.remove();
+ } else {
+ if (manager.isAffected(deltas, candidateDescription, newState)) {
+ buildData.queueURI(candidateURI);
+ iter.remove();
+ }
+ }
+ progress.worked(1);
+ }
+ }
+
+ protected IResourceDescription.Manager getResourceDescriptionManager(URI uri) {
+ IResourceServiceProvider resourceServiceProvider = managerRegistry.getResourceServiceProvider(uri);
+ if (resourceServiceProvider == null) {
+ return null;
+ }
+ return resourceServiceProvider.getResourceDescriptionManager();
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/AbstractResourceLoader.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/AbstractResourceLoader.java
new file mode 100644
index 0000000..2228156
--- /dev/null
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/AbstractResourceLoader.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi NV (http://www.sigasi.com) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.resourceloader;
+
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.xtext.resource.XtextResourceSet;
+
+import com.google.inject.Provider;
+
+
+/**
+ * @author Lieven Lemiengre - Initial contribution and API
+ * @since 2.1
+ */
+public abstract class AbstractResourceLoader implements IResourceLoader {
+
+ private final Provider<XtextResourceSet> resourceSetProvider;
+ private final Sorter sorter;
+
+ public AbstractResourceLoader(Provider<XtextResourceSet> resourceSetProvider, Sorter sorter) {
+ this.resourceSetProvider = resourceSetProvider;
+ this.sorter = sorter;
+ }
+
+ public Provider<XtextResourceSet> getResourceSetProvider() {
+ return resourceSetProvider;
+ }
+
+ public Sorter getSorter() {
+ return sorter;
+ }
+
+ protected Resource loadResource(URI uri, ResourceSet localResourceSet, ResourceSet parentResourceSet) {
+ return localResourceSet.getResource(uri, true);
+ }
+
+ protected class CheckedLoadOperation implements LoadOperation {
+
+ private final LoadOperation parent;
+ private boolean isLoading = false;
+ private boolean isCancelled = false;
+
+ public CheckedLoadOperation(LoadOperation parent) {
+ this.parent = parent;
+ }
+
+ public void load(Collection<URI> uris) {
+ if(isLoading) {
+ throw new IllegalStateException("The load operation has already been started.");
+ }
+ isLoading = true;
+ parent.load(uris);
+ }
+
+ public boolean hasNext() {
+ checkIsLoading();
+ checkIsNotCancelled();
+ return parent.hasNext();
+ }
+
+ public Resource next() throws LoadOperationException {
+ checkIsLoading();
+ checkIsNotCancelled();
+ return parent.next();
+ }
+
+ public Collection<URI> cancel() {
+ checkIsLoading();
+ isCancelled = true;
+ return parent.cancel();
+ }
+
+ private void checkIsLoading() {
+ if(!isLoading) {
+ throw new IllegalStateException("The load operation hasn't started yet.");
+ }
+ }
+
+ private void checkIsNotCancelled() {
+ if(isCancelled) {
+ throw new IllegalStateException("The load operation was cancelled.");
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/IResourceLoader.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/IResourceLoader.java
new file mode 100644
index 0000000..467ad2a
--- /dev/null
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/IResourceLoader.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi NV (http://www.sigasi.com) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.resourceloader;
+
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+
+import com.google.common.collect.Lists;
+import com.google.inject.ImplementedBy;
+
+/**
+ *
+ * @author Lieven Lemiengre - Initial contribution and API
+ * @since 2.1
+ */
+@ImplementedBy(SerialResourceLoader.class)
+public interface IResourceLoader {
+
+ /**
+ * Create a LoadOperation.
+ * Be careful, while the load operation is running the parent ResourceSet may be read from different threads.
+ * Synchronize on the parent ResourceSet when you interact with it!
+ *
+ * @param parent the parent ResourceSet
+ * @return a LoadOperation
+ */
+ LoadOperation create(ResourceSet parent);
+
+ /**
+ * The Interface LoadOperation.
+ */
+ interface LoadOperation {
+
+ /**
+ * Load the resources.
+ *
+ * @param uris the uris
+ * @throws IllegalStateException If the loading was already started or it was cancelled
+ */
+ void load(Collection<URI> uris);
+
+ /**
+ * Checks if the ResourceLoader is still processing resources.
+ *
+ * @return true, if successful
+ * @throws IllegalStateException If the loading was cancelled or it hasn't started yet.
+ */
+ boolean hasNext();
+
+
+ /**
+ * Get the next Resource.
+ * This is a blocking call, it returns the next resource that was finished processing.
+ *
+ * @return the next available resource
+ * @throws IllegalStateException If the loading was cancelled or it hasn't started yet.
+ * @throws LoadOperationException the load operation exception
+ */
+ Resource next() throws LoadOperationException;
+
+ /**
+ * Cancel loading the resources. This method can be executed multiple times.
+ *
+ * @return the collection
+ */
+ Collection<URI> cancel();
+
+ }
+
+ @ImplementedBy(org.eclipse.xtext.builder.resourceloader.IResourceLoader.Sorter.NoSorting.class)
+ interface Sorter {
+ Collection<URI> sort(Collection<URI> uris);
+
+ class NoSorting implements Sorter {
+ public Collection<URI> sort(Collection<URI> uris) {
+ return Lists.newArrayList(uris);
+ }
+ }
+ }
+
+ /**
+ * The Class LoadOperationException.
+ */
+ class LoadOperationException extends WrappedException {
+
+ private static final long serialVersionUID = 8499010336607816601L;
+
+ private final URI uri;
+
+ /**
+ * Instantiates a new load operation exception.
+ *
+ * @param uri the uri of the resource that failed to load
+ * @param exception the exception
+ */
+ public LoadOperationException(URI uri, Throwable exception) {
+ this(uri, new Exception(exception));
+ }
+
+ /**
+ * Instantiates a new load operation exception.
+ *
+ * @param uri the uri of the resource that failed to load
+ * @param exception the exception
+ */
+ public LoadOperationException(URI uri, Exception exception) {
+ super(exception);
+ this.uri = uri;
+ }
+
+ /**
+ * Gets the URI of the resource that failed to load.
+ *
+ * @return the uri
+ */
+ public URI getUri() {
+ return uri;
+ }
+
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ParallelResourceLoader.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ParallelResourceLoader.java
new file mode 100644
index 0000000..82dbd1b
--- /dev/null
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ParallelResourceLoader.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi NV (http://www.sigasi.com) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.resourceloader;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.xtext.resource.XtextResourceSet;
+import org.eclipse.xtext.util.Triple;
+import org.eclipse.xtext.util.Tuples;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Provider;
+
+/**
+ * Loads resources in one more separate threads.
+ *
+ * @author Lieven Lemiengre - Initial contribution and API
+ * @since 2.1
+ */
+public class ParallelResourceLoader extends AbstractResourceLoader {
+
+ private static final long MAX_WAIT_TIME = TimeUnit.SECONDS.toMillis(60);
+
+ private final int nThreads;
+ private final int queueSize;
+ private long timeout;
+
+ public ParallelResourceLoader(Provider<XtextResourceSet> resourceSetProvider, Sorter sorter, int nThreads, int queueSize) {
+ super(resourceSetProvider, sorter);
+ this.nThreads = nThreads;
+ this.queueSize = queueSize;
+ this.timeout = MAX_WAIT_TIME;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long time, TimeUnit unit) {
+ this.timeout = unit.toMillis(time);
+ }
+
+ public LoadOperation create(ResourceSet parent) {
+ return new CheckedLoadOperation(new ParallelLoadOperation(parent));
+ }
+
+ private class ParallelLoadOperation implements LoadOperation {
+
+ private final BlockingQueue<Triple<URI, Resource, Throwable>> resourceQueue;
+ private final ThreadLocal<ResourceSet> resourceSetProvider;
+ private final ExecutorService executor;
+ private final ResourceSet parent;
+ private final long waitTime;
+ private int toProcess;
+
+ public ParallelLoadOperation(final ResourceSet parent) {
+ this.parent = parent;
+ if(queueSize == 0) {
+ this.resourceQueue = new SynchronousQueue<Triple<URI, Resource, Throwable>>(true);
+ } else {
+ this.resourceQueue = new ArrayBlockingQueue<Triple<URI, Resource, Throwable>>(queueSize);
+ }
+ this.resourceSetProvider = new ThreadLocal<ResourceSet>() {
+ @Override
+ protected ResourceSet initialValue() {
+ XtextResourceSet resourceSet = getResourceSetProvider().get();
+ resourceSet.getLoadOptions().putAll(parent.getLoadOptions());
+ resourceSet.setURIConverter(parent.getURIConverter());
+ return resourceSet;
+ }
+ };
+ this.executor = Executors.newFixedThreadPool(nThreads);
+ this.waitTime = getTimeout();
+ }
+
+ private class ResourceLoadJob implements Runnable {
+ private final URI uri;
+
+ public ResourceLoadJob(URI uri) {
+ this.uri = uri;
+ }
+
+ public void run() {
+ Throwable exception = null;
+ Resource resource = null;
+
+ // load resource
+ try {
+ ResourceSet localResourceSet = resourceSetProvider.get();
+ resource = loadResource(uri, localResourceSet, parent);
+ localResourceSet.getResources().clear();
+ } catch (Throwable t) {
+ exception = t;
+ }
+
+ // push resource to the queue, wait if queue is full
+ try {
+ resourceQueue.put(Tuples.create(uri, resource, exception));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public Resource next() {
+ if (!hasNext())
+ throw new NoSuchElementException("The resource queue is empty or the execution was cancelled.");
+ Triple<URI, Resource, Throwable> result = null;
+ try {
+ result = resourceQueue.poll(waitTime, TimeUnit.MILLISECONDS);
+ toProcess--;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ if (result == null) {
+ throw new NullPointerException("Resource load job didn't return a result");
+ }
+
+ URI uri = result.getFirst();
+ Resource resource = result.getSecond();
+ Throwable throwable = result.getThird();
+
+ if (throwable != null) { // rethrow errors in the main thread
+ throw new LoadOperationException(uri, throwable);
+ }
+ return resource;
+ }
+
+ public boolean hasNext() {
+ return toProcess > 0;
+ }
+
+ public void load(Collection<URI> uris) {
+ toProcess += uris.size();
+ Collection<URI> workload = getSorter().sort(uris);
+ for(URI uri : workload) {
+ executor.execute(new ResourceLoadJob(uri));
+ }
+ executor.shutdown();
+ }
+
+ public Collection<URI> cancel() {
+ toProcess = 0;
+ List<Runnable> jobs = executor.shutdownNow();
+ List<URI> ret = Lists.newArrayList();
+ for (Runnable job : jobs) {
+ if (job instanceof ResourceLoadJob) {
+ ret.add(((ResourceLoadJob) job).uri);
+ }
+ }
+ return ret;
+ }
+
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ResourceLoaderProviders.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ResourceLoaderProviders.java
new file mode 100644
index 0000000..5e360c1
--- /dev/null
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/ResourceLoaderProviders.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi NV (http://www.sigasi.com) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.resourceloader;
+
+import org.eclipse.xtext.builder.resourceloader.IResourceLoader.Sorter;
+import org.eclipse.xtext.resource.XtextResourceSet;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/**
+ * @author Lieven Lemiengre - Initial contribution and API
+ * @since 2.1
+ */
+public final class ResourceLoaderProviders {
+
+ public static abstract class AbstractResourceLoaderProvider implements Provider<IResourceLoader> {
+
+ @Inject
+ private Provider<XtextResourceSet> resourceSetProvider;
+
+ @Inject
+ private Sorter resourceSorter;
+
+ public Provider<XtextResourceSet> getResourceSetProvider() {
+ return resourceSetProvider;
+ }
+
+ public Sorter getResourceSorter() {
+ return resourceSorter;
+ }
+ }
+
+ /** Returns a loader with a parallelization degree of 2 to 4 (depending on how many processors are available). */
+ public static Provider<IResourceLoader> getParallelLoader() {
+ int nProcessors = Runtime.getRuntime().availableProcessors();
+ int nThreads = Math.max(2, Math.min(4, nProcessors));
+ return getParallelLoader(nThreads, 0);
+ }
+
+ public static Provider<IResourceLoader> getParallelLoader(final int nrOfThreads) {
+ return getParallelLoader(nrOfThreads, 0);
+ }
+
+ public static Provider<IResourceLoader> getParallelLoader(final int nrOfThreads, final int bufferSize) {
+ return new AbstractResourceLoaderProvider() {
+ public IResourceLoader get() {
+ ParallelResourceLoader resourceLoader = new ParallelResourceLoader(getResourceSetProvider(), getResourceSorter(), nrOfThreads, bufferSize);
+ return resourceLoader;
+ }
+ };
+ }
+
+ public static Provider<IResourceLoader> getSerialLoader() {
+ return new AbstractResourceLoaderProvider() {
+ public IResourceLoader get() {
+ return new SerialResourceLoader(getResourceSetProvider(), getResourceSorter());
+ }
+ };
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/SerialResourceLoader.java b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/SerialResourceLoader.java
new file mode 100644
index 0000000..3765b68
--- /dev/null
+++ b/plugins/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/resourceloader/SerialResourceLoader.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi NV (http://www.sigasi.com) 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
+ *******************************************************************************/
+package org.eclipse.xtext.builder.resourceloader;
+
+import java.util.Collection;
+import java.util.Queue;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.xtext.resource.XtextResourceSet;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Provider;
+
+/**
+ * Loads resources in sequentially in the same thread as it was invoked.
+ *
+ * @author Lieven Lemiengre - Initial contribution and API
+ * @since 2.1
+ */
+public class SerialResourceLoader extends AbstractResourceLoader {
+
+ public SerialResourceLoader(Provider<XtextResourceSet> resourceSetProvider, Sorter sorter) {
+ super(resourceSetProvider, sorter);
+ }
+
+ public LoadOperation create(final ResourceSet parent) {
+ final Queue<URI> queue = Lists.newLinkedList();
+
+ return new CheckedLoadOperation(new LoadOperation() {
+
+ public Resource next() {
+ URI uri = queue.poll();
+ Resource resource = parent.getResource(uri, true);
+ return resource;
+ }
+
+ public boolean hasNext() {
+ return !queue.isEmpty();
+ }
+
+ public Collection<URI> cancel() {
+ return queue;
+ }
+
+ public void load(Collection<URI> uris) {
+ queue.addAll(getSorter().sort(uris));
+ }
+ });
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext.ui.shared/src/org/eclipse/xtext/ui/shared/internal/SharedModule.java b/plugins/org.eclipse.xtext.ui.shared/src/org/eclipse/xtext/ui/shared/internal/SharedModule.java
index 8c1f1c8..cf714f1 100644
--- a/plugins/org.eclipse.xtext.ui.shared/src/org/eclipse/xtext/ui/shared/internal/SharedModule.java
+++ b/plugins/org.eclipse.xtext.ui.shared/src/org/eclipse/xtext/ui/shared/internal/SharedModule.java
@@ -20,6 +20,8 @@ import org.eclipse.xtext.builder.clustering.ClusteringBuilderState;
import org.eclipse.xtext.builder.impl.DirtyStateAwareResourceDescriptions;
import org.eclipse.xtext.builder.impl.ProjectOpenedOrClosedListener;
import org.eclipse.xtext.builder.impl.XtextBuilder;
+import org.eclipse.xtext.builder.resourceloader.IResourceLoader;
+import org.eclipse.xtext.builder.resourceloader.ResourceLoaderProviders;
import org.eclipse.xtext.resource.IExternalContentSupport;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;
@@ -29,8 +31,8 @@ import org.eclipse.xtext.ui.notification.IStateChangeEventBroker;
import org.eclipse.xtext.ui.notification.StateChangeEventBroker;
import org.eclipse.xtext.ui.resource.IResourceSetProvider;
import org.eclipse.xtext.ui.resource.IStorage2UriMapper;
-import org.eclipse.xtext.ui.resource.Storage2UriMapperImpl;
import org.eclipse.xtext.ui.resource.SimpleResourceSetProvider;
+import org.eclipse.xtext.ui.resource.Storage2UriMapperImpl;
import org.eclipse.xtext.ui.shared.JdtHelper;
import org.eclipse.xtext.ui.util.IJdtHelper;
@@ -43,7 +45,7 @@ import com.google.inject.name.Names;
* @author Sven Efftinge - Initial contribution and API
*/
public class SharedModule extends AbstractModule {
-
+
@Override
protected void configure() {
bind(IBuilderState.class).to(ClusteringBuilderState.class).in(Scopes.SINGLETON);
@@ -52,14 +54,14 @@ public class SharedModule extends AbstractModule {
bind(IResourceSetProvider.class).to(SimpleResourceSetProvider.class);
bind(IExtensionRegistry.class).toInstance(Platform.getExtensionRegistry());
bind(IResourceChangeListener.class).annotatedWith(Names.named(ProjectOpenedOrClosedListener.class.getName())).to(ProjectOpenedOrClosedListener.class);
-
+
bind(IExternalContentSupport.IExternalContentProvider.class).to(IDirtyStateManager.class).in(Scopes.SINGLETON);
bind(IDirtyStateManager.class).to(DirtyStateManager.class).in(Scopes.SINGLETON);
bind(IStateChangeEventBroker.class).to(StateChangeEventBroker.class).in(Scopes.SINGLETON);
bind(IncrementalProjectBuilder.class).to(XtextBuilder.class);
bind(IStorage2UriMapper.class).to(Storage2UriMapperImpl.class).in(Scopes.SINGLETON);
-
+
bind(IWorkbench.class).toProvider(new Provider<IWorkbench>() {
public IWorkbench get() {
if (PlatformUI.isWorkbenchRunning())
@@ -67,14 +69,35 @@ public class SharedModule extends AbstractModule {
return null;
}
});
-
+
bind(IWorkspace.class).toProvider(new Provider<IWorkspace>() {
public IWorkspace get() {
return ResourcesPlugin.getWorkspace();
}
});
-
+
bind(IJdtHelper.class).to(JdtHelper.class).asEagerSingleton();
+
+ boolean parallel = true;
+ if (parallel) {
+ bind(IResourceLoader.class).toProvider(ResourceLoaderProviders.getParallelLoader());
+
+ bind(IResourceLoader.class).annotatedWith(
+ Names.named(ClusteringBuilderState.RESOURCELOADER_GLOBAL_INDEX)).toProvider(
+ ResourceLoaderProviders.getParallelLoader());
+
+ bind(IResourceLoader.class).annotatedWith(Names.named(ClusteringBuilderState.RESOURCELOADER_CROSS_LINKING))
+ .toProvider(ResourceLoaderProviders.getParallelLoader());
+ } else {
+ bind(IResourceLoader.class).toProvider(ResourceLoaderProviders.getSerialLoader());
+
+ bind(IResourceLoader.class).annotatedWith(
+ Names.named(ClusteringBuilderState.RESOURCELOADER_GLOBAL_INDEX)).toProvider(
+ ResourceLoaderProviders.getSerialLoader());
+
+ bind(IResourceLoader.class).annotatedWith(Names.named(ClusteringBuilderState.RESOURCELOADER_CROSS_LINKING))
+ .toProvider(ResourceLoaderProviders.getSerialLoader());
+ }
}
-
+
}
diff --git a/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/Bug349445Test.java b/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/Bug349445Test.java
index 42767c5..7c4a539 100644
--- a/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/Bug349445Test.java
+++ b/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/Bug349445Test.java
@@ -10,6 +10,8 @@ package org.eclipse.xtext.builder.builderState;
import java.util.Collections;
import java.util.Map;
+import junit.framework.TestCase;
+
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EcorePackage;
@@ -20,19 +22,20 @@ import org.eclipse.xtext.builder.impl.QueuedBuildData;
import org.eclipse.xtext.builder.impl.ToBeBuilt;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IResourceDescription;
-import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.resource.IResourceDescription.Delta;
+import org.eclipse.xtext.resource.IResourceServiceProvider;
+import org.eclipse.xtext.ui.shared.internal.SharedModule;
+import org.eclipse.xtext.util.Modules2;
import com.google.common.collect.ImmutableList;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
-import junit.framework.TestCase;
-
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
+@SuppressWarnings("restriction")
public class Bug349445Test extends TestCase implements PersistedStateProvider, IMarkerUpdater, IResourceServiceProvider.Registry {
private IBuilderState testMe;
@@ -40,14 +43,14 @@ public class Bug349445Test extends TestCase implements PersistedStateProvider, I
@Override
protected void setUp() throws Exception {
- Injector injector = Guice.createInjector(new AbstractModule() {
+ Injector injector = Guice.createInjector(Modules2.mixin(new SharedModule(), new AbstractModule() {
@Override
protected void configure() {
bind(PersistedStateProvider.class).toInstance(Bug349445Test.this);
bind(IMarkerUpdater.class).toInstance(Bug349445Test.this);
bind(IResourceServiceProvider.Registry.class).toInstance(Bug349445Test.this);
}
- });
+ }));
loadCalled = 0;
testMe = injector.getInstance(ClusteringBuilderState.class);
}
diff --git a/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/PersistableResourceDescriptionsTest.java b/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/PersistableResourceDescriptionsTest.java
index b1dfe5d..52e5c94 100644
--- a/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/PersistableResourceDescriptionsTest.java
+++ b/tests/org.eclipse.xtext.builder.tests/src/org/eclipse/xtext/builder/builderState/PersistableResourceDescriptionsTest.java
@@ -30,7 +30,7 @@ import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescription.Delta;
import org.eclipse.xtext.resource.containers.DelegatingIAllContainerAdapter;
import org.eclipse.xtext.resource.containers.IAllContainersState;
-import org.eclipse.xtext.ui.shared.SharedStateModule;
+import org.eclipse.xtext.ui.shared.internal.SharedModule;
import org.eclipse.xtext.util.StringInputStream;
import com.google.common.base.Function;
@@ -44,6 +44,7 @@ import com.google.inject.Injector;
/**
* @author Sven Efftinge - Initial contribution and API
*/
+@SuppressWarnings("restriction")
public class PersistableResourceDescriptionsTest extends AbstractXtextTests {
private static final String FILE_EXT = ".buildertestlanguage";
private Injector builderInjector;
@@ -56,7 +57,7 @@ public class PersistableResourceDescriptionsTest extends AbstractXtextTests {
public void setUp() throws Exception {
super.setUp();
with(new BuilderTestLanguageStandaloneSetup());
- SharedStateModule module = new SharedStateModule();
+ SharedModule module = new SharedModule();
builderInjector = Guice.createInjector(module);
builderState = builderInjector.getInstance(ClusteringBuilderState.class);
uriConverter = new ExtensibleURIConverterImpl() {