https://kotlinlang.org logo
Title
b

bartvh

07/25/2019, 10:43 AM
Given this situation:
fun doStuffWith(res: SomeResource) {
  // res will and MUST be released soon after this function returns

  run {
    // this is ok, run invokes the lambda immediately
    res.doStuff()
  }

  foo {
    // this will throw because I overlooked that foo invokes its lambda argument some time later, when res has been released
    res.doStuff()
  }
}
Do you think it would be possible in any way to make this usage a compile time error? Or to give a runtime exception earlier than in
doStuff
. Generally speaking, I'd want to prevent some object from being (accidentally) captured by a lambda that is not invoked immediately. I don't think it's possible, especially not while still allowing the usage in
run
, but I'm interested in your ideas. Both
SomeResource
and
foo
are under my control.
m

Marko Mitic

07/25/2019, 10:51 AM
Do you even get compiler error if you do this?
res.release()
res.doStuff()
b

bartvh

07/25/2019, 10:53 AM
No but that's ok, that's a mistake that's not as easily made, particularly if res is given as parameter and released elsewhere
m

Marko Mitic

07/25/2019, 10:56 AM
ok, another example
val res: SomeResource = ...
val job = async {
    res.doStuff()
}
...
job.await()
res.release()
Compiler would have to be quite clever to figure out that this shouldn't be error but same thing without
job.await()
would
b

bartvh

07/25/2019, 10:57 AM
well I don't want to wait for the job to run, that might be minutes later. the job should reacquire the resource when it needs it
we already have contracts to specify when a lambda is invoked. from there on it should theoretically not be very hard to detect that e.g. a
Closeable
object that is `.use {}`d might be accessed after it has been closed. but i'm not in the compiler writing business 😛
m

Marko Mitic

07/25/2019, 11:02 AM
there isn't an API for that right now, you can only specify that lambda is called only during function execution. Another API saying it will be invoked after that could be useful
b

bartvh

07/25/2019, 11:02 AM
it would indeed
m

Marko Mitic

07/25/2019, 11:03 AM
but general problem is knowing in compile time what code will cause issues (as demonstrated by my example)
Here's something that might help in those scenarios:
class ReferenceCloser(closable: Closeable, referenceCount: Int = 1) {
    private val count = AtomicInteger(referenceCount)
    private var reference : Closeable? = closable

    fun close() {
        if (count.decrementAndGet() == 0) {
            reference?.close()
            reference = null
        }
    }
}
you can close this both in lamba and in outer function and it will release the resource on latter call
b

bartvh

07/25/2019, 11:10 AM
well again, that delays the closing of the resource
that may not be allowed, and certainly isn't for my current use case (a thread lock)
m

Marko Mitic

07/25/2019, 11:12 AM
fun doStuffWith(res: SomeResource) {
    val closer = ReferenceCloser(res, 2)

    foo {
        res.doStuff()
        closer.close()
    }
    
    doMoreStuffWith(res)
    closer.close()
}
b

bartvh

07/25/2019, 11:12 AM
but thanks for the idea