API for object passing between platform threads. (#1270)
This commit is contained in:
@@ -594,6 +594,12 @@ task worker7(type: RunKonanTest) {
|
|||||||
source = "runtime/workers/worker7.kt"
|
source = "runtime/workers/worker7.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task worker8(type: RunKonanTest) {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
|
||||||
|
goldValue = "SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\nGot kotlin.Unit\nOK\n"
|
||||||
|
source = "runtime/workers/worker8.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task superFunCall(type: RunKonanTest) {
|
task superFunCall(type: RunKonanTest) {
|
||||||
goldValue = "<fun:C><fun:C1>\n<fun:C><fun:C3>\n"
|
goldValue = "<fun:C><fun:C1>\n<fun:C><fun:C3>\n"
|
||||||
source = "codegen/basics/superFunCall.kt"
|
source = "codegen/basics/superFunCall.kt"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package runtime.workers.worker8
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
import konan.worker.*
|
||||||
|
|
||||||
|
data class SharedDataMember(val double: Double)
|
||||||
|
|
||||||
|
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
val worker = startWorker()
|
||||||
|
// Here we do rather strange thing. To test object detach API we detach object graph,
|
||||||
|
// pass C pointer as a value to worker, where we manually reattached passed value.
|
||||||
|
val future = worker.schedule(TransferMode.CHECKED, {
|
||||||
|
detachObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }
|
||||||
|
} ) {
|
||||||
|
inputC ->
|
||||||
|
val input = attachObjectGraph<SharedData>(inputC)
|
||||||
|
println(input)
|
||||||
|
}
|
||||||
|
future.consume {
|
||||||
|
result -> println("Got $result")
|
||||||
|
}
|
||||||
|
worker.requestTermination().consume { _ -> }
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -17,7 +17,9 @@
|
|||||||
#ifndef RUNTIME_ASSERT_H
|
#ifndef RUNTIME_ASSERT_H
|
||||||
#define RUNTIME_ASSERT_H
|
#define RUNTIME_ASSERT_H
|
||||||
|
|
||||||
void RuntimeAssertFailed(const char* location, const char* message);
|
#include "Common.h"
|
||||||
|
|
||||||
|
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message);
|
||||||
|
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
#define TOSTRING(x) STRINGIFY(x)
|
#define TOSTRING(x) STRINGIFY(x)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define RUNTIME_COMMON_H
|
#define RUNTIME_COMMON_H
|
||||||
|
|
||||||
#define RUNTIME_NOTHROW __attribute__((nothrow))
|
#define RUNTIME_NOTHROW __attribute__((nothrow))
|
||||||
|
#define RUNTIME_NORETURN __attribute__((noreturn))
|
||||||
#define RUNTIME_CONST __attribute__((const))
|
#define RUNTIME_CONST __attribute__((const))
|
||||||
#define RUNTIME_PURE __attribute__((pure))
|
#define RUNTIME_PURE __attribute__((pure))
|
||||||
#define RUNTIME_USED __attribute__((used))
|
#define RUNTIME_USED __attribute__((used))
|
||||||
|
|||||||
@@ -34,22 +34,22 @@ void SetKonanTerminateHandler();
|
|||||||
// The functions below are implemented in Kotlin (at package konan.internal).
|
// The functions below are implemented in Kotlin (at package konan.internal).
|
||||||
|
|
||||||
// Throws null pointer exception. Context is evaluated from caller's address.
|
// Throws null pointer exception. Context is evaluated from caller's address.
|
||||||
void ThrowNullPointerException();
|
void RUNTIME_NORETURN ThrowNullPointerException();
|
||||||
// Throws array index out of bounds exception.
|
// Throws array index out of bounds exception.
|
||||||
// Context is evaluated from caller's address.
|
// Context is evaluated from caller's address.
|
||||||
void ThrowArrayIndexOutOfBoundsException();
|
void RUNTIME_NORETURN ThrowArrayIndexOutOfBoundsException();
|
||||||
// Throws class cast exception.
|
// Throws class cast exception.
|
||||||
void ThrowClassCastException();
|
void RUNTIME_NORETURN ThrowClassCastException();
|
||||||
// Throws arithmetic exception.
|
// Throws arithmetic exception.
|
||||||
void ThrowArithmeticException();
|
void RUNTIME_NORETURN ThrowArithmeticException();
|
||||||
// Throws number format exception.
|
// Throws number format exception.
|
||||||
void ThrowNumberFormatException();
|
void RUNTIME_NORETURN ThrowNumberFormatException();
|
||||||
// Throws out of memory error.
|
// Throws out of memory error.
|
||||||
void ThrowOutOfMemoryError();
|
void RUNTIME_NORETURN ThrowOutOfMemoryError();
|
||||||
// Throws not implemented error.
|
// Throws not implemented error.
|
||||||
void ThrowNotImplementedError();
|
void RUNTIME_NORETURN ThrowNotImplementedError();
|
||||||
// Throws illegal character conversion exception (used in UTF8/UTF16 conversions).
|
// Throws illegal character conversion exception (used in UTF8/UTF16 conversions).
|
||||||
void ThrowIllegalCharacterConversionException();
|
void RUNTIME_NORETURN ThrowIllegalCharacterConversionException();
|
||||||
// Prints out mesage of Throwable.
|
// Prints out mesage of Throwable.
|
||||||
void PrintThrowable(KRef);
|
void PrintThrowable(KRef);
|
||||||
|
|
||||||
|
|||||||
@@ -248,9 +248,13 @@ KLong Kotlin_math_absl(KLong x) { return llabs(x); }
|
|||||||
|
|
||||||
// kotlin.math isn't supported for WASM.
|
// kotlin.math isn't supported for WASM.
|
||||||
namespace {
|
namespace {
|
||||||
void NotImplemented() { ThrowNotImplementedError(); }
|
|
||||||
|
RUNTIME_NORETURN void NotImplemented() {
|
||||||
|
ThrowNotImplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
KDouble Kotlin_math_sin(KDouble x) { NotImplemented(); }
|
KDouble Kotlin_math_sin(KDouble x) { NotImplemented(); }
|
||||||
KDouble Kotlin_math_cos(KDouble x) { NotImplemented(); }
|
KDouble Kotlin_math_cos(KDouble x) { NotImplemented(); }
|
||||||
KDouble Kotlin_math_tan(KDouble x) { NotImplemented(); }
|
KDouble Kotlin_math_tan(KDouble x) { NotImplemented(); }
|
||||||
|
|||||||
@@ -1312,7 +1312,6 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
|
|||||||
#if USE_GC
|
#if USE_GC
|
||||||
if (root != nullptr) {
|
if (root != nullptr) {
|
||||||
auto state = memoryState;
|
auto state = memoryState;
|
||||||
|
|
||||||
auto container = root->container();
|
auto container = root->container();
|
||||||
|
|
||||||
ContainerHeaderSet visited;
|
ContainerHeaderSet visited;
|
||||||
|
|||||||
@@ -460,6 +460,19 @@ KInt versionToken() {
|
|||||||
return theState()->versionToken();
|
return theState()->versionToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(attachObjectGraphInternal, KNativePtr stable) {
|
||||||
|
RETURN_RESULT_OF(AdoptStablePointer, stable);
|
||||||
|
}
|
||||||
|
|
||||||
|
KNativePtr detachObjectGraphInternal(KInt transferMode, KRef producer) {
|
||||||
|
KRef ref = nullptr;
|
||||||
|
WorkerLaunchpad(producer, &ref);
|
||||||
|
if (ref != nullptr) {
|
||||||
|
return transfer(ref, transferMode);
|
||||||
|
} else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
KInt startWorker() {
|
KInt startWorker() {
|
||||||
@@ -502,6 +515,16 @@ KInt versionToken() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(attachObjectGraphInternal, KNativePtr stable) {
|
||||||
|
ThrowWorkerUnsupported();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
KNativePtr detachObjectGraphInternal(KInt transferMode, KRef producer) {
|
||||||
|
ThrowWorkerUnsupported();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_WORKERS
|
#endif // WITH_WORKERS
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -540,5 +563,12 @@ KInt Kotlin_Worker_versionToken() {
|
|||||||
return versionToken();
|
return versionToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(Kotlin_Worker_attachObjectGraphInternal, KNativePtr stable) {
|
||||||
|
RETURN_RESULT_OF(attachObjectGraphInternal, stable);
|
||||||
|
}
|
||||||
|
|
||||||
|
KNativePtr Kotlin_Worker_detachObjectGraphInternal(KInt transferMode, KRef producer) {
|
||||||
|
return detachObjectGraphInternal(transferMode, producer);
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package konan.worker
|
||||||
|
|
||||||
|
import konan.SymbolName
|
||||||
|
import konan.internal.ExportForCppRuntime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier of the future. Futures can be used from other workers.
|
||||||
|
*/
|
||||||
|
typealias FutureId = Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of the future object.
|
||||||
|
*/
|
||||||
|
enum class FutureState(val value: Int) {
|
||||||
|
INVALID(0),
|
||||||
|
// Future is scheduled for execution.
|
||||||
|
SCHEDULED(1),
|
||||||
|
// Future result is computed.
|
||||||
|
COMPUTED(2),
|
||||||
|
// Future is cancelled.
|
||||||
|
CANCELLED(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing abstract computation, whose result may become available in the future.
|
||||||
|
*/
|
||||||
|
// TODO: make me value class!
|
||||||
|
class Future<T> internal constructor(val id: FutureId) {
|
||||||
|
/**
|
||||||
|
* Blocks execution until the future is ready.
|
||||||
|
*/
|
||||||
|
inline fun <R> consume(code: (T) -> R) =
|
||||||
|
when (state) {
|
||||||
|
FutureState.SCHEDULED, FutureState.COMPUTED -> {
|
||||||
|
val value = @Suppress("UNCHECKED_CAST", "NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") (consumeFuture(id) as T)
|
||||||
|
code(value)
|
||||||
|
}
|
||||||
|
FutureState.INVALID ->
|
||||||
|
throw IllegalStateException("Future is in an invalid state: $state")
|
||||||
|
FutureState.CANCELLED ->
|
||||||
|
throw IllegalStateException("Future is cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun result(): T = consume { it -> it }
|
||||||
|
|
||||||
|
val state: FutureState
|
||||||
|
get() = FutureState.values()[stateOfFuture(id)]
|
||||||
|
|
||||||
|
override fun equals(other: Any?) = (other is Future<*>) && (id == other.id)
|
||||||
|
|
||||||
|
override fun hashCode() = id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for availability of futures in the collection. Returns set with all futures which have
|
||||||
|
* value available for the consumption.
|
||||||
|
*/
|
||||||
|
fun <T> Collection<Future<T>>.waitForMultipleFutures(millis: Int): Set<Future<T>> {
|
||||||
|
val result = mutableSetOf<Future<T>>()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val versionToken = versionToken()
|
||||||
|
for (future in this) {
|
||||||
|
if (future.state == FutureState.COMPUTED) {
|
||||||
|
result += future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.isNotEmpty()) return result
|
||||||
|
|
||||||
|
if (waitForAnyFuture(versionToken, millis)) break
|
||||||
|
}
|
||||||
|
|
||||||
|
for (future in this) {
|
||||||
|
if (future.state == FutureState.COMPUTED) {
|
||||||
|
result += future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private APIs.
|
||||||
|
@SymbolName("Kotlin_Worker_stateOfFuture")
|
||||||
|
external internal fun stateOfFuture(id: FutureId): Int
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_Worker_consumeFuture")
|
||||||
|
@kotlin.internal.InlineExposed
|
||||||
|
external internal fun consumeFuture(id: FutureId): Any?
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_Worker_waitForAnyFuture")
|
||||||
|
external internal fun waitForAnyFuture(versionToken: Int, millis: Int): Boolean
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_Worker_versionToken")
|
||||||
|
external internal fun versionToken(): Int
|
||||||
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package konan.worker
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object Transfer Basics.
|
||||||
|
*
|
||||||
|
* Objects can be passed between threads in one of two possible modes.
|
||||||
|
*
|
||||||
|
* - CHECKED - object subgraph is checked to be not reachable by other globals or locals, and passed
|
||||||
|
* if so, otherwise an exception is thrown
|
||||||
|
* - UNCHECKED - object is blindly passed to another worker, if there are references
|
||||||
|
* left in the passing worker - it may lead to crash or program malfunction
|
||||||
|
*
|
||||||
|
* Checked mode checks if object is no longer used in passing worker, using memory-management
|
||||||
|
* specific algorithm (ARC implementation relies on trial deletion on object graph rooted in
|
||||||
|
* passed object), and throws IllegalStateException if object graph rooted in transferred object
|
||||||
|
* is reachable by some other means,
|
||||||
|
*
|
||||||
|
* Unchecked mode, intended for most performance crititcal operations, where object graph ownership
|
||||||
|
* is expected to be correct (such as application debugged earlier in CHECKED mode), just transfers
|
||||||
|
* ownership without further checks.
|
||||||
|
*
|
||||||
|
* Note, that for some cases cycle collection need to be done to ensure that dead cycles do not affect
|
||||||
|
* reachability of passed object graph. See `konan.internal.GC.collect()`.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum class TransferMode(val value: Int) {
|
||||||
|
CHECKED(0),
|
||||||
|
UNCHECKED(1) // USE UNCHECKED MODE ONLY IF ABSOLUTELY SURE WHAT YOU'RE DOING!!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates verbatim *shallow* copy of passed object, use carefully to create disjoint object graph.
|
||||||
|
*/
|
||||||
|
inline fun <reified T> T.shallowCopy(): T = shallowCopyInternal(this) as T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates verbatim *deep* copy of passed object's graph, use *VERY* carefully to create disjoint object graph.
|
||||||
|
* Note that this function could potentially duplicate a lot of objects.
|
||||||
|
*/
|
||||||
|
inline fun <reified T> T.deepCopy(): T = TODO()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates stable pointer to object, ensuring associated object subgraph is disjoint in specified mode
|
||||||
|
* ([TransferMode.CHECKED] by default).
|
||||||
|
* It could be stored to C variable or passed to another thread, where it could be retrieved with [attachObjectGraph].
|
||||||
|
*/
|
||||||
|
inline fun <reified T> detachObjectGraph(mode: TransferMode = TransferMode.CHECKED, noinline producer: () -> T): COpaquePointer? =
|
||||||
|
detachObjectGraphInternal(mode.value, producer as () -> Any?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches previously detached with [detachObjectGraph] object subgraph.
|
||||||
|
* Please note, that once object graph is attached, the stable pointer does not have sense anymore,
|
||||||
|
* and shall be discarded.
|
||||||
|
*/
|
||||||
|
inline fun <reified T> attachObjectGraph(stable: COpaquePointer?): T =
|
||||||
|
attachObjectGraphInternal(stable) as T
|
||||||
|
|
||||||
|
// Private APIs.
|
||||||
|
@PublishedApi
|
||||||
|
@SymbolName("Kotlin_Worker_shallowCopyInternal")
|
||||||
|
external internal fun shallowCopyInternal(value: Any?): Any?
|
||||||
|
@PublishedApi
|
||||||
|
@SymbolName("Kotlin_Worker_detachObjectGraphInternal")
|
||||||
|
external internal fun detachObjectGraphInternal(mode: Int, producer: () -> Any?): COpaquePointer?
|
||||||
|
@PublishedApi
|
||||||
|
@SymbolName("Kotlin_Worker_attachObjectGraphInternal")
|
||||||
|
external internal fun attachObjectGraphInternal(stable: COpaquePointer?): Any?
|
||||||
|
|
||||||
@@ -31,85 +31,11 @@ import kotlinx.cinterop.*
|
|||||||
* workers as needed.
|
* workers as needed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* State of the future object.
|
|
||||||
*/
|
|
||||||
enum class FutureState(val value: Int) {
|
|
||||||
INVALID(0),
|
|
||||||
// Future is scheduled for execution.
|
|
||||||
SCHEDULED(1),
|
|
||||||
// Future result is computed.
|
|
||||||
COMPUTED(2),
|
|
||||||
// Future is cancelled.
|
|
||||||
CANCELLED(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object Transfer Basics.
|
|
||||||
*
|
|
||||||
* Objects can be passed between threads in one of two possible modes.
|
|
||||||
*
|
|
||||||
* - CHECKED - object subgraph is checked to be not reachable by other globals or locals, and passed
|
|
||||||
* if so, otherwise an exception is thrown
|
|
||||||
* - UNCHECKED - object is blindly passed to another worker, if there are references
|
|
||||||
* left in the passing worker - it may lead to crash or program malfunction
|
|
||||||
*
|
|
||||||
* Checked mode checks if object is no longer used in passing worker, using memory-management
|
|
||||||
* specific algorithm (ARC implementation relies on trial deletion on object graph rooted in
|
|
||||||
* passed object), and throws IllegalStateException if object graph rooted in transferred object
|
|
||||||
* is reachable by some other means,
|
|
||||||
*
|
|
||||||
* Unchecked mode, intended for most performance crititcal operations, where object graph ownership
|
|
||||||
* is expected to be correct (such as application debugged earlier in CHECKED mode), just transfers
|
|
||||||
* ownership without further checks.
|
|
||||||
*
|
|
||||||
* Note, that for some cases cycle collection need to be done to ensure that dead cycles do not affect
|
|
||||||
* reachability of passed object graph. See `konan.internal.GC.collect()`.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum class TransferMode(val value: Int) {
|
|
||||||
CHECKED(0),
|
|
||||||
UNCHECKED(1) // USE UNCHECKED MODE ONLY IF ABSOLUTELY SURE WHAT YOU'RE DOING!!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unique identifier of the worker. Workers can be used from other workers.
|
* Unique identifier of the worker. Workers can be used from other workers.
|
||||||
*/
|
*/
|
||||||
typealias WorkerId = Int
|
typealias WorkerId = Int
|
||||||
/**
|
|
||||||
* Unique identifier of the future. Futures can be used from other workers.
|
|
||||||
*/
|
|
||||||
typealias FutureId = Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class representing abstract computation, whose result may become available in the future.
|
|
||||||
*/
|
|
||||||
// TODO: make me value class!
|
|
||||||
class Future<T> internal constructor(val id: FutureId) {
|
|
||||||
/**
|
|
||||||
* Blocks execution until the future is ready.
|
|
||||||
*/
|
|
||||||
inline fun <R> consume(code: (T) -> R) =
|
|
||||||
when (state) {
|
|
||||||
FutureState.SCHEDULED, FutureState.COMPUTED -> {
|
|
||||||
val value = @Suppress("UNCHECKED_CAST", "NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") (consumeFuture(id) as T)
|
|
||||||
code(value)
|
|
||||||
}
|
|
||||||
FutureState.INVALID ->
|
|
||||||
throw IllegalStateException("Future is in an invalid state: $state")
|
|
||||||
FutureState.CANCELLED ->
|
|
||||||
throw IllegalStateException("Future is cancelled")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun result(): T = consume { it -> it }
|
|
||||||
|
|
||||||
val state: FutureState
|
|
||||||
get() = FutureState.values()[stateOfFuture(id)]
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = (other is Future<*>) && (id == other.id)
|
|
||||||
|
|
||||||
override fun hashCode() = id
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing worker.
|
* Class representing worker.
|
||||||
@@ -154,46 +80,7 @@ class Worker(val id: WorkerId) {
|
|||||||
*/
|
*/
|
||||||
fun startWorker(): Worker = Worker(startInternal())
|
fun startWorker(): Worker = Worker(startInternal())
|
||||||
|
|
||||||
/**
|
// Private APIs.
|
||||||
* Wait for availability of futures in the collection. Returns set with all futures which have
|
|
||||||
* value available for the consumption.
|
|
||||||
*/
|
|
||||||
fun <T> Collection<Future<T>>.waitForMultipleFutures(millis: Int): Set<Future<T>> {
|
|
||||||
val result = mutableSetOf<Future<T>>()
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
val versionToken = versionToken()
|
|
||||||
for (future in this) {
|
|
||||||
if (future.state == FutureState.COMPUTED) {
|
|
||||||
result += future
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result.isNotEmpty()) return result
|
|
||||||
|
|
||||||
if (waitForAnyFuture(versionToken, millis)) break
|
|
||||||
}
|
|
||||||
|
|
||||||
for (future in this) {
|
|
||||||
if (future.state == FutureState.COMPUTED) {
|
|
||||||
result += future
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates verbatim *shallow* copy of passed object, use carefully to create disjoint object graph.
|
|
||||||
*/
|
|
||||||
fun <T> T.shallowCopy(): T = @Suppress("UNCHECKED_CAST") (shallowCopyInternal(this) as T)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates verbatim *deep* copy of passed object's graph, use *VERY* carefully to create disjoint object graph.
|
|
||||||
* Note that this function could potentially duplicate a lot of objects.
|
|
||||||
*/
|
|
||||||
fun <T> T.deepCopy(): T = TODO()
|
|
||||||
|
|
||||||
// Implementation details.
|
|
||||||
@konan.internal.ExportForCompiler
|
@konan.internal.ExportForCompiler
|
||||||
internal fun scheduleImpl(worker: Worker, mode: TransferMode, producer: () -> Any?,
|
internal fun scheduleImpl(worker: Worker, mode: TransferMode, producer: () -> Any?,
|
||||||
job: CPointer<CFunction<*>>): Future<Any?> =
|
job: CPointer<CFunction<*>>): Future<Any?> =
|
||||||
@@ -209,22 +96,6 @@ external internal fun requestTerminationInternal(id: WorkerId, processScheduledJ
|
|||||||
external internal fun scheduleInternal(
|
external internal fun scheduleInternal(
|
||||||
id: WorkerId, mode: Int, producer: () -> Any?, job: CPointer<CFunction<*>>): FutureId
|
id: WorkerId, mode: Int, producer: () -> Any?, job: CPointer<CFunction<*>>): FutureId
|
||||||
|
|
||||||
@SymbolName("Kotlin_Worker_shallowCopyInternal")
|
|
||||||
external internal fun shallowCopyInternal(value: Any?): Any?
|
|
||||||
|
|
||||||
@SymbolName("Kotlin_Worker_stateOfFuture")
|
|
||||||
external internal fun stateOfFuture(id: FutureId): Int
|
|
||||||
|
|
||||||
@SymbolName("Kotlin_Worker_consumeFuture")
|
|
||||||
@kotlin.internal.InlineExposed
|
|
||||||
external internal fun consumeFuture(id: FutureId): Any?
|
|
||||||
|
|
||||||
@SymbolName("Kotlin_Worker_waitForAnyFuture")
|
|
||||||
external internal fun waitForAnyFuture(versionToken: Int, millis: Int): Boolean
|
|
||||||
|
|
||||||
@SymbolName("Kotlin_Worker_versionToken")
|
|
||||||
external internal fun versionToken(): Int
|
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun ThrowWorkerUnsupported(): Unit =
|
internal fun ThrowWorkerUnsupported(): Unit =
|
||||||
throw UnsupportedOperationException("Workers are not supported")
|
throw UnsupportedOperationException("Workers are not supported")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ typedef struct {
|
|||||||
int x;
|
int x;
|
||||||
float f;
|
float f;
|
||||||
char* string;
|
char* string;
|
||||||
|
void* kotlinObject;
|
||||||
} SharedData;
|
} SharedData;
|
||||||
|
|
||||||
SharedData sharedData;
|
SharedData sharedData;
|
||||||
|
|||||||
@@ -25,8 +25,15 @@ inline fun Int.ensureUnixCallResult(op: String, predicate: (Int) -> Boolean = {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dumpShared(prefix: String) =
|
data class SharedDataMember(val double: Double)
|
||||||
println("$prefix: ${pthread_self().rawValue} x=${sharedData.x} f=${sharedData.f} s=${sharedData.string!!.toKString()}")
|
|
||||||
|
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
|
||||||
|
|
||||||
|
fun dumpShared(prefix: String): Unit {
|
||||||
|
println("""
|
||||||
|
$prefix: ${pthread_self().rawValue} x=${sharedData.x} f=${sharedData.f} s=${sharedData.string!!.toKString()}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
// Arena owning all native allocs.
|
// Arena owning all native allocs.
|
||||||
@@ -36,6 +43,9 @@ fun main(args: Array<String>) {
|
|||||||
sharedData.x = 239
|
sharedData.x = 239
|
||||||
sharedData.f = 0.5f
|
sharedData.f = 0.5f
|
||||||
sharedData.string = "Hello Kotlin!".cstr.getPointer(arena)
|
sharedData.string = "Hello Kotlin!".cstr.getPointer(arena)
|
||||||
|
sharedData.kotlinObject = konan.worker.detachObjectGraph {
|
||||||
|
SharedData("A string", 42, SharedDataMember(2.39))
|
||||||
|
}
|
||||||
dumpShared("thread1")
|
dumpShared("thread1")
|
||||||
|
|
||||||
// Start a new thread, that sees the variable.
|
// Start a new thread, that sees the variable.
|
||||||
@@ -43,12 +53,15 @@ fun main(args: Array<String>) {
|
|||||||
memScoped {
|
memScoped {
|
||||||
val thread = alloc<pthread_tVar>()
|
val thread = alloc<pthread_tVar>()
|
||||||
pthread_create(thread.ptr, null, staticCFunction {
|
pthread_create(thread.ptr, null, staticCFunction {
|
||||||
_ ->
|
argC ->
|
||||||
initRuntimeIfNeeded()
|
initRuntimeIfNeeded()
|
||||||
dumpShared("thread2")
|
dumpShared("thread2")
|
||||||
|
val kotlinObject = konan.worker.attachObjectGraph<SharedData>(sharedData.kotlinObject)
|
||||||
|
val arg = konan.worker.attachObjectGraph<SharedDataMember>(argC)
|
||||||
|
println("thread arg is $arg Kotlin object is $kotlinObject")
|
||||||
// Workaround for compiler issue.
|
// Workaround for compiler issue.
|
||||||
null as COpaquePointer?
|
null as COpaquePointer?
|
||||||
}, null).ensureUnixCallResult("pthread_create")
|
}, konan.worker.detachObjectGraph { SharedDataMember(3.14)} ).ensureUnixCallResult("pthread_create")
|
||||||
pthread_join(thread.value, null).ensureUnixCallResult("pthread_join")
|
pthread_join(thread.value, null).ensureUnixCallResult("pthread_join")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user