[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:
committed by
Space Team
parent
d266f49c7c
commit
d738646a61
+1
-1
@@ -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")
|
||||
}
|
||||
|
||||
+2
-1
@@ -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() {
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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
|
||||
Reference in New Issue
Block a user