You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/05/16 20:50:31 UTC

[2/44] git commit: Support caching of exists(), forLocale(), and toURL() for Resources - Uses ReentrantReadWriteLock for high throughput Fix incorrect version numbers for previous batch of changes

Support caching of exists(), forLocale(), and toURL() for Resources
- Uses ReentrantReadWriteLock for high throughput
Fix incorrect version numbers for previous batch of changes


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/6bd95286
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6bd95286
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6bd95286

Branch: refs/heads/master
Commit: 6bd9528652cd6359a0b4b915871204a604ce4399
Parents: a824b8d
Author: Howard M. Lewis Ship <hl...@gmail.com>
Authored: Tue May 15 10:56:52 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed May 16 11:50:15 2012 -0700

----------------------------------------------------------------------
 .../org/apache/tapestry5/ComponentResources.java   |    6 +-
 .../internal/services/ContextResource.java         |   84 +++++++---
 .../apache/tapestry5/internal/structure/Page.java  |    6 +-
 .../internal/structure/PageResetListener.java      |    2 +-
 .../tapestry5/runtime/PageLifecycleAdapter.java    |    2 +-
 .../runtime/PageLifecycleCallbackHub.java          |    8 +-
 .../tapestry5/runtime/PageLifecycleListener.java   |    4 +-
 .../java/org/apache/tapestry5/ioc/Resource.java    |   14 +-
 .../ioc/internal/util/AbstractResource.java        |  131 ++++++++++++++-
 .../ioc/internal/util/ClasspathResource.java       |   42 ++++-
 10 files changed, 245 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
index 4e7bb8f..fcc1c7c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
@@ -173,7 +173,7 @@ public interface ComponentResources extends ComponentResourcesCommon
     /**
      * Adds a listener object that will be notified about page lifecycle events.
      *
-     * @deprecated In 5.3.3, use {@link #getPageLifecycleCallbackHub()} instead
+     * @deprecated In 5.3.4, use {@link #getPageLifecycleCallbackHub()} instead
      */
     void addPageLifecycleListener(PageLifecycleListener listener);
 
@@ -181,7 +181,7 @@ public interface ComponentResources extends ComponentResourcesCommon
      * Provides access to an object that can be used to register callbacks for page lifecycle events.
      *
      * @return the hub
-     * @since 5.3.3
+     * @since 5.3.4
      */
     PageLifecycleCallbackHub getPageLifecycleCallbackHub();
 
@@ -189,7 +189,7 @@ public interface ComponentResources extends ComponentResourcesCommon
      * Removes a previously added listener.
      *
      * @since 5.2.0
