[K/N] Move kotlin.native.internal.Cleaner to kotlin.native.ref.Cleaner

As a part of efforts to stabilize Native stdlib #KT-55765.


Co-authored-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
Co-authored-by: Ilya Gorbunov <Ilya.Gorbunov@jetbrains.com>

Merge-request: KT-MR-9347
Merged-by: Abduqodiri Qurbonzoda <abduqodiri.qurbonzoda@jetbrains.com>
This commit is contained in:
Abduqodiri Qurbonzoda
2023-03-31 16:04:26 +00:00
committed by Space Team
parent d266f49c7c
commit d738646a61
13 changed files with 151 additions and 37 deletions
@@ -25,5 +25,5 @@ object RuntimeNames {
val kotlinNativeCoroutinesInternalPackageName = FqName.fromSegments(listOf("kotlin", "coroutines", "native", "internal"))
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
val typedIntrinsicAnnotation = FqName("kotlin.native.internal.TypedIntrinsic")
val cleaner = FqName("kotlin.native.internal.Cleaner")
val cleaner = FqName("kotlin.native.ref.Cleaner")
}
@@ -231,7 +231,8 @@ internal abstract class KonanSymbols(
val executeImpl =
irBuiltIns.findFunctions(Name.identifier("executeImpl"),"kotlin", "native", "concurrent").single()
val createCleaner = internalFunction("createCleaner")
val createCleaner =
irBuiltIns.findFunctions(Name.identifier("createCleaner"),"kotlin", "native", "ref").single()
val areEqualByValue = internalFunctions("areEqualByValue").associateBy {
it.descriptor.valueParameters[0].type.computePrimitiveBinaryTypeOrNull()!!
@@ -11,6 +11,8 @@ import kotlin.test.*
import kotlin.native.internal.*
import kotlin.native.concurrent.*
import kotlin.native.ref.WeakReference
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
class AtomicBoolean(initialValue: Boolean) {
private val impl = AtomicInt(if (initialValue) 1 else 0)
@@ -4,7 +4,8 @@
*/
@file:OptIn(ExperimentalStdlibApi::class)
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
fun main() {
@@ -4,7 +4,7 @@
*/
@file:OptIn(ExperimentalStdlibApi::class)
import kotlin.native.internal.*
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
fun main() {
@@ -7,7 +7,8 @@
import kotlin.test.*
import kotlin.native.concurrent.*
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
@ThreadLocal
@@ -7,7 +7,8 @@
import kotlin.test.*
import kotlin.native.concurrent.*
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
@ThreadLocal
@@ -8,6 +8,8 @@ import kotlin.test.*
import kotlin.native.concurrent.*
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
@ThreadLocal
var tlsCleaner: Cleaner? = null
@@ -6,7 +6,8 @@
import kotlin.test.*
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
// This cleaner won't be run, because it's deinitialized with globals after
@@ -6,8 +6,8 @@
import kotlin.test.*
import kotlin.native.internal.*
import kotlin.native.Platform
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
// This cleaner won't be run, because it's deinitialized with globals after
// cleaners are disabled.
@@ -11,6 +11,8 @@ import kotlin.test.*
import kotlin.native.internal.*
import kotlin.native.concurrent.*
import kotlin.native.ref.WeakReference
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
class AtomicBoolean(initialValue: Boolean) {
private val impl = AtomicInt(if (initialValue) 1 else 0)
@@ -8,6 +8,8 @@ package kotlin.native.internal
import kotlin.native.concurrent.*
import kotlinx.cinterop.NativePtr
@Deprecated("Use kotlin.native.ref.Cleaner instead.", ReplaceWith("kotlin.native.ref.Cleaner"))
@DeprecatedSinceKotlin(warningSince = "1.9")
public interface Cleaner
/**
@@ -67,26 +69,14 @@ public interface Cleaner
*/
// TODO: Consider just annotating the lambda argument rather than hardcoding checking
// by function name in the compiler.
@Deprecated("Use kotlin.native.ref.createCleaner instead.", ReplaceWith("kotlin.native.ref.createCleaner(argument, block)"))
@DeprecatedSinceKotlin(warningSince = "1.9")
@Suppress("DEPRECATION")
@ExperimentalStdlibApi
@ExportForCompiler
@OptIn(FreezingIsDeprecated::class)
fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner {
if (!argument.isShareable())
throw IllegalArgumentException("$argument must be shareable")
val clean = {
// TODO: Maybe if this fails with exception, it should be (optionally) reported.
block(argument)
}.freeze()
// Make sure there's an extra reference to clean, so it's definitely alive when CleanerImpl is destroyed.
val cleanPtr = createStablePointer(clean)
// Make sure cleaner worker is initialized.
getCleanerWorker()
return CleanerImpl(cleanPtr).freeze()
}
fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner =
kotlin.native.ref.createCleanerImpl(argument, block) as Cleaner
/**
* Perform GC on a worker that executes Cleaner blocks.
@@ -107,7 +97,7 @@ fun waitCleanerWorker() =
}.result
@GCUnsafeCall("Kotlin_CleanerImpl_getCleanerWorker")
external private fun getCleanerWorker(): Worker
external internal fun getCleanerWorker(): Worker
@ExportForCppRuntime("Kotlin_CleanerImpl_shutdownCleanerWorker")
private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Boolean) {
@@ -118,13 +108,3 @@ private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Bool
private fun createCleanerWorker(): Worker {
return Worker.start(errorReporting = false, name = "Cleaner worker")
}
@NoReorderFields
@ExportTypeInfo("theCleanerImplTypeInfo")
@HasFinalizer
private class CleanerImpl(
private val cleanPtr: NativePtr,
): Cleaner {}
@GCUnsafeCall("CreateStablePointer")
external private fun createStablePointer(obj: Any): NativePtr
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.native.ref
import kotlin.native.concurrent.isShareable
import kotlin.native.concurrent.freeze
import kotlin.native.internal.*
import kotlinx.cinterop.NativePtr
/**
* The marker interface for objects that have a cleanup action associated with them.
*
* Use [createCleaner] to create an instance of this type.
*/
@ExperimentalStdlibApi
@SinceKotlin("1.9")
public sealed interface Cleaner
/**
* Creates a [Cleaner] object that runs [cleanupAction] with given [resource] some time after its deallocation.
*
* Example of usage:
* ```
* class ResourceWrapper {
* private val resource = Resource()
*
* private val cleaner = createCleaner(resource) { it.dispose() }
* }
* ```
*
* When `ResourceWrapper` becomes unused and gets deallocated, its `cleaner`
* is also deallocated, and the resource is disposed later.
*
* It is not specified which thread runs [cleanupAction], as well as whether two or more
* cleanup actions from different cleaners can be run in parallel.
*
* Note: if [resource] refers (directly or indirectly) the cleaner, then both
* might leak, and the [cleanupAction] will not be called in this case.
* For example, the code below has a leak:
* ```
* class LeakingResourceWrapper {
* private val resource = Resource()
* private val cleaner = createCleaner(this) { it.resource.dispose() }
* }
* ```
* In this case cleaner's argument (`LeakingResourceWrapper`) can't be deallocated
* until [cleanupAction] (`it.resource.dispose()`) is executed, which can happen only strictly after
* the cleaner is deallocated, which can't happen until `LeakingResourceWrapper`
* is deallocated. So the requirements on object deallocations are contradictory
* in this case, which can't be handled gracefully. The cleanup action
* is not executed then, and cleaner and its argument might leak
* (depending on the implementation).
* The same problem occures when [cleanupAction] captures a value that refers (directly or indirectly) the cleaner:
* ```
* class LeakingResourceWrapper {
* private val cleaner = createCleaner(...) {
* doSomething()
* ...
* }
*
* private fun doSomething() {
* ...
* }
* }
* ```
* In the example above the cleanup lambda implicitly captures `this` object to call `doSomething()`.
*
* [cleanupAction] should not use `@ThreadLocal` globals, because it may
* be executed on a different thread.
*
* If [cleanupAction] throws an exception, the behavior is unspecified.
*
* Cleaners cannot be used to perform actions during the program shutdown:
* * cleaners that are referenced from globals will not be garbage collected at all,
* * cleaners that become unreferenced just before exiting main() might not be garbage collected,
because the GC might not get a chance to run.
*
* @param resource an object for which to perform [cleanupAction]
* @param cleanupAction a cleanup to perform on [resource]. Must not capture anything.
*/
// TODO: Consider just annotating the lambda argument rather than hardcoding checking
// by function name in the compiler.
@ExperimentalStdlibApi
@SinceKotlin("1.9")
@ExportForCompiler
public fun <T> createCleaner(resource: T, cleanupAction: (resource: T) -> Unit): Cleaner =
createCleanerImpl(resource, cleanupAction)
@ExperimentalStdlibApi
@OptIn(FreezingIsDeprecated::class)
internal fun <T> createCleanerImpl(resource: T, cleanupAction: (T) -> Unit): Cleaner {
if (!resource.isShareable())
throw IllegalArgumentException("$resource must be shareable")
val clean = {
// TODO: Maybe if this fails with exception, it should be (optionally) reported.
cleanupAction(resource)
}.freeze()
// Make sure there's an extra reference to clean, so it's definitely alive when CleanerImpl is destroyed.
val cleanPtr = createStablePointer(clean)
// Make sure cleaner worker is initialized.
getCleanerWorker()
return CleanerImpl(cleanPtr).freeze()
}
@Suppress("DEPRECATION")
@ExperimentalStdlibApi
@NoReorderFields
@ExportTypeInfo("theCleanerImplTypeInfo")
@HasFinalizer
private class CleanerImpl(
private val cleanPtr: NativePtr,
): Cleaner, kotlin.native.internal.Cleaner {}
@GCUnsafeCall("CreateStablePointer")
external private fun createStablePointer(obj: Any): NativePtr