blob: 47fdd888124671a7c910b92822adcfcf3d3f67ea (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package mill.api
/**
* A combination lock & cache; users provide a key, value-factory, and a
* body function to be called with the value. [[KeyedLockedCache]] ensures that
* the body function is called with the computed/cached value sequentially.
*/
trait KeyedLockedCache[T]{
def withCachedValue[V](key: Long)(f: => T)(f2: T => V): V
}
object KeyedLockedCache{
class RandomBoundedCache[T](hotParallelism: Int, coldCacheSize: Int) extends KeyedLockedCache[T]{
private[this] val random = new scala.util.Random(313373)
val available = new java.util.concurrent.Semaphore(hotParallelism)
// Awful asymptotic complexity, but our caches are tiny n < 10 so it doesn't matter
var cache = Array.fill[Option[(Long, T)]](coldCacheSize)(None)
def withCachedValue[V](key: Long)(f: => T)(f2: T => V): V = {
available.acquire()
val pickedValue = synchronized{
cache.indexWhere(_.exists(_._1 == key)) match {
case -1 => f
case i =>
val (k, v) = cache(i).get
cache(i) = None
v
}
}
val result = f2(pickedValue)
synchronized{
cache.indexWhere(_.isEmpty) match{
// Random eviction #YOLO
case -1 => cache(random.nextInt(cache.length)) = Some((key, pickedValue))
case i => cache(i) = Some((key, pickedValue))
}
}
available.release()
result
}
}
}
|