Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting a singleton calls the initializer() every time #1

Closed
apatrida opened this issue Aug 31, 2015 · 4 comments
Closed

Getting a singleton calls the initializer() every time #1

apatrida opened this issue Aug 31, 2015 · 4 comments

Comments

@apatrida
Copy link

When you call to get the singleton, the putIfAbsent() calls the initializer every time a get is done. That could cause unintended consequences. especially if the initializer is expensive or has side effect.

@vinc3m1
Copy link
Owner

vinc3m1 commented Aug 31, 2015

Good catch. Originally was using getOrPut but was concerned about thread safety. Will look into this more. Happy to accept Pull Requests if you have ideas.

@apatrida
Copy link
Author

Not perfect but the only hole is a race condition of N threads racing to initialize at same time could call factory more than once, but all will return same instance. Without JDK 8 with internal computeIfAbsent, it is harder without more locking to be surer. I'll see what M13 implementation looks like as well.

public inline fun <K, V> ConcurrentMap<K, V>.concurrentGetOrPut(key: K, defaultValue: () -> V): V {
    var answer = this.get(key)
    if (answer == null) {
        answer = defaultValue()
        var temp = this.putIfAbsent(key, answer!!)
        if (temp != null) {
            answer = temp
        }
    }
    return answer!!
}

@apatrida
Copy link
Author

Ah, the M13 std-lib version is mostly the same logic, just prettier:

/**
 * Concurrent getOrPut, that is safe for concurrent maps.
 *
 * Returns the value for the given [key]. If the key is not found in the map, calls the [defaultValue] function,
 * puts its result into the map under the given key and returns it.
 *
 * This method guarantees not to put the value into the map if the key is already there,
 * but the [defaultValue] function may be invoked even if the key is already in the map.
 */
public inline fun <K, V: Any> ConcurrentMap<K, V>.concurrentGetOrPut(key: K, defaultValue: () -> V): V {
    // Do not use computeIfAbsent on JVM8 as it would change locking behavior
    return this.get(key) ?:
            defaultValue().let { default -> this.putIfAbsent(key, default) ?: default }

}

@vinc3m1
Copy link
Owner

vinc3m1 commented Aug 31, 2015

Ah nice, makes sense. Thanks!

@vinc3m1 vinc3m1 closed this as completed in a4c0707 Sep 1, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants