This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new b30dec1605 Copilot/review commit f206204 (#997)
b30dec1605 is described below
commit b30dec1605bc44d9a2f5521c8c19797fa2d8808c
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Apr 23 19:30:13 2026 +0200
Copilot/review commit f206204 (#997)
* Fix race condition in ReplicatedContext.getServletContext()
Co-authored-by: copilot-swe-agent[bot]
<[email protected]>
Co-authored-by: markt-asf <[email protected]>
---
.../catalina/ha/context/ReplicatedContext.java | 10 ++--
.../catalina/ha/context/TestReplicatedContext.java | 57 ++++++++++++++++++++++
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/java/org/apache/catalina/ha/context/ReplicatedContext.java
b/java/org/apache/catalina/ha/context/ReplicatedContext.java
index 5ba2583fb0..8db3b70394 100644
--- a/java/org/apache/catalina/ha/context/ReplicatedContext.java
+++ b/java/org/apache/catalina/ha/context/ReplicatedContext.java
@@ -118,9 +118,13 @@ public class ReplicatedContext extends StandardContext
implements MapOwner {
@Override
public ServletContext getServletContext() {
if (context == null) {
- context = new ReplApplContext(this);
- if (getAltDDName() != null) {
- context.setAttribute(Globals.ALT_DD_ATTR, getAltDDName());
+ synchronized (this) {
+ if (context == null) {
+ context = new ReplApplContext(this);
+ if (getAltDDName() != null) {
+ context.setAttribute(Globals.ALT_DD_ATTR,
getAltDDName());
+ }
+ }
}
}
diff --git a/test/org/apache/catalina/ha/context/TestReplicatedContext.java
b/test/org/apache/catalina/ha/context/TestReplicatedContext.java
index 3840a19249..dffdab05db 100644
--- a/test/org/apache/catalina/ha/context/TestReplicatedContext.java
+++ b/test/org/apache/catalina/ha/context/TestReplicatedContext.java
@@ -18,7 +18,12 @@ package org.apache.catalina.ha.context;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicReference;
+import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
@@ -37,6 +42,58 @@ import org.apache.tomcat.util.buf.ByteChunk;
public class TestReplicatedContext extends TomcatBaseTest {
+ @Test
+ public void testGetServletContextReturnsSameInstanceUnderConcurrency()
throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ Host host = tomcat.getHost();
+ if (host instanceof StandardHost) {
+ ((StandardHost)
host).setContextClass(ReplicatedContext.class.getName());
+ }
+
+ File root = new File("test/webapp");
+ ReplicatedContext replicatedContext =
+ (ReplicatedContext) tomcat.addWebapp(host, "",
root.getAbsolutePath());
+ tomcat.start();
+
+ // Null the context field to simulate the window during reload
+ replicatedContext.context = null;
+
+ int numThreads = 20;
+ CyclicBarrier barrier = new CyclicBarrier(numThreads);
+ List<Thread> threads = new ArrayList<>();
+ ServletContext[] results = new ServletContext[numThreads];
+ AtomicReference<Throwable> failure = new AtomicReference<>();
+
+ for (int i = 0; i < numThreads; i++) {
+ final int index = i;
+ Thread thread = new Thread(() -> {
+ try {
+ barrier.await();
+ results[index] = replicatedContext.getServletContext();
+ } catch (Throwable ex) {
+ failure.set(ex);
+ }
+ });
+ thread.start();
+ threads.add(thread);
+ }
+
+ for (Thread thread : threads) {
+ thread.join(5000);
+ }
+
+ if (failure.get() != null) {
+ Assert.fail("Thread failed: " + failure.get());
+ }
+
+ ServletContext first = results[0];
+ Assert.assertNotNull(first);
+ for (int i = 1; i < numThreads; i++) {
+ Assert.assertSame(first, results[i]);
+ }
+ }
+
+
@Test
public void testBug57425() throws LifecycleException, IOException {
Tomcat tomcat = getTomcatInstance();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]