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 ;
8085import com .avaloq .tools .ddk .xtext .resource .AbstractResourceDescriptionDelta ;
8186import com .avaloq .tools .ddk .xtext .resource .extensions .ForwardingResourceDescriptions ;
8287import com .avaloq .tools .ddk .xtext .resource .extensions .IResourceDescriptions2 ;
88+ import com .avaloq .tools .ddk .xtext .resource .persistence .DirectLinkingResourceStorageFacade ;
8389import com .avaloq .tools .ddk .xtext .resource .persistence .DirectLinkingSourceLevelURIsAdapter ;
8490import com .avaloq .tools .ddk .xtext .scoping .ImplicitReferencesAdapter ;
8591import com .avaloq .tools .ddk .xtext .tracing .ITraceSet ;
@@ -163,6 +169,11 @@ public class MonitoredClusteringBuilderState extends ClusteringBuilderState
163169 @ Inject
164170 private IResourceServiceProvider .Registry resourceServiceProviderRegistry ;
165171
172+ @ Inject (optional = true )
173+ private IFileSystemAccess fileSystemAccess ;
174+
175+ private final ForkJoinPool binaryStorageExecutor = new ForkJoinPool (4 );
176+
166177 /**
167178 * Handle to the ResourceDescriptionsData we use viewed as a IResourceDescriptions2 (with findReferences()). Parent class does not provide direct access to
168179 * its ResourceDescriptionsData, and anyway it wouldn't know about out new interfaces.
@@ -414,6 +425,7 @@ protected Collection<Delta> doUpdate(final BuildData buildData, final ResourceDe
414425 final Map <URI , DerivedObjectAssociations > oldDerivedObjectAssociations = saveOldDerivedObjectAssociations (buildData );
415426
416427 buildData .getSourceLevelURICache ().cacheAsSourceURIs (toBeDeleted );
428+ deleteBinaryResources (toBeDeleted );
417429 installSourceLevelURIs (buildData );
418430
419431 // Step 3: Create a queue; write new temporary resource descriptions for the added or updated resources
@@ -612,6 +624,7 @@ public boolean isCanceled() {
612624 if (resource instanceof XtextResource ) {
613625 ((XtextResource ) resource ).getCache ().clear (resource );
614626 }
627+ storeBinaryResource (resource , buildData );
615628 traceSet .ended (ResourceProcessingEvent .class );
616629 buildData .getSourceLevelURICache ().getSources ().remove (changedURI );
617630 subProgress .worked (1 );
@@ -644,6 +657,7 @@ public boolean isCanceled() {
644657 }
645658 traceSet .ended (BuildLinkingEvent .class );
646659 watchdog .interrupt ();
660+ awaitBinaryStorageExecutorQuiescence ();
647661 }
648662 return allDeltas ;
649663 // CHECKSTYLE:CHECK-ON NestedTryDepth
@@ -666,6 +680,70 @@ protected Resource addResource(final Resource resource, final ResourceSet resour
666680 }
667681 }
668682
683+ /**
684+ * Stores the process resource as a binary if it doesn't contain syntax or linking errors.
685+ *
686+ * @param resource
687+ * resource to store, must not be {@code null}
688+ * @param buildData
689+ * build data, must not be {@code null}
690+ */
691+ protected void storeBinaryResource (final Resource resource , final BuildData buildData ) {
692+ if (resource instanceof StorageAwareResource && ((StorageAwareResource ) resource ).getResourceStorageFacade () != null
693+ && fileSystemAccess instanceof IFileSystemAccessExtension3 ) {
694+ CompletableFuture .runAsync (() -> {
695+ final long maxTaskExecutionNanos = TimeUnit .NANOSECONDS .convert (1 , TimeUnit .SECONDS );
696+
697+ try {
698+ long elapsed = System .nanoTime ();
699+
700+ IResourceStorageFacade storageFacade = ((StorageAwareResource ) resource ).getResourceStorageFacade ();
701+ storageFacade .saveResource ((StorageAwareResource ) resource , (IFileSystemAccessExtension3 ) fileSystemAccess );
702+ buildData .getSourceLevelURICache ().getSources ().remove (resource .getURI ());
703+
704+ elapsed = System .nanoTime () - elapsed ;
705+ if (elapsed > maxTaskExecutionNanos ) {
706+ LOGGER .info ("saving binary taking longer than expected (" + elapsed + " ns) : " + resource .getURI ()); //$NON-NLS-1$ //$NON-NLS-2$
707+ }
708+ // CHECKSTYLE:OFF
709+ } catch (Throwable ex ) {
710+ // CHECKSTYLE:ON
711+ LOGGER .error ("Failed to save binary for " + resource .getURI (), ex ); //$NON-NLS-1$
712+ }
713+ }, binaryStorageExecutor );
714+ }
715+ }
716+
717+ /**
718+ * Deletes the binary models for the given set of URIs.
719+ *
720+ * @param toBeDeleted
721+ * set of URIs, must not be {@code null}
722+ */
723+ protected void deleteBinaryResources (final Set <URI > toBeDeleted ) {
724+ if (fileSystemAccess == null ) {
725+ return ;
726+ }
727+ for (URI uri : toBeDeleted ) {
728+ IResourceStorageFacade resourceStorageFacade = resourceServiceProviderRegistry .getResourceServiceProvider (uri ).get (IResourceStorageFacade .class );
729+ if (resourceStorageFacade instanceof DirectLinkingResourceStorageFacade ) {
730+ ((DirectLinkingResourceStorageFacade ) resourceStorageFacade ).deleteStorage (uri , fileSystemAccess );
731+ }
732+ }
733+ }
734+
735+ /**
736+ * Waits until binary models are stored.
737+ */
738+ protected void awaitBinaryStorageExecutorQuiescence () {
739+ int activeThreadCount = binaryStorageExecutor .getActiveThreadCount ();
740+ long queuedTaskCount = binaryStorageExecutor .getQueuedTaskCount ();
741+ if (!binaryStorageExecutor .awaitQuiescence (1 , TimeUnit .MINUTES )) {
742+ 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$
743+ activeThreadCount , queuedTaskCount , binaryStorageExecutor .getActiveThreadCount (), binaryStorageExecutor .getQueuedTaskCount ()));
744+ }
745+ }
746+
669747 /**
670748 * Log the first and last 10 StackOverflowError Stack Trace.
671749 *
@@ -841,7 +919,9 @@ protected boolean recordDeltaAsNew(final Delta newDelta) {
841919 * The progress monitor used for user feedback
842920 * @return the list of {@link URI}s of loaded resources to be processed in the second phase
843921 */
844- private List <URI > writeResources (final Collection <URI > toWrite , final BuildData buildData , final IResourceDescriptions oldState , final CurrentDescriptions newState , final IProgressMonitor monitor ) { // NOPMD NPath Complexity
922+ private List <URI > writeResources (final Collection <URI > toWrite , final BuildData buildData , final IResourceDescriptions oldState , final CurrentDescriptions newState , final IProgressMonitor monitor ) { // NOPMD
923+ // NPath
924+ // Complexity
845925 ResourceSet resourceSet = buildData .getResourceSet ();
846926 IProject currentProject = getBuiltProject (buildData );
847927 List <URI > toBuild = Lists .newLinkedList ();
@@ -885,7 +965,7 @@ private List<URI> writeResources(final Collection<URI> toWrite, final BuildData
885965 if (uri == null && ex instanceof LoadOperationException ) { // NOPMD
886966 uri = ((LoadOperationException ) ex ).getUri ();
887967 }
888- LOGGER .error (NLS .bind (Messages .MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE , uri != null ? uri : "unknown uri" ), ex ); //$NON-NLS-1$
968+ LOGGER .error (NLS .bind (Messages .MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE , uri != null ? uri : "unknown uri" ), ex ); //$NON-NLS-1$
889969 if (resource != null ) {
890970 resourceSet .getResources ().remove (resource );
891971 }
@@ -971,6 +1051,10 @@ protected void writeNewResourceDescriptions(final BuildData buildData, final IRe
9711051 */
9721052 @ Override
9731053 protected void clearResourceSet (final ResourceSet resourceSet ) {
1054+ // this is important as otherwise the resources would unexpectedly become detached from the resource set
1055+ while (!binaryStorageExecutor .awaitQuiescence (1 , TimeUnit .SECONDS )) {
1056+ LOGGER .warn ("Waiting for binary resource storage tasks to complete." ); //$NON-NLS-1$
1057+ }
9741058 traceSet .started (BuildResourceSetClearEvent .class , resourceSet .getResources ().size ());
9751059 try {
9761060 EmfResourceSetUtil .clearResourceSetWithoutNotifications (resourceSet );
0 commit comments