Changeset 12451


Ignore:
Timestamp:
02/12/10 11:08:20 (13 years ago)
Author:
Mark Evenson
Message:

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?.

Location:
trunk/abcl/src/org/armedbear/lisp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/Autoload.java

    r12441 r12451  
    607607        autoload(PACKAGE_SYS, "default-time-zone", "Time");
    608608        autoload(PACKAGE_SYS, "disassemble-class-bytes", "disassemble_class_bytes", true);
     609        autoload(PACKAGE_SYS, "disable-zip-cache", "ZipCache", true);
    609610        autoload(PACKAGE_SYS, "double-float-high-bits", "FloatFunctions", true);
    610611        autoload(PACKAGE_SYS, "double-float-low-bits", "FloatFunctions", true);
  • trunk/abcl/src/org/armedbear/lisp/ZipCache.java

    r12448 r12451  
    3737
    3838import java.io.File;
     39import java.io.InputStream;
    3940import java.io.IOException;
    4041import java.net.JarURLConnection;
     
    4243import java.net.URL;
    4344import java.net.URLConnection;
     45import java.util.Enumeration;
    4446import java.util.HashMap;
    4547import java.util.zip.ZipException;
    4648import java.util.zip.ZipFile;
     49import java.util.zip.ZipEntry;
    4750
    4851/**
    4952 * A cache for all zip/jar file accesses by URL that uses the last
    5053 * modified time of the cached resource.
     54 *
     55 * This implementation is NOT thread safe, although usage without
     56 * multiple threads recompiling code that is then re-loaded should be
     57 * fine.
     58 *
     59 * If you run into problems with caching, use
     60 * (SYS::DISABLE-ZIP-CACHE).  Once disabled, the caching cannot be
     61 * re-enabled.
     62 *
    5163 */
    5264public class ZipCache {
    53   static class Entry {
    54     long lastModified;
    55     ZipFile file;
    56   }
    57    
    58   static HashMap<URL, Entry> zipCache = new HashMap<URL, Entry>();
    59 
    60   public static ZipFile get(LispObject arg) {
    61       return get(Pathname.makeURL(arg));
    62   }
    63  
    64   public static ZipFile get(URL url) {
    65       Entry entry = zipCache.get(url);
    66       if (entry != null) {
    67           if (url.getProtocol().equals("file")) {
    68               File f = new File(url.getPath());
     65
     66    // To make this thread safe, we should return a proxy for ZipFile
     67    // that keeps track of the number of outstanding references handed
     68    // out, not allowing ZipFile.close() to succeed until that count
     69    // has been reduced to 1 or the finalizer is executing.
     70    // Unfortunately the relatively simple strategy of extended
     71    // ZipFile via a CachedZipFile does not work because there is not
     72    // a null arg constructor for ZipFile.
     73    static class Entry {
     74        long lastModified;
     75        ZipFile file;
     76    }
     77
     78    static boolean cacheEnabled = true;
     79
     80    private final static Primitive DISABLE_ZIP_CACHE = new disable_zip_cache();
     81    final static class disable_zip_cache extends Primitive {
     82        disable_zip_cache() {
     83            super("disable-zip-cache", PACKAGE_SYS, true, "",
     84                  "Disable all caching of ABCL FASLs and ZIPs.");
     85        }
     86        @Override
     87        public LispObject execute() {
     88            ZipCache.disable();
     89            return T;
     90        }
     91    }
     92
     93    static public synchronized void disable() {
     94        cacheEnabled = false;
     95        zipCache.clear(); 
     96    }
     97
     98    static HashMap<URL, Entry> zipCache = new HashMap<URL, Entry>();
     99
     100    synchronized public static ZipFile get(LispObject arg) {
     101        return get(Pathname.makeURL(arg));
     102    }
     103
     104    synchronized public static ZipFile get(final URL url) {
     105        if (!cacheEnabled) {
     106            if (url.getProtocol().equals("file")) {
     107                File f = new File(url.getPath());
     108                try {
     109                    return new ZipFile(f);
     110                } catch (ZipException e) {
     111                    Debug.trace(e); // XXX
     112                    return null;
     113                } catch (IOException e) {
     114                    Debug.trace(e); // XXX
     115                    return null;
     116                }
     117            } else {
     118                Entry e = fetchURL(url, false);
     119                return e.file;
     120            }
     121        }               
     122
     123        Entry entry = zipCache.get(url);
     124
     125        // Check that the cache entry still accesses a valid ZipFile
     126        if (entry != null) {
     127            // Simplest way to call private ZipFile.ensureOpen()
     128            try {
     129                int size = entry.file.size();
     130            } catch (IllegalStateException e) {
     131                zipCache.remove(url);
     132                entry = null;
     133            }
     134        }
     135
     136        if (entry != null) {
     137            if (url.getProtocol().equals("file")) {
     138                File f = new File(url.getPath());
    69139                long current = f.lastModified();
    70140                if (current > entry.lastModified) {
    71141                    try {
    72                     entry.file.close();
    73                     entry.file = new ZipFile(f);
    74                     entry.lastModified = current;
     142                        entry.file = new ZipFile(f);
     143                        entry.lastModified = current;
    75144                    } catch (IOException e) {
    76145                        Debug.trace(e.toString()); // XXX
     
    78147                }
    79148            } else {
    80               // Unfortunately, the Apple JDK under OS X doesn't do
    81               // HTTP HEAD requests, instead refetching the entire
    82               // resource, so the following code is a waste.  I assume
    83               // this is the case in all Sun-dervied JVMs. We'll have
    84               // to implement a custom HTTP lastModified checker.
    85 
    86               // URLConnection connection;
    87               // try {
    88               //     connection = url.openConnection();
    89               // } catch (IOException ex) {
    90               //     Debug.trace("Failed to open "
    91               //                 + "'" + url + "'");
    92               //     return null;
    93               // }
    94               // long current = connection.getLastModified();
    95               // if (current > entry.lastModified) {
    96               //     try {
    97               //         entry.file.close();
    98               //     } catch (IOException ex) {}
    99               //     entry = fetchURL(url, false);
    100               // }
     149                // Unfortunately, the Apple JDK under OS X doesn't do
     150                // HTTP HEAD requests, instead refetching the entire
     151                // resource, so the following code is a waste.  I assume
     152                // this is the case in all Sun-dervied JVMs. We'll have
     153                // to implement a custom HTTP lastModified checker.
     154
     155                // URLConnection connection;
     156                // try {
     157                //     connection = url.openConnection();
     158                // } catch (IOException ex) {
     159                //     Debug.trace("Failed to open "
     160                //                 + "'" + url + "'");
     161                //     return null;
     162                // }
     163                // long current = connection.getLastModified();
     164                // if (current > entry.lastModified) {
     165                //     try {
     166                //         entry.file.close();
     167                //     } catch (IOException ex) {}
     168                //     entry = fetchURL(url, false);
     169                // }
    101170            }
    102171        } else {
    103            if (url.getProtocol().equals("file")) {
     172            if (url.getProtocol().equals("file")) {
    104173                entry = new Entry();
    105174                File f = new File(url.getPath());
     
    120189        }
    121190        return entry.file;
    122   }
     191    }
    123192     
    124       static private Entry fetchURL(URL url, boolean cached) {
    125           Entry result = new Entry();
    126           URL jarURL = null;
    127           try {
    128               jarURL = new URL("jar:" + url + "!/");
    129           } catch (MalformedURLException e) {
    130               Debug.trace(e);
    131               Debug.assertTrue(false); // XXX
    132           }
    133           URLConnection connection;
    134           try {
    135               connection = jarURL.openConnection();
    136           } catch (IOException ex) {
    137               Debug.trace("Failed to open "
    138                           + "'" + jarURL + "'");
    139               return null;
    140           }
    141           if (!(connection instanceof JarURLConnection)) {
    142               // XXX
    143               Debug.trace("Could not get a URLConnection from " + jarURL);
    144               return null;
    145           }
    146           JarURLConnection jarURLConnection = (JarURLConnection) connection;
    147           jarURLConnection.setUseCaches(cached);
    148           try {
    149               result.file = jarURLConnection.getJarFile();
    150           } catch (IOException e) {
    151               Debug.trace(e);
    152               Debug.assertTrue(false); // XXX
    153           }
    154           result.lastModified = jarURLConnection.getLastModified();
    155           return result;
    156       }
    157 
    158 
    159 
    160   // ## remove-zip-cache-entry pathname => boolean
    161   private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
    162   private static class remove_zip_cache_entry extends Primitive {
    163     remove_zip_cache_entry() {
    164       super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
    165     }
    166     @Override
    167     public LispObject execute(LispObject arg) {
    168       Pathname p = coerceToPathname(arg);
    169       boolean result = ZipCache.remove(p);
    170       return result ? T : NIL;
    171     }
    172   }
     193    static private Entry fetchURL(URL url, boolean cached) {
     194        Entry result = new Entry();
     195        URL jarURL = null;
     196        try {
     197            jarURL = new URL("jar:" + url + "!/");
     198        } catch (MalformedURLException e) {
     199            Debug.trace(e);
     200            Debug.assertTrue(false); // XXX
     201        }
     202        URLConnection connection;
     203        try {
     204            connection = jarURL.openConnection();
     205        } catch (IOException ex) {
     206            Debug.trace("Failed to open "
     207                        + "'" + jarURL + "'");
     208            return null;
     209        }
     210        if (!(connection instanceof JarURLConnection)) {
     211            // XXX
     212            Debug.trace("Could not get a URLConnection from " + jarURL);
     213            return null;
     214        }
     215        JarURLConnection jarURLConnection = (JarURLConnection) connection;
     216        jarURLConnection.setUseCaches(cached);
     217        try {
     218            result.file = jarURLConnection.getJarFile();
     219        } catch (IOException e) {
     220            Debug.trace(e);
     221            Debug.assertTrue(false); // XXX
     222        }
     223        result.lastModified = jarURLConnection.getLastModified();
     224        return result;
     225    }
     226
     227    // ## remove-zip-cache-entry pathname => boolean
     228    private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
     229    private static class remove_zip_cache_entry extends Primitive {
     230        remove_zip_cache_entry() {
     231            super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
     232        }
     233        @Override
     234        public LispObject execute(LispObject arg) {
     235            Pathname p = coerceToPathname(arg);
     236            boolean result = ZipCache.remove(p);
     237            return result ? T : NIL;
     238        }
     239    }
    173240     
    174   public static boolean remove(URL url) {
    175     Entry entry = zipCache.get(url);
    176     if (entry != null) {
    177       try {
    178         entry.file.close();
    179       } catch (IOException e) {}
    180       zipCache.remove(entry);
    181       return true;
    182     }
    183     return false;
    184   }
    185 
    186   public static boolean remove(Pathname p) {
    187     URL url = Pathname.makeURL(p);
    188     if (url == null) {
    189       return false;
    190     }
    191     return ZipCache.remove(url);
    192   }
    193 
    194   public static boolean remove(File f) {
    195     Pathname p = Pathname.makePathname(f);
    196     return ZipCache.remove(p);
    197   }
    198    
     241    synchronized public static boolean remove(URL url) {
     242        Entry entry = zipCache.get(url);
     243        if (entry != null) {
     244            try {
     245                entry.file.close();
     246            } catch (IOException e) {}
     247            zipCache.remove(entry);
     248            return true;
     249        }
     250        return false;
     251    }
     252
     253    synchronized public static boolean remove(Pathname p) {
     254        URL url = Pathname.makeURL(p);
     255        if (url == null) {
     256            return false;
     257        }
     258        return ZipCache.remove(url);
     259    }
     260
     261    synchronized public static boolean remove(File f) {
     262        Pathname p = Pathname.makePathname(f);
     263        return ZipCache.remove(p);
     264    }
    199265}
Note: See TracChangeset for help on using the changeset viewer.