[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 kotlinNativeCoroutinesInternalPackageName = FqName.fromSegments(listOf("kotlin", "coroutines", "native", "internal"))
|
||||||
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
|
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
|
||||||
val typedIntrinsicAnnotation = FqName("kotlin.native.internal.TypedIntrinsic")
|
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 =
|
val executeImpl =
|
||||||
irBuiltIns.findFunctions(Name.identifier("executeImpl"),"kotlin", "native", "concurrent").single()
|
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 {
|
val areEqualByValue = internalFunctions("areEqualByValue").associateBy {
|
||||||
it.descriptor.valueParameters[0].type.computePrimitiveBinaryTypeOrNull()!!
|
it.descriptor.valueParameters[0].type.computePrimitiveBinaryTypeOrNull()!!
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import kotlin.test.*
|
|||||||
import kotlin.native.internal.*
|
import kotlin.native.internal.*
|
||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlin.native.ref.WeakReference
|
import kotlin.native.ref.WeakReference
|
||||||
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
|
|
||||||
class AtomicBoolean(initialValue: Boolean) {
|
class AtomicBoolean(initialValue: Boolean) {
|
||||||
private val impl = AtomicInt(if (initialValue) 1 else 0)
|
private val impl = AtomicInt(if (initialValue) 1 else 0)
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
@file:OptIn(ExperimentalStdlibApi::class)
|
@file:OptIn(ExperimentalStdlibApi::class)
|
||||||
|
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.Platform
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
@file:OptIn(ExperimentalStdlibApi::class)
|
@file:OptIn(ExperimentalStdlibApi::class)
|
||||||
|
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.createCleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.Platform
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.Platform
|
||||||
|
|
||||||
@ThreadLocal
|
@ThreadLocal
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.Platform
|
||||||
|
|
||||||
@ThreadLocal
|
@ThreadLocal
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import kotlin.test.*
|
|||||||
|
|
||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.internal.*
|
||||||
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
|
|
||||||
@ThreadLocal
|
@ThreadLocal
|
||||||
var tlsCleaner: Cleaner? = null
|
var tlsCleaner: Cleaner? = null
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.Platform
|
||||||
|
|
||||||
// This cleaner won't be run, because it's deinitialized with globals after
|
// This cleaner won't be run, because it's deinitialized with globals after
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
import kotlin.native.internal.*
|
import kotlin.native.ref.Cleaner
|
||||||
import kotlin.native.Platform
|
import kotlin.native.ref.createCleaner
|
||||||
|
|
||||||
// This cleaner won't be run, because it's deinitialized with globals after
|
// This cleaner won't be run, because it's deinitialized with globals after
|
||||||
// cleaners are disabled.
|
// cleaners are disabled.
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import kotlin.test.*
|
|||||||
import kotlin.native.internal.*
|
import kotlin.native.internal.*
|
||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlin.native.ref.WeakReference
|
import kotlin.native.ref.WeakReference
|
||||||
|
import kotlin.native.ref.Cleaner
|
||||||
|
import kotlin.native.ref.createCleaner
|
||||||
|
|
||||||
class AtomicBoolean(initialValue: Boolean) {
|
class AtomicBoolean(initialValue: Boolean) {
|
||||||
private val impl = AtomicInt(if (initialValue) 1 else 0)
|
private val impl = AtomicInt(if (initialValue) 1 else 0)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ package kotlin.native.internal
|
|||||||
import kotlin.native.concurrent.*
|
import kotlin.native.concurrent.*
|
||||||
import kotlinx.cinterop.NativePtr
|
import kotlinx.cinterop.NativePtr
|
||||||
|
|
||||||
|
@Deprecated("Use kotlin.native.ref.Cleaner instead.", ReplaceWith("kotlin.native.ref.Cleaner"))
|
||||||
|
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||||
public interface Cleaner
|
public interface Cleaner
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,26 +69,14 @@ public interface Cleaner
|
|||||||
*/
|
*/
|
||||||
// TODO: Consider just annotating the lambda argument rather than hardcoding checking
|
// TODO: Consider just annotating the lambda argument rather than hardcoding checking
|
||||||
// by function name in the compiler.
|
// 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
|
@ExperimentalStdlibApi
|
||||||
@ExportForCompiler
|
@ExportForCompiler
|
||||||
@OptIn(FreezingIsDeprecated::class)
|
@OptIn(FreezingIsDeprecated::class)
|
||||||
fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner {
|
fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner =
|
||||||
if (!argument.isShareable())
|
kotlin.native.ref.createCleanerImpl(argument, block) as Cleaner
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform GC on a worker that executes Cleaner blocks.
|
* Perform GC on a worker that executes Cleaner blocks.
|
||||||
@@ -107,7 +97,7 @@ fun waitCleanerWorker() =
|
|||||||
}.result
|
}.result
|
||||||
|
|
||||||
@GCUnsafeCall("Kotlin_CleanerImpl_getCleanerWorker")
|
@GCUnsafeCall("Kotlin_CleanerImpl_getCleanerWorker")
|
||||||
external private fun getCleanerWorker(): Worker
|
external internal fun getCleanerWorker(): Worker
|
||||||
|
|
||||||
@ExportForCppRuntime("Kotlin_CleanerImpl_shutdownCleanerWorker")
|
@ExportForCppRuntime("Kotlin_CleanerImpl_shutdownCleanerWorker")
|
||||||
private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Boolean) {
|
private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Boolean) {
|
||||||
@@ -118,13 +108,3 @@ private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Bool
|
|||||||
private fun createCleanerWorker(): Worker {
|
private fun createCleanerWorker(): Worker {
|
||||||
return Worker.start(errorReporting = false, name = "Cleaner 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