1717import java .util .Optional ;
1818import java .util .Queue ;
1919import java .util .Set ;
20+ import java .util .concurrent .CompletableFuture ;
21+ import java .util .concurrent .ForkJoinPool ;
2022import java .util .concurrent .TimeUnit ;
2123import java .util .concurrent .TimeoutException ;
2224import java .util .stream .Collectors ;
3840import org .eclipse .xtext .builder .impl .BuildData ;
3941import org .eclipse .xtext .builder .resourceloader .IResourceLoader ;
4042import org .eclipse .xtext .builder .resourceloader .IResourceLoader .LoadOperationException ;
43+ import org .eclipse .xtext .generator .IFileSystemAccess ;
44+ import org .eclipse .xtext .generator .IFileSystemAccessExtension3 ;
4145import org .eclipse .xtext .resource .IEObjectDescription ;
4246import org .eclipse .xtext .resource .IReferenceDescription ;
4347import org .eclipse .xtext .resource .IResourceDescription ;
4953import org .eclipse .xtext .resource .impl .DefaultResourceDescriptionDelta ;
5054import org .eclipse .xtext .resource .impl .ResourceDescriptionChangeEvent ;
5155import org .eclipse .xtext .resource .impl .ResourceDescriptionsData ;
56+ import org .eclipse .xtext .resource .persistence .IResourceStorageFacade ;
5257import org .eclipse .xtext .resource .persistence .StorageAwareResource ;
5358import org .eclipse .xtext .service .OperationCanceledManager ;
5459import org .eclipse .xtext .util .CancelIndicator ;
7984import com .avaloq .tools .ddk .xtext .resource .AbstractResourceDescriptionDelta ;
8085import com .avaloq .tools .ddk .xtext .resource .extensions .ForwardingResourceDescriptions ;
8186import com .avaloq .tools .ddk .xtext .resource .extensions .IResourceDescriptions2 ;
87+ import com .avaloq .tools .ddk .xtext .resource .persistence .DirectLinkingResourceStorageFacade ;
8288import com .avaloq .tools .ddk .xtext .resource .persistence .DirectLinkingSourceLevelURIsAdapter ;
8389import com .avaloq .tools .ddk .xtext .scoping .ImplicitReferencesAdapter ;
8490import com .avaloq .tools .ddk .xtext .tracing .ITraceSet ;
@@ -162,6 +168,11 @@ public class MonitoredClusteringBuilderState extends ClusteringBuilderState
162168 @ Inject
163169 private IResourceServiceProvider .Registry resourceServiceProviderRegistry ;
164170
171+ @ Inject (optional = true )
172+ private IFileSystemAccess fileSystemAccess ;
173+
174+ private final ForkJoinPool binaryStorageExecutor = new ForkJoinPool (4 );
175+
165176 /**
166177 * Handle to the ResourceDescriptionsData we use viewed as a IResourceDescriptions2 (with findReferences()). Parent class does not provide direct access to
167178 * its ResourceDescriptionsData, and anyway it wouldn't know about out new interfaces.
@@ -413,6 +424,7 @@ protected Collection<Delta> doUpdate(final BuildData buildData, final ResourceDe
413424 final Map <URI , DerivedObjectAssociations > oldDerivedObjectAssociations = saveOldDerivedObjectAssociations (buildData );
414425
415426 buildData .getSourceLevelURICache ().cacheAsSourceURIs (toBeDeleted );
427+ deleteBinaryResources (toBeDeleted );
416428 installSourceLevelURIs (buildData );
417429
418430 // Step 3: Create a queue; write new temporary resource descriptions for the added or updated resources
@@ -611,6 +623,7 @@ public boolean isCanceled() {
611623 if (resource instanceof XtextResource ) {
612624 ((XtextResource ) resource ).getCache ().clear (resource );
613625 }
626+ storeBinaryResource (resource , buildData );
614627 traceSet .ended (ResourceProcessingEvent .class );
615628 buildData .getSourceLevelURICache ().getSources ().remove (changedURI );
616629 subProgress .worked (1 );
@@ -643,6 +656,7 @@ public boolean isCanceled() {
643656 }
644657 traceSet .ended (BuildLinkingEvent .class );
645658 watchdog .interrupt ();
659+ awaitBinaryStorageExecutorQuiescence ();
646660 }
647661 return allDeltas ;
648662 // CHECKSTYLE:CHECK-ON NestedTryDepth
@@ -665,6 +679,70 @@ protected Resource addResource(final Resource resource, final ResourceSet resour
665679 }
666680 }
667681
682+ /**
683+ * Stores the process resource as a binary if it doesn't contain syntax or linking errors.
684+ *
685+ * @param resource
686+ * resource to store, must not be {@code null}
687+ * @param buildData
688+ * build data, must not be {@code null}
689+ */
690+ protected void storeBinaryResource (final Resource resource , final BuildData buildData ) {
691+ if (resource instanceof StorageAwareResource && ((StorageAwareResource ) resource ).getResourceStorageFacade () != null
692+ && fileSystemAccess instanceof IFileSystemAccessExtension3 ) {
693+ CompletableFuture .runAsync (() -> {
694+ final long maxTaskExecutionNanos = TimeUnit .NANOSECONDS .convert (1 , TimeUnit .SECONDS );
695+
696+ try {
697+ long elapsed = System .nanoTime ();
698+
699+ IResourceStorageFacade storageFacade = ((StorageAwareResource ) resource ).getResourceStorageFacade ();
700+ storageFacade .saveResource ((StorageAwareResource ) resource , (IFileSystemAccessExtension3 ) fileSystemAccess );
701+ buildData .getSourceLevelURICache ().getSources ().remove (resource .getURI ());
702+
703+ elapsed = System .nanoTime () - elapsed ;
704+ if (elapsed > maxTaskExecutionNanos ) {
705+ LOGGER .info ("saving binary taking longer than expected (" + elapsed + " ns) : " + resource .getURI ()); //$NON-NLS-1$ //$NON-NLS-2$
706+ }
707+ // CHECKSTYLE:OFF
708+ } catch (Throwable ex ) {
709+ // CHECKSTYLE:ON
710+ LOGGER .error ("Failed to save binary for " + resource .getURI (), ex ); //$NON-NLS-1$
711+ }
712+ }, binaryStorageExecutor );
713+ }
714+ }
715+
716+ /**
717+ * Deletes the binary models for the given set of URIs.
718+ *
719+ * @param toBeDeleted
720+ * set of URIs, must not be {@code null}
721+ */
722+ protected void deleteBinaryResources (final Set <URI > toBeDeleted ) {
723+ if (fileSystemAccess == null ) {
724+ return ;
725+ }
726+ for (URI uri : toBeDeleted ) {
727+ IResourceStorageFacade resourceStorageFacade = resourceServiceProviderRegistry .getResourceServiceProvider (uri ).get (IResourceStorageFacade .class );
728+ if (resourceStorageFacade instanceof DirectLinkingResourceStorageFacade ) {
729+ ((DirectLinkingResourceStorageFacade ) resourceStorageFacade ).deleteStorage (uri , fileSystemAccess );
730+ }
731+ }
732+ }
733+
734+ /**
735+ * Waits until binary models are stored.
736+ */
737+ protected void awaitBinaryStorageExecutorQuiescence () {
738+ int activeThreadCount = binaryStorageExecutor .getActiveThreadCount ();
739+ long queuedTaskCount = binaryStorageExecutor .getQueuedTaskCount ();
740+ if (!binaryStorageExecutor .awaitQuiescence (1 , TimeUnit .MINUTES )) {
741+ LOGGER .warn (String .format ("Binary resource storage tasks not completed in time, start with task / queue %d / %d; now have %d / %d" , //$NON-NLS-1$
742+ activeThreadCount , queuedTaskCount , binaryStorageExecutor .getActiveThreadCount (), binaryStorageExecutor .getQueuedTaskCount ()));
743+ }
744+ }
745+
668746 /**
669747 * Log the first and last 10 StackOverflowError Stack Trace.
670748 *
@@ -840,7 +918,9 @@ protected boolean recordDeltaAsNew(final Delta newDelta) {
840918 * The progress monitor used for user feedback
841919 * @return the list of {@link URI}s of loaded resources to be processed in the second phase
842920 */
843- private List <URI > writeResources (final Collection <URI > toWrite , final BuildData buildData , final IResourceDescriptions oldState , final CurrentDescriptions newState , final IProgressMonitor monitor ) { // NOPMD NPath Complexity
921+ private List <URI > writeResources (final Collection <URI > toWrite , final BuildData buildData , final IResourceDescriptions oldState , final CurrentDescriptions newState , final IProgressMonitor monitor ) { // NOPMD
922+ // NPath
923+ // Complexity
844924 ResourceSet resourceSet = buildData .getResourceSet ();
845925 IProject currentProject = getBuiltProject (buildData );
846926 List <URI > toBuild = Lists .newLinkedList ();
@@ -884,7 +964,7 @@ private List<URI> writeResources(final Collection<URI> toWrite, final BuildData
884964 if (uri == null && ex instanceof LoadOperationException ) { // NOPMD
885965 uri = ((LoadOperationException ) ex ).getUri ();
886966 }
887- LOGGER .error (NLS .bind (Messages .MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE , uri != null ? uri : "unknown uri" ), ex ); //$NON-NLS-1$
967+ LOGGER .error (NLS .bind (Messages .MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE , uri != null ? uri : "unknown uri" ), ex ); //$NON-NLS-1$
888968 if (resource != null ) {
889969 resourceSet .getResources ().remove (resource );
890970 }
@@ -970,6 +1050,10 @@ protected void writeNewResourceDescriptions(final BuildData buildData, final IRe
9701050 */
9711051 @ Override
9721052 protected void clearResourceSet (final ResourceSet resourceSet ) {
1053+ // this is important as otherwise the resources would unexpectedly become detached from the resource set
1054+ while (!binaryStorageExecutor .awaitQuiescence (1 , TimeUnit .SECONDS )) {
1055+ LOGGER .warn ("Waiting for binary resource storage tasks to complete." ); //$NON-NLS-1$
1056+ }
9731057 traceSet .started (BuildResourceSetClearEvent .class , resourceSet .getResources ().size ());
9741058 try {
9751059 EmfResourceSetUtil .clearResourceSetWithoutNotifications (resourceSet );
0 commit comments