| author | Hendrik Eeckhaut | 2011-09-28 04:23:33 (EDT) |
|---|---|---|
| committer | Knut Wannheden | 2011-10-10 05:21:48 (EDT) |
| commit | 7bcaa8c2c6d52c9724334d3ad1720310f1596720 (patch) (side-by-side diff) | |
| tree | bec9c9d897accee559028cf44d4cdb86f7485922 | |
| parent | 02999f02ed972a04d41312f58a545f7276c0a470 (diff) | |
| download | org.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
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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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() { |

