What's the simplest way to create a single value t...
# coroutines
c
What's the simplest way to create a single value that is lazily computed in the scope of the first caller? I'd like to declare how the value should be created before a CoroutineScope is available. I'm essentially searching for
CoroutineScope.async(CoroutineStart.LAZY) {}
, but without the requirement for the CoroutineScope (instead, the first user to
await
uses their own CoroutineScope to compute the value, and then the same is returned for all other users).
s
In my experience, trying to do this isn't really a good idea. For example, what happens if the first arriving coroutine gets cancelled while it's computing the value? If you know that this is safe, that implies that you have a shared coroutine scope which all the coroutines inherit from, and you can launch the async task in that scope. If you don't have such a common parent scope, having one coroutine perform work and then expose the result to another coroutine is going to poke holes in structured concurrency. Consequently there isn't a built-in for this that I'm aware of. You could probably write one with a mutex though, so long as you're careful about error handling and cancellation.
c
What I came up with:
Copy code
fun <T: Any> lazySingleton(block: CoroutineScope.() -> T): CoroutineScope.() -> T {
    val lock = Mutex()
    var data: T? = null

    return {
        lock.withLock {
            if (data == null) {
                block().also { data = it }
            } else {
                data!!
            }
        }
    }
}
@Sam This will only be used in tests, not in production, so if anything goes wrong I'm fine with it crashing. I need this because
@BeforeTest
cannot
suspend
.
If you find a way the function I just wrote can live to invalid results other than by throwing an exception, please tell me 🙂
e
I just define a
Copy code
private fun runTest(testBody: suspend TestScope.() -> Unit) = kotlinx.coroutines.test.runTest {
    setUp()
    try {
        testBody()
    } finally {
        tearDown()
    }
}
in the test classes that need a suspending setup/teardown