High-level, my major issues at this point are: 1) MemoryUnitCache having anything to do with the on-disk cache.
2) Using PUC from entry points other than DevMode and Compiler (although we should seriously consider the JUnitShell use case). 3) Caching by type name instead of resource location. On 2011/03/22 17:33:02, zundel wrote:
Uploaded a new patch that adds a unit test for the persistent cache
and fixes
the problem unit testing the type oracle. After discussion with
tobyr,
modified the unitMap and unitMapByContentId to be HARD key, SOFT
value. +1 http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/GWTCompiler.java File dev/core/src/com/google/gwt/dev/GWTCompiler.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/GWTCompiler.java#newcode179 dev/core/src/com/google/gwt/dev/GWTCompiler.java:179: tempWorkDir = true; I'm coming around to thinking that only DevMode and Compiler (both of which have a war dir) are the only entry points that should use PUC at all. I don't see any real use for it from any other entry point, it just makes things more complicated. http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/Precompile.java File dev/core/src/com/google/gwt/dev/Precompile.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/Precompile.java#newcode664 dev/core/src/com/google/gwt/dev/Precompile.java:664: CompilationStateBuilder.init(logger, options.getWorkDir()); Or we just disable PUC from those entry points, which kind of makes sense anyway. If you're doing something sophisticated with Precompile, you're ultimately going to want to use CompileModule anyway instead of PUC. "war or nothing" seems like a pretty simple and effective strategy to me. http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java File dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java (left): http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java#oldcode394 dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java:394: ResourceTag tag = resourceContentCache.get(location); Glad you fixed this. NFS, virus scanners, disk encryption, can all make an impact here. (I'm particularly aware of this angle since one of my machines is a Windows with virus scanning & disk encryption, and disk access is much, much slower than on a similar Linux system for me.) http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java File dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java#newcode324 dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java:324: } That should be fine as long as the cacheDirectory is the same for all invocations, right? Seems bad to not check. http://gwt-code-reviews.appspot.com/1375802/diff/9001/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java#newcode350 dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java:350: } Sorry, I didn't very clearly make the point that you just made, which is that it's so cheap to instantiate an in-memory cache, you might as well just auto-initialize the in-memory cache and replace it later if necessary. Persistent unit cache doesn't make sense for unit tests. For directly testing PUC itself you'd set it up manually, and for anything else you'd rather have the hermeticism and decoupling. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java#newcode339 dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java:339: * My one issue here is that this used to lookup by location, not type name. Doing it by location means that different modules with different super source paths can coexist peacefully. Doing it by type name means that two different resources with the same logical type name end up stepping on each other. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java#newcode340 dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java:340: * TODO: maybe use a finer brush than to synchronize the whole thing. Yay, nice! If it's stale, should we go ahead and evict the stale unit to free up memory (since we're about to compile, which will need a lot of memory)? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java File dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java#newcode184 dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java:184: return lastModified; Unlike SourceFileCompilationUnit, I wouldn't think this one would need this change? GeneratedUnit should handle consistency. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java File dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java#newcode44 dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java:44: private final UnitOrigin source; rename field and getter 'source' -> 'origin' too? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java#newcode90 dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java:90: protected final Map<String, UnitCacheEntry> unitMap = Collections Document the semantic meaning of the key? e.g. unitMapByXXX? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java#newcode95 dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java:95: // compareTo() and hashCode(), but it looks like its failing in this case. This comment still true? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java#newcode108 dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java:108: * from the system property <code>java.io.tmpdir</code>. This still seems way unintuitive to me. Is there no better way to handle this? MemoryUnitCache shouldn't import java.io.File at all, IMHO. Why should every unit test that uses MemoryUnitCache affect the disk? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java#newcode111 dev/core/src/com/google/gwt/dev/javac/MemoryUnitCache.java:111: String propertyCacheDir = System.getProperty("gwt.persistentunitcachedir"); How does this relate to UnitCacheFactory.isPersistent? Are these supposed to be two different properties, or is this a left-over from a previous patch version? http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java File dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java#newcode237 dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java:237: } This block is typically rendered Utility.close(stream). http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java#newcode255 dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java:255: Runtime.getRuntime().removeShutdownHook(this); "this" is UnitWriteMessageThread, you'd need to save a reference to the original shutdown hook thread. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java#newcode323 dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java:323: if (unitCacheEntry.getSource() == UnitOrigin.PERSISTENT) { Once the unit is copied from an 'old' cache file into the current one, shouldn't its state get updated? Otherwise, multiple identical copies could get written into the current cache. Now that I think about it, it seems like UnitOrigin is really a flag between whether or not the unit has been written to the current cache file. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java#newcode401 dev/core/src/com/google/gwt/dev/javac/PersistentUnitCache.java:401: } Another Utility.close() http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java File dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java#newcode52 dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java:52: this.lastModified = sourceFile.getLastModified(); Yes, this was the exact reason ResourceTag/resourceContentCache existed. But you should move the initialization into getSource(). http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java#newcode70 dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java:70: String sourceCode = Shared.readSource(sourceFile); Initialize lastModified right before reading the source, to ensure the lastModified most closely matches the content. (Doing it before means you see newer content with an older mod time, which worst case means duplicate work later; reading first then reading the mod time means you potentially miss an update and the user gets stale code). http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/UnitCache.java File dev/core/src/com/google/gwt/dev/javac/UnitCache.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/UnitCache.java#newcode28 dev/core/src/com/google/gwt/dev/javac/UnitCache.java:28: static final String UNIT_CACHE_PREFIX = "gwt-unitCache"; 'static' and 'final' are also redundant http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java File dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java#newcode42 dev/core/src/com/google/gwt/dev/javac/UnitCacheFactory.java:42: return new PersistentUnitCache(logger, cacheDir); It occurs to me that every client that uses the same directory ought to share the same PUC. A weak map of File -> PUC seems correct here to get singleton behavior by File. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java File dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java#newcode12 dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java:12: private static AtomicInteger nextTimestamp = new AtomicInteger(1); final http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java#newcode28 dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java:28: return "Mock: " + typeName; In other places, we always use "/mock/" + typeName http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java File dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java (right): http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java#newcode17 dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java:17: cacheDir.deleteOnExit(); This works? The doc for File.delete() says it only works for empty directories, I would have though this directory would not be empty at the end of the test run. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java#newcode18 dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java:18: } catch (IOException ex) { Just add the throws to the unit test method, the original exception is generally better in test failure logs. http://gwt-code-reviews.appspot.com/1375802/diff/10027/dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java#newcode37 dev/core/test/com/google/gwt/dev/javac/PersistentUnitCacheTest.java:37: assertNotNull(result); Total opinion, but I tend to find that this level of pedantic checking makes the test code harder to read. The next line would throw an NPE anyway, so why clutter the test code? http://gwt-code-reviews.appspot.com/1375802/ -- http://groups.google.com/group/Google-Web-Toolkit-Contributors