This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit aee6ef722008ac50b6527e623e6a0c8e11a2c8ce
Author: Jiri Ondrusek <[email protected]>
AuthorDate: Mon Aug 10 15:15:44 2026 +0200

    Fix native image build deadlock in Saxon class initialization
    
    Saxon's SequenceType and MapType have mutually referencing static
    initializers: SequenceType.SINGLE_MAP references MapType.ANY_MAP_TYPE,
    which in turn references SequenceType.ANY_SEQUENCE. Initializing them
    from a single thread is safe, because class initialization is reentrant
    and ANY_SEQUENCE is assigned before SINGLE_MAP.
    
    SaxonProcessor registers all Saxon functions for reflection, and the
    native image builder performs those registrations from several analysis
    threads. When ArraySortBy and LoadXqueryModule are initialized
    concurrently, each thread ends up holding one class initialization lock
    while waiting for the other, and the image generator watchdog aborts the
    build. This is reproducible in the foundation-grouped integration test.
    
    Initialize the cycle from a GraalVM Feature before the analysis starts,
    so that it stays on a single thread.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../component/saxon/deployment/SaxonProcessor.java |  7 ++++
 extensions/saxon/runtime/pom.xml                   |  5 +++
 .../saxon/SaxonClassInitializationFeature.java     | 43 ++++++++++++++++++++++
 3 files changed, 55 insertions(+)

diff --git 
a/extensions/saxon/deployment/src/main/java/org/apache/camel/quarkus/component/saxon/deployment/SaxonProcessor.java
 
b/extensions/saxon/deployment/src/main/java/org/apache/camel/quarkus/component/saxon/deployment/SaxonProcessor.java
index 469abbd3d7..f84027fa8a 100644
--- 
a/extensions/saxon/deployment/src/main/java/org/apache/camel/quarkus/component/saxon/deployment/SaxonProcessor.java
+++ 
b/extensions/saxon/deployment/src/main/java/org/apache/camel/quarkus/component/saxon/deployment/SaxonProcessor.java
@@ -25,11 +25,13 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
+import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
 import 
io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
 import net.sf.saxon.Configuration;
 import net.sf.saxon.functions.SystemFunction;
 import net.sf.saxon.xpath.XPathFactoryImpl;
+import 
org.apache.camel.quarkus.component.saxon.SaxonClassInitializationFeature;
 import org.jboss.jandex.ClassInfo;
 import org.jboss.jandex.DotName;
 import org.jboss.logging.Logger;
@@ -74,6 +76,11 @@ class SaxonProcessor {
         deps.produce(new IndexDependencyBuildItem("net.sf.saxon", "Saxon-HE"));
     }
 
+    @BuildStep
+    NativeImageFeatureBuildItem classInitializationFeature() {
+        return new 
NativeImageFeatureBuildItem(SaxonClassInitializationFeature.class);
+    }
+
     @BuildStep
     void runtimeInit(BuildProducer<RuntimeInitializedClassBuildItem> deps) {
         deps.produce(new 
RuntimeInitializedClassBuildItem("org.apache.hc.client5.http.impl.auth.NTLMEngineImpl"));
diff --git a/extensions/saxon/runtime/pom.xml b/extensions/saxon/runtime/pom.xml
index a0ca2fc8b4..be51ff5748 100644
--- a/extensions/saxon/runtime/pom.xml
+++ b/extensions/saxon/runtime/pom.xml
@@ -48,6 +48,11 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-saxon</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.graalvm.sdk</groupId>
+            <artifactId>nativeimage</artifactId>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/extensions/saxon/runtime/src/main/java/org/apache/camel/quarkus/component/saxon/SaxonClassInitializationFeature.java
 
b/extensions/saxon/runtime/src/main/java/org/apache/camel/quarkus/component/saxon/SaxonClassInitializationFeature.java
new file mode 100644
index 0000000000..5633283383
--- /dev/null
+++ 
b/extensions/saxon/runtime/src/main/java/org/apache/camel/quarkus/component/saxon/SaxonClassInitializationFeature.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.saxon;
+
+import java.util.Objects;
+
+import net.sf.saxon.ma.map.MapType;
+import net.sf.saxon.value.SequenceType;
+import org.graalvm.nativeimage.hosted.Feature;
+
+/**
+ * Works around a class initialization deadlock in the native image builder.
+ *
+ * SequenceType and MapType have mutually referencing static initializers: 
SequenceType.SINGLE_MAP references
+ * MapType.ANY_MAP_TYPE, which in turn references SequenceType.ANY_SEQUENCE. 
Initializing them from a single thread is
+ * safe, because class initialization is reentrant and ANY_SEQUENCE is 
assigned before SINGLE_MAP. Initializing them
+ * concurrently deadlocks, as each thread holds one class initialization lock 
while waiting for the other.
+ *
+ * Saxon functions are registered for reflection in SaxonProcessor, and the 
builder performs those registrations on
+ * several analysis threads, which is enough to trigger the deadlock. 
Initializing the cycle here, before the analysis
+ * starts, keeps it on a single thread.
+ */
+public class SaxonClassInitializationFeature implements Feature {
+    @Override
+    public void beforeAnalysis(BeforeAnalysisAccess access) {
+        Objects.requireNonNull(SequenceType.ANY_SEQUENCE);
+        Objects.requireNonNull(MapType.ANY_MAP_TYPE);
+    }
+}

Reply via email to