-     * @deprecated in 5.3.3, not necessary with {@link PageLifecycleCallbackHub#addPageLoadedCallback(Runnable)}.
+     * @deprecated in 5.3.4, not necessary with {@link PageLifecycleCallbackHub#addPageLoadedCallback(Runnable)}.
      */
     void removePageLifecycleListener(PageLifecycleListener listener);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
index bb0e4e9..7d2db59 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
@@ -14,14 +14,14 @@
 
 package org.apache.tapestry5.internal.services;
 
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
 import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.ioc.internal.util.AbstractResource;
 import org.apache.tapestry5.services.Context;
 
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
 /**
  * A resource stored with in the web application context.
  */
@@ -31,10 +31,16 @@ public class ContextResource extends AbstractResource
 
     private final Context context;
 
+    // Guarded by lock
+    private URL url;
+
+    // Guarded by lock
+    private boolean urlResolved;
+
     public ContextResource(Context context, String path)
     {
         super(path);
-        
+
         assert context != null;
 
         this.context = context;
@@ -54,33 +60,69 @@ public class ContextResource extends AbstractResource
 
     public URL toURL()
     {
-        // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
-        // and HttpServletContext.getResource() does. This is what I mean when I say that
-        // a framework is an accumulation of the combined experience of many users and developers.
+        try
+        {
+            lock.readLock().lock();
 
-        String contextPath = "/" + getPath();
+            if (!urlResolved)
+            {
+                resolveURL();
+            }
 
-        // Always prefer the actual file to the URL.  This is critical for templates to
-        // reload inside Tomcat.
+            return url;
 
-        File file = context.getRealFile(contextPath);
+        } finally
+        {
+            lock.readLock().unlock();
+        }
+    }
 
-        if (file != null && file.exists())
+    private void resolveURL()
+    {
+        try
         {
-            try
+            upgradeReadLockToWriteLock();
+
+            // Race condition on the write lock:
+            if (urlResolved)
             {
-                return file.toURL();
+                return;
             }
-            catch (MalformedURLException ex)
+
+            // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
+            // and HttpServletContext.getResource() does. This is what I mean when I say that
+            // a framework is an accumulation of the combined experience of many users and developers.
+
+            String contextPath = "/" + getPath();
+
+            // Always prefer the actual file to the URL.  This is critical for templates to
+            // reload inside Tomcat.
+
+            File file = context.getRealFile(contextPath);
+
+            if (file != null && file.exists())
             {
-                throw new RuntimeException(ex);
+                try
+                {
+                    url = file.toURL();
+                    urlResolved = true;
+                    return;
+                } catch (MalformedURLException ex)
+                {
+                    throw new RuntimeException(ex);
+                }
             }
-        }
 
-        // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
-        // URL we get ... but reloading won't work.
+            // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
+            // URL we get ... but reloading won't work.
 
-        return context.getResource(contextPath);
+            url = context.getResource(contextPath);
+            urlResolved = true;
+
+        } finally
+        {
+            downgradeWriteLockToReadLock();
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
index ae66822..845f0a4 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
@@ -143,7 +143,7 @@ public interface Page extends PageLifecycleCallbackHub
     /**
      * Adds a listener that is notified of large scale page events.
      *
-     * @deprecated in 5.3.3; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
+     * @deprecated in 5.3.4; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
      *             {@link #addPageDetachedCallback(Runnable)}  instead
      */
     void addLifecycleListener(PageLifecycleListener listener);
@@ -152,7 +152,7 @@ public interface Page extends PageLifecycleCallbackHub
      * Removes a listener that was previously added.
      *
      * @since 5.2.0
-     * @deprecated in 5.3.3, due to introduction of {@link #addPageLoadedCallback(Runnable)}
+     * @deprecated in 5.3.4, due to introduction of {@link #addPageLoadedCallback(Runnable)}
      */
     void removeLifecycleListener(PageLifecycleListener listener);
 
@@ -213,7 +213,7 @@ public interface Page extends PageLifecycleCallbackHub
      * @param listener
      *         will receive notifications when the page is accessed from a different page
      * @since 5.2.0
-     * @deprecated in 5.3.3,
+     * @deprecated in 5.3.4,
      */
     void addResetListener(PageResetListener listener);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
index ad95d29..055c907 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
@@ -21,7 +21,7 @@ import org.apache.tapestry5.annotations.PageReset;
  * 
  * @since 5.2.0
  * @see PageReset
- * @deprecated in 5.3.3
+ * @deprecated in 5.3.4
  * @see org.apache.tapestry5.runtime.PageLifecycleCallbackHub#addResetCallback(Runnable)
  */
 public interface PageResetListener

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
index e7f45ff..cb5b891 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.runtime;
 /**
  * Empty implementation of the {@link PageLifecycleListener} interface.
  *
- * @deprecated in 5.3.3, as {@link PageLifecycleListener} has been deprecated
+ * @deprecated in 5.3.4, as {@link PageLifecycleListener} has been deprecated
  */
 public class PageLifecycleAdapter implements PageLifecycleListener
 {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
index 518925e..8d10098 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
@@ -18,7 +18,7 @@ package org.apache.tapestry5.runtime;
  * Defines a way for different aspects of a page to add callbacks for important lifecycle events.
  *
  * @see org.apache.tapestry5.ComponentResources#getPageLifecycleCallbackHub()
- * @since 5.3.3
+ * @since 5.3.4
  */
 public interface PageLifecycleCallbackHub
 {
@@ -28,7 +28,7 @@ public interface PageLifecycleCallbackHub
      *
      * @param callback
      *         invoked once, when page is first loaded
-     * @since 5.3.3
+     * @since 5.3.4
      */
     void addPageLoadedCallback(Runnable callback);
 
@@ -36,7 +36,7 @@ public interface PageLifecycleCallbackHub
      * Adds a callback for when the page is attached to the request.
      *
      * @param callback
-     * @since 5.3.3
+     * @since 5.3.4
      */
     void addPageAttachedCallback(Runnable callback);
 
@@ -44,7 +44,7 @@ public interface PageLifecycleCallbackHub
      * Adds a callback for when the page is detached from the request.
      *
      * @param callback
-     * @since 5.3.3
+     * @since 5.3.4
      */
     void addPageDetachedCallback(Runnable callback);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
index 5e80cec..b1d5405 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
@@ -19,7 +19,7 @@ package org.apache.tapestry5.runtime;
  * {@linkplain org.apache.tapestry5.plastic.PlasticClass#introduceInterface(Class)} introduced}, the component will
  * automatically register itself as a listener with the page.
  *
- * @deprecated in 5.3.3, replaced with {@link PageLifecycleCallbackHub}
+ * @deprecated in 5.3.4, replaced with {@link PageLifecycleCallbackHub}
  */
 public interface PageLifecycleListener
 {
@@ -27,7 +27,7 @@ public interface PageLifecycleListener
      * Invoked when the page finishes loading. This occurs once all components are loaded and all parameters have been
      * set.
      *
-     * @deprecated in 5.3.3,  use {@link org.apache.tapestry5.ComponentResources#addPageLoadedCallback(Runnable)} instead
+     * @deprecated in 5.3.4,  use {@link org.apache.tapestry5.ComponentResources#addPageLoadedCallback(Runnable)} instead
      */
     void containingPageDidLoad();
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
index aa431f5..7a73fd7 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@ public interface Resource
 {
 
     /**
-     * Returns true if the resource exists; if a stream to the content of the file may be openned.
+     * Returns true if the resource exists; if a stream to the content of the file may be opened. A resource exists
+     * if {@link #toURL()} returns a non-null value. Starting in release 5.3.4, the result of this is cached.
      *
      * @return true if the resource exists, false if it does not
      */
@@ -44,12 +45,14 @@ public interface Resource
     InputStream openStream() throws IOException;
 
     /**
-     * Returns the URL for the resource, or null if it does not exist.
+     * Returns the URL for the resource, or null if it does not exist. This value is lazily computed; starting in 5.3.4, subclasses may cache
+     * the result.
      */
     URL toURL();
 
     /**
-     * Returns a localized version of the resource. May return null if no such resource exists.
+     * Returns a localized version of the resource. May return null if no such resource exists. Starting in release
+     * 5.3.4, the result of this method is cached internally.
      */
     Resource forLocale(Locale locale);
 
@@ -63,7 +66,8 @@ public interface Resource
      * Returns a new Resource with the extension changed (or, if the resource does not have an extension, the extension
      * is added). The new Resource may not exist (that is, {@link #toURL()} may return null.
      *
-     * @param extension to apply to the resource, such as "html" or "properties"
+     * @param extension
+     *         to apply to the resource, such as "html" or "properties"
      * @return the new resource
      */
     Resource withExtension(String extension);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
index d376514..f873eec 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2008, 2010, 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
 
 package org.apache.tapestry5.ioc.internal.util;
 
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
+
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Locale;
-
-import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * Abstract implementation of {@link Resource}. Subclasses must implement the abstract methods {@link Resource#toURL()}
@@ -29,8 +30,35 @@ import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
  */
 public abstract class AbstractResource implements Resource
 {
+    private class Localization
+    {
+        final Locale locale;
+
+        final Resource resource;
+
+        final Localization next;
+
+        private Localization(Locale locale, Resource resource, Localization next)
+        {
+            this.locale = locale;
+            this.resource = resource;
+            this.next = next;
+        }
+    }
+
     private final String path;
 
+    /**
+     * A lock used to when lazily computing other values.
+     */
+    protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+    // Guarded by Lock
+    private boolean exists, existsComputed;
+
+    // Guarded by lock
+    private Localization firstLocalization;
+
     protected AbstractResource(String path)
     {
         assert path != null;
@@ -99,6 +127,69 @@ public abstract class AbstractResource implements Resource
 
     public final Resource forLocale(Locale locale)
     {
+        try
+        {
+            lock.readLock().lock();
+
+            for (Localization l = firstLocalization; l != null; l = l.next)
+            {
+                if (l.locale.equals(locale))
+                {
+                    return l.resource;
+                }
+            }
+
+            return populateLocalizationCache(locale);
+        } finally
+        {
+            lock.readLock().unlock();
+        }
+    }
+
+    private Resource populateLocalizationCache(Locale locale)
+    {
+        try
+        {
+            upgradeReadLockToWriteLock();
+
+            // Race condition: another thread may have beaten us to it:
+
+            for (Localization l = firstLocalization; l != null; l = l.next)
+            {
+                if (l.locale.equals(locale))
+                {
+                    return l.resource;
+                }
+            }
+
+            Resource result = findLocalizedResource(locale);
+
+            firstLocalization = new Localization(locale, result, firstLocalization);
+
+            return result;
+
+        } finally
+        {
+            downgradeWriteLockToReadLock();
+        }
+    }
+
+    protected final void upgradeReadLockToWriteLock()
+    {
+        lock.readLock().unlock();
+        // This is that instant where another thread may grab the write lock. Very rare, but possible.
+        lock.writeLock().lock();
+    }
+
+    protected final void downgradeWriteLockToReadLock()
+    {
+
+        lock.readLock().lock();
+        lock.writeLock().unlock();
+    }
+
+    private Resource findLocalizedResource(Locale locale)
+    {
         for (String path : new LocalizedNameGenerator(this.path, locale))
         {
             Resource potential = createResource(path);
@@ -138,7 +229,37 @@ public abstract class AbstractResource implements Resource
      */
     public boolean exists()
     {
-        return toURL() != null;
+        try
+        {
+            lock.readLock().lock();
+
+            if (!existsComputed)
+            {
+                computeExists();
+            }
+
+            return exists;
+        } finally
+        {
+            lock.readLock().unlock();
+        }
+    }
+
+    private void computeExists()
+    {
+        try
+        {
+            upgradeReadLockToWriteLock();
+
+            if (!existsComputed)
+            {
+                exists = toURL() != null;
+                existsComputed = true;
+            }
+        } finally
+        {
+            downgradeWriteLockToReadLock();
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
index 85eb8cb..a6d21ae 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
@@ -14,10 +14,10 @@
 
 package org.apache.tapestry5.ioc.internal.util;
 
-import java.net.URL;
-
 import org.apache.tapestry5.ioc.Resource;
 
+import java.net.URL;
+
 /**
  * Implementation of {@link Resource} for files on the classpath (as defined by a {@link ClassLoader}).
  */
@@ -25,10 +25,10 @@ public final class ClasspathResource extends AbstractResource
 {
     private final ClassLoader classLoader;
 
-    // Guarded by this
+    // Guarded by lock
     private URL url;
 
-    // Guarded by this
+    // Guarded by lock
     private boolean urlResolved;
 
     public ClasspathResource(String path)
@@ -50,15 +50,39 @@ public final class ClasspathResource extends AbstractResource
         return new ClasspathResource(classLoader, path);
     }
 
-    public synchronized URL toURL()
+    public URL toURL()
     {
-        if (!urlResolved)
+        try
+        {
+            lock.readLock().lock();
+
+            if (!urlResolved)
+            {
+                resolveURL();
+            }
+
+            return url;
+        } finally
         {
-            url = classLoader.getResource(getPath());
-            urlResolved = true;
+            lock.readLock().unlock();
         }
+    }
 
-        return url;
+    private void resolveURL()
+    {
+        try
+        {
+            upgradeReadLockToWriteLock();
+
+            if (!urlResolved)
+            {
+                url = classLoader.getResource(getPath());
+                urlResolved = true;
+            }
+        } finally
+        {
+            downgradeWriteLockToReadLock();
+        }
     }
 
     @Override