Closable for Kotlin/Native

While programming lcarswm, I ran into a scenario where I would have liked to have something like Java's Closable. But I needed it in Kotlin/Native and I needed it on an application global scale.

The scenario

Something that showed itself to me during development was this: there are quite a few resources that need to be requested from Xlib and the other libraries or setup in another way and kept until the end of the program, where they need to be freed again, often calling also a library funtion. In the long startup routine were also certain points, where the program needs to be aborted, if the resource is unaccessible. If the program is aborted, previously acquired resources must also be freed.

The initial solution

Initially, the scenario was solved by explicitly releasing everything during shutdown and having if branches in the startup that explicitly cleaned up resources that were acquired before those conditions, if they were not met. It's roughly shown in the following figure.
// startup code
A resourceA = getResourceA()
if (resourceA == NULL) {
  return;
}

[...]

B resourceB = getResourceB()
if (resourceB == NULL) {
  free(resourceA);
  return;
}

[...]

C resourceC = getResourceC()
if (resourceC == NULL) {
  free(resourceB);
  free(resourceA);
  return;
}

[...]

// main event loop

// shutdown
free(resourceC);
free(resourceB);
free(resourceA);
Pseudo code for manual resource cleanup in start and shutdown

The more resources are required, the uglier this approach looks due to a lot of code duplication. I wanted something more slim and semi-automatic.

As an additional requirement, the resources generally should be released in reversed order of acquiring them.

My solution

My basic idea/wish was this: whenever an acquired resource needs to be cleanup that should be taken care of at the moment of the acquisition. I was thinking about creating an interface, but sometimes resources are directly requested from the same libraries using different functions for different resources at different times. An interface would have not worked here unless creating an extra class for each resource. What looked more flexible and convenient to me was a solution with extension function.

First of all, the resources that need to be cleaned up need to be tracked or alternatively, the clean-up itself can be tracked. I decided for the later in form of clean-up functions and called it closables as shown in the next figure:

private val closables = mutableListOf<() -> Unit>()
closables - global list of clean-up functions

When shutting down at any point in the program all the closables get called in reversed order by calling the following global clean-up function. Whatever has been instantiated at the point of calling the function shown in the next figure, it will get cleaned up and it is not required to know what exactly that is.

fun closeClosables() {
    closables.asReversed()
            .forEach { it() }

    closables.clear()
}
Using the closables to cleanup resources

So much for the straightforward part. What is missing is the registration of the closables. This is done via a generic extension function. This way everything automatically gets the extension function. It also returns the object on which it was called which allows for chaining of calls on the object. All the registration function requires is a function from the extended class itself, that has no argument and no return value and implements the cleanup. This function will be kept in the list of closables. Here is the implementation:

fun <T> T.closeWith(closeFunction: T.() -> Unit): T {
    closables.add { this.closeFunction() }
    return this
}
Registering closables via an extension function

Finally, in lcarswm closables are used for instance like this:

fun startup(system: SystemApi): RuntimeResources? {
    [...]

    if (!system.openDisplay()) {
        logger.logError("::startup::got no display")
        return null // returning null skips the main event loop
    }
    system.closeWith { closeDisplay() }

    [...]
}

fun shutdown() {
    closeClosables()
}
Using closables

The full code can be found on Github in the lcarswm repository: closable.kt.