[armedbear-cvs] r12451 - trunk/abcl/src/org/armedbear/lisp

Mark Evenson mevenson at common-lisp.net
Fri Feb 12 11:08:21 UTC 2010


Author: mevenson
Date: Fri Feb 12 06:08:20 2010
New Revision: 12451

Log:
Fix for ZipException under interpreted ANSI tests.

Check that the cache entries still accesses an open ZipFile when it is
handed out.

Use SYS:DISABLE-ZIP-CACHE to disable the ZipCache entirely.

Implemented some notion of thread synchronization, although we cannot
guard against the case where two or more references to a ZipFile
exist, and one thread closes the ZipFile.



Modified:
   trunk/abcl/src/org/armedbear/lisp/Autoload.java
   trunk/abcl/src/org/armedbear/lisp/ZipCache.java

Modified: trunk/abcl/src/org/armedbear/lisp/Autoload.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/Autoload.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/Autoload.java	Fri Feb 12 06:08:20 2010
@@ -606,6 +606,7 @@
         autoload(PACKAGE_SYS, "create-new-file", "create_new_file");
         autoload(PACKAGE_SYS, "default-time-zone", "Time");
         autoload(PACKAGE_SYS, "disassemble-class-bytes", "disassemble_class_bytes", true);
+        autoload(PACKAGE_SYS, "disable-zip-cache", "ZipCache", true);
         autoload(PACKAGE_SYS, "double-float-high-bits", "FloatFunctions", true);
         autoload(PACKAGE_SYS, "double-float-low-bits", "FloatFunctions", true);
         autoload(PACKAGE_SYS, "float-infinity-p", "FloatFunctions", true);

Modified: trunk/abcl/src/org/armedbear/lisp/ZipCache.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/ZipCache.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/ZipCache.java	Fri Feb 12 06:08:20 2010
@@ -36,71 +36,140 @@
 import static org.armedbear.lisp.Lisp.*;
 
 import java.io.File;
