Concurrent cache

A similar class to the LockingLazyVar class is the Cache class. An instance of this class declares a concurrent cache of values that you can look up quickly and in a thread-safe way. The cache is similar to a Least Recently Used (LRU) cache. Because the Cache class uses the Java concurrency libraries, access to the cache is thread-safe. Each entry in the cache is defined by a key, which is also called an input value. A block uses the value of the key to calculate the value of the cache entry.

The constructor for a cache requires:

  • A name as a String. The implementation uses this name to generate logging for cache misses.
  • The size, as a number of slots.
  • A block that defines a function to calculate a value from a key. Typically, this calculation is resource-intensive.

Use the key and value types to parameterize the Cache type using Gosu generics syntax. For example, if you need to pass a String to the cache and get an Integer back, create a new Cache<String, Integer>.

To use the cache, call the get method and pass an input value, which is the key. If the value for the key is in the cache, get returns that value. If the value is not cached, Gosu calls the block to calculate the value from the key and then caches the result. On any subsequent call of the get method, the value is the same but Gosu uses the cached value, if it is still in the cache. If too many items were added to the cache and the required item is unavailable, Gosu reruns the block to regenerate the value. Gosu then caches the result again.

To use a cache within another class, you can define a static instance of the cache. The static variable definition defines your block. Because the Cache class uses the Java concurrency libraries, this static implementation is thread-safe. For example, in your class definition, define a static variable like this:

static var _myCache = new Cache<String, Integer>( "StrToInt", 1000, \ str -> getMyInt( str ) )

To use your cache, get a value from the cache using code like the following. In this example, inputString is a String variable that may or may not contain a String that you used before with this cache:

var fastValue = _myCache.get( inputString )

The first time you call the get method, Gosu calls the block to generate the Integer value.

On any subsequent call of the get method, the value is the same but Gosu uses the cached value, if it is still in the cache. If too many items were added to the cache and the required item is unavailable, Gosu reruns the block to regenerate the value. Gosu then caches the result again in the concurrent cache object.

An even better way to use the cache is to abstract the cache implementation into a property accessor function. A private static object Cache object, as shown in the previous example, can handle the actual cache. For example, define a property accessor function such as:

static property get function MyInt( str : String ) {
  return _myCache.get( str )
}

These code examples are demonstrations that have a simple operation in the block. Typically, the overhead of maintaining the cache is appropriate if your calculation is resource-intensive and you expect repeated access with the same input values.

Warning: Incorrect implementation of caching can lead to run-time errors and data corruption. Only use caches as a last resort for performance issues.