diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2016-03-13 03:18:18 -0400 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2016-03-19 21:13:48 -0400 |
commit | c095f435b68272d4ae0409ab4c9466145609710e (patch) | |
tree | 189f0f2755d8fd2e6af3eec9d514c29b3cebdef5 /stage1/ClassLoaderCache.scala | |
parent | ca7e166e09776410ef39e2808aab6a3fdd1e7911 (diff) | |
download | cbt-c095f435b68272d4ae0409ab4c9466145609710e.tar.gz cbt-c095f435b68272d4ae0409ab4c9466145609710e.tar.bz2 cbt-c095f435b68272d4ae0409ab4c9466145609710e.zip |
Refactored ClassLoaderCache to use key locked cache to pave the way for caching classloaders hierarchically without deadlocks
Diffstat (limited to 'stage1/ClassLoaderCache.scala')
-rw-r--r-- | stage1/ClassLoaderCache.scala | 69 |
1 files changed, 52 insertions, 17 deletions
diff --git a/stage1/ClassLoaderCache.scala b/stage1/ClassLoaderCache.scala index 18a0d0e..35008f0 100644 --- a/stage1/ClassLoaderCache.scala +++ b/stage1/ClassLoaderCache.scala @@ -1,25 +1,60 @@ package cbt import java.net._ +import java.util.concurrent.ConcurrentHashMap -private[cbt] object ClassLoaderCache{ - private val cache = NailgunLauncher.classLoaderCache - def get( classpath: ClassPath )(implicit logger: Logger): ClassLoader - = cache.synchronized{ - val lib = new Stage1Lib(logger) - val key = classpath.strings.sorted.mkString(":") - if( cache.containsKey(key) ){ - logger.resolver("CACHE HIT: "++key) - cache.get(key) - } else { - logger.resolver("CACHE MISS: "++key) - val cl = new cbt.URLClassLoader( classpath, ClassLoader.getSystemClassLoader ) - cache.put( key, cl ) - cl +class ClassLoaderCache(logger: Logger){ + val permanent = new KeyLockedLazyCache( + NailgunLauncher.classLoaderCache.asInstanceOf[ConcurrentHashMap[String,AnyRef]], + NailgunLauncher.classLoaderCache.asInstanceOf[ConcurrentHashMap[AnyRef,ClassLoader]], + logger + ) + val transient = new KeyLockedLazyCache( + new ConcurrentHashMap[String,AnyRef], + new ConcurrentHashMap[AnyRef,ClassLoader], + logger + ) +} + +private[cbt] class LockableKey +/** +A cache that lazily computes values if needed during lookup. +Locking occurs on the key, so separate keys can be looked up +simultaneously without a deadlock. +*/ +final private[cbt] class KeyLockedLazyCache[Key <: AnyRef,Value <: AnyRef]( + keys: ConcurrentHashMap[Key,AnyRef], + builds: ConcurrentHashMap[AnyRef,Value], + logger: Logger +){ + def get( key: Key, value: => Value ): Value = { + val keyObject = keys.synchronized{ + if( ! (keys containsKey key) ){ + logger.resolver("CACHE MISS: " ++ key.toString) + keys.put( key, new LockableKey ) + } else { + logger.resolver("CACHE HIT: " ++ key.toString) + } + keys get key + } + import collection.JavaConversions._ + logger.resolver("CACHE: \n" ++ keys.mkString("\n")) + def k = ClassPath(new java.io.File("c")).asInstanceOf[Key] + // synchronizing on key only, so asking for a particular key does + // not block the whole cache, but just that cache entry + key.synchronized{ + if( ! (builds containsKey keyObject) ){ + builds.put( keyObject, value ) + } + builds get keyObject } } - def remove( classpath: ClassPath ) = { - val key = classpath.strings.sorted.mkString(":") - cache.remove( key ) + def remove( key: Key ) = keys.synchronized{ + if( (keys containsKey key) ){ + keys.put( key, new LockableKey ) + } + val keyObject = keys get key + keys.remove( key ) + builds.remove( keyObject ) } } |