Given this situation: ``` fun doStuffWith(res: Som...
# announcements
b
Given this situation:
Copy code
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
Do you even get compiler error if you do this?
Copy code
res.release()
res.doStuff()
b
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
ok, another example
Copy code
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
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
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
it would indeed
m
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:
Copy code
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
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
Copy code
fun doStuffWith(res: SomeResource) {
    val closer = ReferenceCloser(res, 2)

    foo {
        res.doStuff()
        closer.close()
    }
    
    doMoreStuffWith(res)
    closer.close()
}
b
but thanks for the idea