+import java.io.InputStream;
 import java.io.IOException;
 import java.net.JarURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
 
 /**
  * A cache for all zip/jar file accesses by URL that uses the last
  * modified time of the cached resource.
+ *
+ * This implementation is NOT thread safe, although usage without
+ * multiple threads recompiling code that is then re-loaded should be
+ * fine.
+ *
+ * If you run into problems with caching, use
+ * (SYS::DISABLE-ZIP-CACHE).  Once disabled, the caching cannot be
+ * re-enabled.
+ *
  */ 
 public class ZipCache {
-  static class Entry {
-    long lastModified;
-    ZipFile file;
-  }
-    
-  static HashMap<URL, Entry> zipCache = new HashMap<URL, Entry>();
-
-  public static ZipFile get(LispObject arg) {
-      return get(Pathname.makeURL(arg));
-  }
-  
-  public static ZipFile get(URL url) {
-      Entry entry = zipCache.get(url);
-      if (entry != null) {
-          if (url.getProtocol().equals("file")) {
-              File f = new File(url.getPath());
+
+    // To make this thread safe, we should return a proxy for ZipFile
+    // that keeps track of the number of outstanding references handed
+    // out, not allowing ZipFile.close() to succeed until that count
+    // has been reduced to 1 or the finalizer is executing.
+    // Unfortunately the relatively simple strategy of extended
+    // ZipFile via a CachedZipFile does not work because there is not
+    // a null arg constructor for ZipFile.
+    static class Entry {
+        long lastModified;
+        ZipFile file;
+    }
+
+    static boolean cacheEnabled = true;
+
+    private final static Primitive DISABLE_ZIP_CACHE = new disable_zip_cache();
+    final static class disable_zip_cache extends Primitive {
+        disable_zip_cache() {
+            super("disable-zip-cache", PACKAGE_SYS, true, "",
+                  "Disable all caching of ABCL FASLs and ZIPs.");
+        }
+        @Override
+        public LispObject execute() {
+            ZipCache.disable();
+            return T;
+        }
+    }
+
+    static public synchronized void disable() {
+        cacheEnabled = false;
+        zipCache.clear();  
+    }
+
+    static HashMap<URL, Entry> zipCache = new HashMap<URL, Entry>();
+
+    synchronized public static ZipFile get(LispObject arg) {
+        return get(Pathname.makeURL(arg));
+    }
+
+    synchronized public static ZipFile get(final URL url) {
+        if (!cacheEnabled) {
+            if (url.getProtocol().equals("file")) {
+                File f = new File(url.getPath());
+                try { 
+                    return new ZipFile(f);
+                } catch (ZipException e) {
+                    Debug.trace(e); // XXX
+                    return null;
+                } catch (IOException e) {
+                    Debug.trace(e); // XXX
+                    return null;
+                }
+            } else {
+                Entry e = fetchURL(url, false);
+                return e.file;
+            }
+        }                
+
+        Entry entry = zipCache.get(url);
+
+        // Check that the cache entry still accesses a valid ZipFile
+        if (entry != null) {
+            // Simplest way to call private ZipFile.ensureOpen()
+            try {
+                int size = entry.file.size(); 
+            } catch (IllegalStateException e) {
+                zipCache.remove(url);
+                entry = null;
+            }
+        }
+
+        if (entry != null) {
+            if (url.getProtocol().equals("file")) {
+                File f = new File(url.getPath());
                 long current = f.lastModified();
                 if (current > entry.lastModified) {
                     try {
-                    entry.file.close(); 
-                    entry.file = new ZipFile(f);
-                    entry.lastModified = current;
+                        entry.file = new ZipFile(f);
+                        entry.lastModified = current;
                     } catch (IOException e) {
                         Debug.trace(e.toString()); // XXX
                     }
                 }
             } else {
-              // Unfortunately, the Apple JDK under OS X doesn't do
-              // HTTP HEAD requests, instead refetching the entire
-              // resource, so the following code is a waste.  I assume
-              // this is the case in all Sun-dervied JVMs. We'll have
-              // to implement a custom HTTP lastModified checker.
-
-              // URLConnection connection;
-              // try {
-              //     connection = url.openConnection();
-              // } catch (IOException ex) {
-              //     Debug.trace("Failed to open "
-              //                 + "'" + url + "'");
-              //     return null;
-              // }
-              // long current = connection.getLastModified();
-              // if (current > entry.lastModified) {
-              //     try {
-              //         entry.file.close();
-              //     } catch (IOException ex) {}
-              //     entry = fetchURL(url, false);
-              // }
+                // Unfortunately, the Apple JDK under OS X doesn't do
+                // HTTP HEAD requests, instead refetching the entire
+                // resource, so the following code is a waste.  I assume
+                // this is the case in all Sun-dervied JVMs. We'll have
+                // to implement a custom HTTP lastModified checker.
+
+                // URLConnection connection;
+                // try {
+                //     connection = url.openConnection();
+                // } catch (IOException ex) {
+                //     Debug.trace("Failed to open "
+                //                 + "'" + url + "'");
+                //     return null;
+                // }
+                // long current = connection.getLastModified();
+                // if (current > entry.lastModified) {
+                //     try {
+                //         entry.file.close();
+                //     } catch (IOException ex) {}
+                //     entry = fetchURL(url, false);
+                // }
             }
         } else {
-           if (url.getProtocol().equals("file")) {
+            if (url.getProtocol().equals("file")) {
                 entry = new Entry();
                 File f = new File(url.getPath());
                 entry.lastModified = f.lastModified();
@@ -119,81 +188,78 @@
             zipCache.put(url, entry);
         }
         return entry.file;
-  }
+    }
       
-      static private Entry fetchURL(URL url, boolean cached) {
-          Entry result = new Entry();
-          URL jarURL = null;
-          try {
-              jarURL = new URL("jar:" + url + "!/");
-          } catch (MalformedURLException e) {
-              Debug.trace(e);
-              Debug.assertTrue(false); // XXX
-          }
-          URLConnection connection;
-          try {
-              connection = jarURL.openConnection();
-          } catch (IOException ex) {
-              Debug.trace("Failed to open "
-                          + "'" + jarURL + "'");
-              return null;
-          }
-          if (!(connection instanceof JarURLConnection)) {
-              // XXX
-              Debug.trace("Could not get a URLConnection from " + jarURL);
-              return null;
-          }
-          JarURLConnection jarURLConnection = (JarURLConnection) connection;
-          jarURLConnection.setUseCaches(cached);
-          try {
-              result.file = jarURLConnection.getJarFile();
-          } catch (IOException e) {
-              Debug.trace(e);
-              Debug.assertTrue(false); // XXX
-          }
-          result.lastModified = jarURLConnection.getLastModified();
-          return result;
-      }
+    static private Entry fetchURL(URL url, boolean cached) {
+        Entry result = new Entry();
+        URL jarURL = null;
+        try {
+            jarURL = new URL("jar:" + url + "!/");
+        } catch (MalformedURLException e) {
+            Debug.trace(e);
+            Debug.assertTrue(false); // XXX
+        }
+        URLConnection connection;
+        try {
+            connection = jarURL.openConnection();
+        } catch (IOException ex) {
+            Debug.trace("Failed to open "
+                        + "'" + jarURL + "'");
+            return null;
+        }
+        if (!(connection instanceof JarURLConnection)) {
+            // XXX
+            Debug.trace("Could not get a URLConnection from " + jarURL);
+            return null;
+        }
+        JarURLConnection jarURLConnection = (JarURLConnection) connection;
+        jarURLConnection.setUseCaches(cached);
+        try {
+            result.file = jarURLConnection.getJarFile();
+        } catch (IOException e) {
+            Debug.trace(e);
+            Debug.assertTrue(false); // XXX
+        }
+        result.lastModified = jarURLConnection.getLastModified();
+        return result;
+    }
 
+    // ## remove-zip-cache-entry pathname => boolean
+    private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
+    private static class remove_zip_cache_entry extends Primitive { 
+        remove_zip_cache_entry() {
+            super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
+        }
+        @Override
+        public LispObject execute(LispObject arg) {
+            Pathname p = coerceToPathname(arg);
+            boolean result = ZipCache.remove(p);
+            return result ? T : NIL;
+        }
+    }
+      
+    synchronized public static boolean remove(URL url) {
+        Entry entry = zipCache.get(url);
+        if (entry != null) {
+            try {
+                entry.file.close();
+            } catch (IOException e) {}
+            zipCache.remove(entry);
+            return true;
+        }
+        return false;
+    }
 
+    synchronized public static boolean remove(Pathname p) {
+        URL url = Pathname.makeURL(p);
+        if (url == null) {
+            return false;
+        }
+        return ZipCache.remove(url);
+    }
 
-  // ## remove-zip-cache-entry pathname => boolean
-  private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
-  private static class remove_zip_cache_entry extends Primitive { 
-    remove_zip_cache_entry() {
-      super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
-    }
-    @Override
-    public LispObject execute(LispObject arg) {
-      Pathname p = coerceToPathname(arg);
-      boolean result = ZipCache.remove(p);
-      return result ? T : NIL;
+    synchronized public static boolean remove(File f) {
+        Pathname p = Pathname.makePathname(f);
+        return ZipCache.remove(p);
     }
-  }
-      
-  public static boolean remove(URL url) {
-    Entry entry = zipCache.get(url);
-    if (entry != null) {
-      try {
-        entry.file.close();
-      } catch (IOException e) {}
-      zipCache.remove(entry);
-      return true;
-    }
-    return false;
-  }
-
-  public static boolean remove(Pathname p) {
-    URL url = Pathname.makeURL(p);
-    if (url == null) {
-      return false;
-    }
-    return ZipCache.remove(url);
-  }
-
-  public static boolean remove(File f) {
-    Pathname p = Pathname.makePathname(f);
-    return ZipCache.remove(p);
-  }
-    
 }
\ No newline at end of file




More information about the armedbear-cvs mailing list