progquester
progquester

Reputation: 1766

How to ensure that a function is called to release resources when an object is GCed in Kotlin?

Suppose I have an object that must explicitly call the close() function to ensure that all resources are released.

If this object is passed (e.g. through a channel or CompletableDeferred) and there is no guarantee that it will be received correctly by the receiver (e.g. risk of concurrency if the user does a Cancel), then obviously this object can only be GC'd because no one references it, but who is guaranteed that this close() will be called?

class TestLink(val address: String) {
        val fd = openNativeResource(address)
        private val _eventChannel = Channel<String>()
        val incoming: ReceiveChannel<String> get() = _eventChannel
        companion object {
            suspend fun create(scope: CoroutineScope, address: String): TestLink {
                val link = TestLink(address)
                link.start(scope)
                return link
            }
        }
        fun start(scope: CoroutineScope) {
            scope.launch { 
                while (true) {
                    val data = readNativeResource(fd)
                    if (data != null)
                        _eventChannel.send(data)
                    else
                        break
                }
                _eventChannel.close()
            }
        }
        fun close() {
            closeNativeResource(fd)
        }
    }

    @Test
    fun createLinkWithTimeout() = runBlocking {
        val linkDeferred = CompletableDeferred<TestLink>()
        val job = launch {
            try {
                val link = TestLink.create(this, "test")
                linkDeferred.complete(link)
                // run here, and timeout at the same time.
            } catch (e: Exception) {
                linkDeferred.completeExceptionally(e)
            }
        }
        val link = withTimeoutOrNull(100) {
            linkDeferred.await()
        }
        if (link != null) {
            var counter = 0
            for (data in link.incoming) {
                counter++
                if (counter >= 100) break
                println(data)
            }
            link.close()
        } else {
            job.cancelAndJoin()
        }
    }

Upvotes: 0

Views: 28

Answers (0)

Related Questions