Mark all Worker-related API as obsolete.
* Explain what obsolete means and why workers are obsolete * Add some documentation to workers to explain its common pitfalls ^KT-54702 Merge-request: KT-MR-9563 Merged-by: Vsevolod Tolstopyatov <qwwdfsad@gmail.com>
This commit is contained in:
committed by
Space Team
parent
ec4fab57a7
commit
af9252aa96
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import kotlin.native.internal.Frozen
|
||||
/**
|
||||
* State of the future object.
|
||||
*/
|
||||
@ObsoleteWorkersApi
|
||||
enum class FutureState(val value: Int) {
|
||||
INVALID(0),
|
||||
/** Future is scheduled for execution. */
|
||||
@@ -26,6 +27,7 @@ enum class FutureState(val value: Int) {
|
||||
* Class representing abstract computation, whose result may become available in the future.
|
||||
*/
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
@ObsoleteWorkersApi
|
||||
public value class Future<T> @PublishedApi internal constructor(val id: Int) {
|
||||
/**
|
||||
* Blocks execution until the future is ready.
|
||||
@@ -70,6 +72,7 @@ public value class Future<T> @PublishedApi internal constructor(val id: Int) {
|
||||
|
||||
|
||||
@Deprecated("Use 'waitForMultipleFutures' top-level function instead", ReplaceWith("waitForMultipleFutures(this, millis)"), DeprecationLevel.ERROR)
|
||||
@ObsoleteWorkersApi
|
||||
public fun <T> Collection<Future<T>>.waitForMultipleFutures(millis: Int): Set<Future<T>> = waitForMultipleFutures(this, millis)
|
||||
|
||||
|
||||
@@ -79,6 +82,7 @@ public fun <T> Collection<Future<T>>.waitForMultipleFutures(millis: Int): Set<Fu
|
||||
*
|
||||
* @param timeoutMillis the amount of time in milliseconds to wait for the computed future
|
||||
*/
|
||||
@ObsoleteWorkersApi
|
||||
public fun <T> waitForMultipleFutures(futures: Collection<Future<T>>, timeoutMillis: Int): Set<Future<T>> {
|
||||
val result = mutableSetOf<Future<T>>()
|
||||
|
||||
@@ -101,4 +105,4 @@ public fun <T> waitForMultipleFutures(futures: Collection<Future<T>>, timeoutMil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,77 +21,98 @@ external internal fun Any?.isShareable(): Boolean
|
||||
// Implementation details.
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_stateOfFuture")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun stateOfFuture(id: Int): Int
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_consumeFuture")
|
||||
@PublishedApi
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun consumeFuture(id: Int): Any?
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_waitForAnyFuture")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun waitForAnyFuture(versionToken: Int, millis: Int): Boolean
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_versionToken")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun versionToken(): Int
|
||||
|
||||
@kotlin.native.internal.ExportForCompiler
|
||||
@ObsoleteWorkersApi
|
||||
internal fun executeImpl(worker: Worker, mode: TransferMode, producer: () -> Any?,
|
||||
job: CPointer<CFunction<*>>): Future<Any?> =
|
||||
Future<Any?>(executeInternal(worker.id, mode.value, producer, job))
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_startInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun startInternal(errorReporting: Boolean, name: String?): Int
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_currentInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun currentInternal(): Int
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_requestTerminationWorkerInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun requestTerminationInternal(id: Int, processScheduledJobs: Boolean): Int
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_executeInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun executeInternal(
|
||||
id: Int, mode: Int, producer: () -> Any?, job: CPointer<CFunction<*>>): Int
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_executeAfterInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun executeAfterInternal(id: Int, operation: () -> Unit, afterMicroseconds: Long): Unit
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_processQueueInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun processQueueInternal(id: Int): Boolean
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_parkInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun parkInternal(id: Int, timeoutMicroseconds: Long, process: Boolean): Boolean
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_getNameInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun getWorkerNameInternal(id: Int): String?
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun ThrowWorkerAlreadyTerminated(): Unit =
|
||||
throw IllegalStateException("Worker is already terminated")
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun ThrowWrongWorkerOrAlreadyTerminated(): Unit =
|
||||
throw IllegalStateException("Worker is not current or already terminated")
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun ThrowCannotTransferOwnership(): Unit =
|
||||
throw IllegalStateException("Unable to transfer object: it is still owned elsewhere")
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun ThrowFutureInvalidState(): Unit =
|
||||
throw IllegalStateException("Future is in an invalid state")
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun ThrowWorkerUnsupported(): Unit =
|
||||
throw UnsupportedOperationException("Workers are not supported")
|
||||
|
||||
@ExportForCppRuntime
|
||||
@ObsoleteWorkersApi
|
||||
internal fun WorkerLaunchpad(function: () -> Any?) = function()
|
||||
|
||||
@PublishedApi
|
||||
@GCUnsafeCall("Kotlin_Worker_detachObjectGraphInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun detachObjectGraphInternal(mode: Int, producer: () -> Any?): NativePtr
|
||||
|
||||
@PublishedApi
|
||||
@GCUnsafeCall("Kotlin_Worker_attachObjectGraphInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun attachObjectGraphInternal(stable: NativePtr): Any?
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_freezeInternal")
|
||||
@@ -128,10 +149,13 @@ external internal fun checkIfFrozen(ref: Any?)
|
||||
|
||||
@InternalForKotlinNative
|
||||
@GCUnsafeCall("Kotlin_Worker_waitTermination")
|
||||
@ObsoleteWorkersApi
|
||||
external public fun waitWorkerTermination(worker: Worker)
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_getPlatformThreadIdInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun getPlatfromThreadIdInternal(id: Int): ULong
|
||||
|
||||
@GCUnsafeCall("Kotlin_Worker_getActiveWorkersInternal")
|
||||
@ObsoleteWorkersApi
|
||||
external internal fun getActiveWorkersInternal(): IntArray
|
||||
|
||||
@@ -36,6 +36,7 @@ import kotlin.native.internal.Frozen
|
||||
* @see [kotlin.native.runtime.GC.collect].
|
||||
*/
|
||||
// Not @FreezingIsDeprecated: every `Worker.execute` uses this.
|
||||
@ObsoleteWorkersApi
|
||||
public enum class TransferMode(val value: Int) {
|
||||
/**
|
||||
* Reachability check is performed.
|
||||
@@ -54,6 +55,7 @@ public enum class TransferMode(val value: Int) {
|
||||
*/
|
||||
@Frozen
|
||||
@FreezingIsDeprecated
|
||||
@ObsoleteWorkersApi
|
||||
public class DetachedObjectGraph<T> internal constructor(pointer: NativePtr) {
|
||||
@PublishedApi
|
||||
internal val stable = AtomicNativePtr(pointer)
|
||||
@@ -84,6 +86,7 @@ public class DetachedObjectGraph<T> internal constructor(pointer: NativePtr) {
|
||||
* happen once.
|
||||
*/
|
||||
@FreezingIsDeprecated
|
||||
@ObsoleteWorkersApi
|
||||
public inline fun <reified T> DetachedObjectGraph<T>.attach(): T {
|
||||
var rawStable: NativePtr
|
||||
do {
|
||||
|
||||
@@ -12,22 +12,28 @@ import kotlin.native.internal.IntrinsicType
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
/**
|
||||
* ## Workers: theory of operations.
|
||||
*
|
||||
* [Worker] represents asynchronous and concurrent computation, usually performed by other threads
|
||||
* in the same process. Object passing between workers is performed using transfer operation, so that
|
||||
* object graph belongs to one worker at the time, but can be disconnected and reconnected as needed.
|
||||
* See 'Object Transfer Basics' and [TransferMode] for more details on how objects shall be transferred.
|
||||
* This approach ensures that no concurrent access happens to same object, while data may flow between
|
||||
* workers as needed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class representing worker.
|
||||
* Class representing a worker.
|
||||
*
|
||||
* [Worker] represents a thread of execution _and_ a queue of work sent to this worker.
|
||||
* Once started, the worker processes its own queue until it is explicitly [terminated][requestTermination].
|
||||
*
|
||||
* Worker's API resembles both executor and threads API:
|
||||
*
|
||||
* - It is a resource that has to be [closed][requestTermination].
|
||||
* - Failure to terminatae **and** join the termination request leads to native memory leak.
|
||||
* - It cannot be terminated externally while processing the work queue, leading to a crash.
|
||||
* - It can be [parked][park], but while parked, worker is allowed to process its own tasks' queue.
|
||||
* - In order to unpark a worker, any task has to be submited to the worker.
|
||||
* - The work can be sent to the worker both in forms of regular [runnables][execute] and [delayed (timed)][executeAfter] runnables.
|
||||
* - There is no mechanism to cancel delayed runnables.
|
||||
* - Attempts to invoke any method of worker being terminated lead to a runtime exception.
|
||||
* - It includes such things as innocuous [Worker.current] from the **current** worker.
|
||||
*/
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@ObsoleteWorkersApi
|
||||
public value class Worker @PublishedApi internal constructor(val id: Int) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -75,6 +81,9 @@ public value class Worker @PublishedApi internal constructor(val id: Int) {
|
||||
/**
|
||||
* Requests termination of the worker.
|
||||
*
|
||||
* Returns [Future] that **must** be joined with [Future.result] blocking call.
|
||||
* Failure to do so will leak native memory and underlying native thread handles.
|
||||
*
|
||||
* @param processScheduledJobs controls is we shall wait until all scheduled jobs processed,
|
||||
* or terminate immediately. If there are jobs to be execucted with [executeAfter] their execution
|
||||
* is awaited for.
|
||||
@@ -213,6 +222,7 @@ public value class Worker @PublishedApi internal constructor(val id: Int) {
|
||||
* @param block to be executed.
|
||||
* @return value returned by the block.
|
||||
*/
|
||||
@ObsoleteWorkersApi
|
||||
public inline fun <R> withWorker(name: String? = null, errorReporting: Boolean = true, block: Worker.() -> R): R {
|
||||
val worker = Worker.start(errorReporting, name)
|
||||
try {
|
||||
|
||||
@@ -8,12 +8,15 @@ package kotlin.native.concurrent
|
||||
import kotlin.native.internal.*
|
||||
|
||||
@GCUnsafeCall("Kotlin_WorkerBoundReference_create")
|
||||
@ObsoleteWorkersApi
|
||||
external private fun createWorkerBoundReference(value: Any): NativePtr
|
||||
|
||||
@GCUnsafeCall("Kotlin_WorkerBoundReference_deref")
|
||||
@ObsoleteWorkersApi
|
||||
external private fun derefWorkerBoundReference(ref: NativePtr): Any?
|
||||
|
||||
@GCUnsafeCall("Kotlin_WorkerBoundReference_describe")
|
||||
@ObsoleteWorkersApi
|
||||
external private fun describeWorkerBoundReference(ref: NativePtr): String
|
||||
|
||||
/**
|
||||
@@ -31,6 +34,7 @@ external private fun describeWorkerBoundReference(ref: NativePtr): String
|
||||
@HasFinalizer
|
||||
@HasFreezeHook
|
||||
@FreezingIsDeprecated
|
||||
@ObsoleteWorkersApi
|
||||
public class WorkerBoundReference<out T : Any>(value: T) {
|
||||
|
||||
private var ptr = NativePtr.NULL
|
||||
|
||||
@@ -76,7 +76,7 @@ public interface Cleaner
|
||||
@Suppress("DEPRECATION")
|
||||
@ExperimentalStdlibApi
|
||||
@ExportForCompiler
|
||||
@OptIn(ExperimentalNativeApi::class)
|
||||
@OptIn(ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
|
||||
fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner =
|
||||
kotlin.native.ref.createCleanerImpl(argument, block) as Cleaner
|
||||
|
||||
@@ -84,7 +84,7 @@ fun <T> createCleaner(argument: T, block: (T) -> Unit): Cleaner =
|
||||
* Perform GC on a worker that executes Cleaner blocks.
|
||||
*/
|
||||
@InternalForKotlinNative
|
||||
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
|
||||
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class, ObsoleteWorkersApi::class)
|
||||
fun performGCOnCleanerWorker() =
|
||||
getCleanerWorker().execute(TransferMode.SAFE, {}) {
|
||||
GC.collect()
|
||||
@@ -94,20 +94,24 @@ fun performGCOnCleanerWorker() =
|
||||
* Wait for a worker that executes Cleaner blocks to complete its scheduled tasks.
|
||||
*/
|
||||
@InternalForKotlinNative
|
||||
@OptIn(ObsoleteWorkersApi::class)
|
||||
fun waitCleanerWorker() =
|
||||
getCleanerWorker().execute(TransferMode.SAFE, {}) {
|
||||
Unit
|
||||
}.result
|
||||
|
||||
@GCUnsafeCall("Kotlin_CleanerImpl_getCleanerWorker")
|
||||
@OptIn(ObsoleteWorkersApi::class)
|
||||
external internal fun getCleanerWorker(): Worker
|
||||
|
||||
@ExportForCppRuntime("Kotlin_CleanerImpl_shutdownCleanerWorker")
|
||||
@OptIn(ObsoleteWorkersApi::class)
|
||||
private fun shutdownCleanerWorker(worker: Worker, executeScheduledCleaners: Boolean) {
|
||||
worker.requestTermination(executeScheduledCleaners).result
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_CleanerImpl_createCleanerWorker")
|
||||
@OptIn(ObsoleteWorkersApi::class)
|
||||
private fun createCleanerWorker(): Worker {
|
||||
return Worker.start(errorReporting = false, name = "Cleaner worker")
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@ExperimentalNativeApi
|
||||
@ObsoleteWorkersApi
|
||||
fun worker(args: Array<String>) {
|
||||
val worker = Worker.start()
|
||||
val exitCode = worker.execute(TransferMode.SAFE, { args.freeze() }) {
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
package kotlin.native.ref
|
||||
|
||||
import kotlin.experimental.ExperimentalNativeApi
|
||||
import kotlin.native.concurrent.isShareable
|
||||
import kotlin.native.concurrent.freeze
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.internal.*
|
||||
import kotlinx.cinterop.NativePtr
|
||||
|
||||
@@ -91,7 +90,7 @@ public fun <T> createCleaner(resource: T, cleanupAction: (resource: T) -> Unit):
|
||||
createCleanerImpl(resource, cleanupAction)
|
||||
|
||||
@ExperimentalNativeApi
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
|
||||
internal fun <T> createCleanerImpl(resource: T, cleanupAction: (T) -> Unit): Cleaner {
|
||||
if (!resource.isShareable())
|
||||
throw IllegalArgumentException("$resource must be shareable")
|
||||
@@ -121,4 +120,5 @@ private class CleanerImpl(
|
||||
|
||||
|
||||
@GCUnsafeCall("CreateStablePointer")
|
||||
external private fun createStablePointer(obj: Any): NativePtr
|
||||
external private fun createStablePointer(obj: Any): NativePtr
|
||||
|
||||
|
||||
Reference in New Issue
Block a user