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

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new df82cbfd36 Fix incorrect cache size calculations for concurrent 
PUT/DELETE
df82cbfd36 is described below

commit df82cbfd3610af439e97a2f6c2dbfb95d559bdad
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Oct 30 10:15:37 2024 +0000

    Fix incorrect cache size calculations for concurrent PUT/DELETE
---
 .../catalina/webresources/CachedResource.java      | 27 ++++++++++++++++++----
 webapps/docs/changelog.xml                         |  4 ++++
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/java/org/apache/catalina/webresources/CachedResource.java 
b/java/org/apache/catalina/webresources/CachedResource.java
index 0b7543ce30..413ba477d8 100644
--- a/java/org/apache/catalina/webresources/CachedResource.java
+++ b/java/org/apache/catalina/webresources/CachedResource.java
@@ -73,6 +73,7 @@ public class CachedResource implements WebResource {
     private volatile Boolean cachedExists = null;
     private volatile Boolean cachedIsVirtual = null;
     private volatile Long cachedContentLength = null;
+    private final Object cachedContentLengthLock = new Object();
 
 
     public CachedResource(Cache cache, StandardRoot root, String path, long 
ttl, int objectMaxSizeBytes,
@@ -243,13 +244,29 @@ public class CachedResource implements WebResource {
 
     @Override
     public long getContentLength() {
+        /*
+         * Cache the content length for two reasons.
+         *
+         * 1. It is relatively expensive to calculate, it shouldn't change and 
this method is called multiple times
+         * during cache validation. Caching, therefore, offers a performance 
benefit.
+         *
+         * 2. There is a race condition if concurrent threads are trying to 
PUT and DELETE the same resource. If the
+         * DELETE thread removes the cache entry after the PUT thread has 
created it but before validation is complete
+         * the DELETE thread sees a content length for a non-existant resource 
but the PUT thread sees the actual
+         * content length. While that isn't an issue for the individual 
requests it, does corrupt the cache size
+         * tracking as the size used for the resource is different between 
when it is added to the cache and when it is
+         * removed. Caching combined with locking ensures that a consistent 
content length is reported for the resource.
+         */
         if (cachedContentLength == null) {
-            long result = 0;
-            if (webResource != null) {
-                result = webResource.getContentLength();
-                cachedContentLength = Long.valueOf(result);
+            synchronized (cachedContentLengthLock) {
+                if (cachedContentLength == null) {
+                    if (webResource == null) {
+                        cachedContentLength = Long.valueOf(0);
+                    } else {
+                        cachedContentLength = 
Long.valueOf(webResource.getContentLength());
+                    }
+                }
             }
-            return result;
         }
         return cachedContentLength.longValue();
     }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index bcee13ff64..7a72b65141 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -202,6 +202,10 @@
         possible for subcomponents to be in <code>FAILED</code> after init.
         (remm)
       </fix>
+      <fix>
+        Fix incorrect cache size calculations when there is a concurrent PUT 
and
+        DELETE for the same resource. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to