Add Lyng deferred cancellation support
This commit is contained in:
parent
3e338f3d53
commit
311cf6ee44
@ -14,6 +14,8 @@ Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/s
|
|||||||
- Assertions/tests: `assert`, `assertEquals`/`assertEqual`, `assertNotEquals`, `assertThrows`.
|
- Assertions/tests: `assert`, `assertEquals`/`assertEqual`, `assertNotEquals`, `assertThrows`.
|
||||||
- Preconditions: `require`, `check`.
|
- Preconditions: `require`, `check`.
|
||||||
- Async/concurrency: `launch`, `yield`, `flow`, `delay`.
|
- Async/concurrency: `launch`, `yield`, `flow`, `delay`.
|
||||||
|
- `Deferred.cancel()` cancels an active task.
|
||||||
|
- `Deferred.await()` throws `CancellationException` if that task was cancelled.
|
||||||
- Math: `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `exp`, `ln`, `log10`, `log2`, `pow`, `sqrt`, `abs`, `clamp`.
|
- Math: `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `exp`, `ln`, `log10`, `log2`, `pow`, `sqrt`, `abs`, `clamp`.
|
||||||
- These helpers also accept `lyng.decimal.Decimal`.
|
- These helpers also accept `lyng.decimal.Decimal`.
|
||||||
- Exact Decimal path today: `abs`, `floor`, `ceil`, `round`, and `pow` with integral exponent.
|
- Exact Decimal path today: `abs`, `floor`, `ceil`, `round`, and `pow` with integral exponent.
|
||||||
@ -26,13 +28,14 @@ Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/s
|
|||||||
- Collections/types: `Iterable`, `Iterator`, `Collection`, `Array`, `List`, `ImmutableList`, `Set`, `ImmutableSet`, `Map`, `ImmutableMap`, `MapEntry`, `Range`, `RingBuffer`.
|
- Collections/types: `Iterable`, `Iterator`, `Collection`, `Array`, `List`, `ImmutableList`, `Set`, `ImmutableSet`, `Map`, `ImmutableMap`, `MapEntry`, `Range`, `RingBuffer`.
|
||||||
- Random: singleton `Random` and class `SeededRandom`.
|
- Random: singleton `Random` and class `SeededRandom`.
|
||||||
- Async types: `Deferred`, `CompletableDeferred`, `Mutex`, `Flow`, `FlowBuilder`.
|
- Async types: `Deferred`, `CompletableDeferred`, `Mutex`, `Flow`, `FlowBuilder`.
|
||||||
|
- Async exception: `CancellationException`.
|
||||||
- Delegation types: `Delegate`, `DelegateContext`.
|
- Delegation types: `Delegate`, `DelegateContext`.
|
||||||
- Regex types: `Regex`, `RegexMatch`.
|
- Regex types: `Regex`, `RegexMatch`.
|
||||||
- Also present: `Math.PI` namespace constant.
|
- Also present: `Math.PI` namespace constant.
|
||||||
|
|
||||||
## 4. `lyng.stdlib` Module Surface (from `root.lyng`)
|
## 4. `lyng.stdlib` Module Surface (from `root.lyng`)
|
||||||
### 4.1 Extern class declarations
|
### 4.1 Extern class declarations
|
||||||
- Exceptions/delegation base: `Exception`, `IllegalArgumentException`, `NotImplementedException`, `Delegate`.
|
- Exceptions/delegation base: `Exception`, `CancellationException`, `IllegalArgumentException`, `NotImplementedException`, `Delegate`.
|
||||||
- Collections and iterables: `Iterable<T>`, `Iterator<T>`, `Collection<T>`, `Array<T>`, `List<T>`, `ImmutableList<T>`, `Set<T>`, `ImmutableSet<T>`, `Map<K,V>`, `ImmutableMap<K,V>`, `MapEntry<K,V>`, `RingBuffer<T>`.
|
- Collections and iterables: `Iterable<T>`, `Iterator<T>`, `Collection<T>`, `Array<T>`, `List<T>`, `ImmutableList<T>`, `Set<T>`, `ImmutableSet<T>`, `Map<K,V>`, `ImmutableMap<K,V>`, `MapEntry<K,V>`, `RingBuffer<T>`.
|
||||||
- Host iterator bridge: `KotlinIterator<T>`.
|
- Host iterator bridge: `KotlinIterator<T>`.
|
||||||
- Random APIs: `extern object Random`, `extern class SeededRandom`.
|
- Random APIs: `extern object Random`, `extern class SeededRandom`.
|
||||||
|
|||||||
@ -32,10 +32,25 @@ Depending on the platform, these coroutines may be executed on different CPU and
|
|||||||
assert(xIsCalled)
|
assert(xIsCalled)
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
This example shows how to launch a coroutine with `launch` which returns [Deferred] instance, the latter have ways to await for the coroutine completion and retrieve possible result.
|
This example shows how to launch a coroutine with `launch` which returns [Deferred] instance, the latter have ways to await for the coroutine completion, cancel it if it is no longer needed, and retrieve possible result.
|
||||||
|
|
||||||
Launch has the only argument which should be a callable (lambda usually) that is run in parallel (or cooperatively in parallel), and return anything as the result.
|
Launch has the only argument which should be a callable (lambda usually) that is run in parallel (or cooperatively in parallel), and return anything as the result.
|
||||||
|
|
||||||
|
If you no longer need the result, cancel the deferred. Awaiting a cancelled deferred throws `CancellationException`:
|
||||||
|
|
||||||
|
var reached = false
|
||||||
|
val work = launch {
|
||||||
|
delay(100)
|
||||||
|
reached = true
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
work.cancel()
|
||||||
|
assertThrows(CancellationException) { work.await() }
|
||||||
|
assert(work.isCancelled)
|
||||||
|
assert(!work.isActive)
|
||||||
|
assert(!reached)
|
||||||
|
>>> void
|
||||||
|
|
||||||
## Synchronization: Mutex
|
## Synchronization: Mutex
|
||||||
|
|
||||||
Suppose we have a resource, that could be used concurrently, a counter in our case. If we won't protect it, concurrent usage cause RC, Race Condition, providing wrong result:
|
Suppose we have a resource, that could be used concurrently, a counter in our case. If we won't protect it, concurrent usage cause RC, Race Condition, providing wrong result:
|
||||||
|
|||||||
@ -343,6 +343,73 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
fun handleTopLevelKeyword(keyword: Token): Boolean {
|
||||||
|
when (keyword.value) {
|
||||||
|
"fun", "fn" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type != Token.Type.ID) return true
|
||||||
|
val afterName = cc.peekNextNonWhitespace()
|
||||||
|
if (afterName.type == Token.Type.DOT) {
|
||||||
|
cc.nextNonWhitespace()
|
||||||
|
val actual = cc.nextNonWhitespace()
|
||||||
|
if (actual.type == Token.Type.ID) {
|
||||||
|
extensionNames.add(actual.value)
|
||||||
|
registerExtensionName(nameToken.value, actual.value)
|
||||||
|
declareSlotNameIn(plan, extensionCallableName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
||||||
|
moduleDeclaredNames.add(extensionCallableName(nameToken.value, actual.value))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
||||||
|
moduleDeclaredNames.add(nameToken.value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
"val", "var" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type != Token.Type.ID) return true
|
||||||
|
val afterName = cc.peekNextNonWhitespace()
|
||||||
|
if (afterName.type == Token.Type.DOT) {
|
||||||
|
cc.nextNonWhitespace()
|
||||||
|
val actual = cc.nextNonWhitespace()
|
||||||
|
if (actual.type == Token.Type.ID) {
|
||||||
|
extensionNames.add(actual.value)
|
||||||
|
registerExtensionName(nameToken.value, actual.value)
|
||||||
|
declareSlotNameIn(plan, extensionPropertyGetterName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
||||||
|
moduleDeclaredNames.add(extensionPropertyGetterName(nameToken.value, actual.value))
|
||||||
|
if (keyword.value == "var") {
|
||||||
|
declareSlotNameIn(plan, extensionPropertySetterName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
||||||
|
moduleDeclaredNames.add(extensionPropertySetterName(nameToken.value, actual.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
declareSlotNameIn(plan, nameToken.value, isMutable = keyword.value == "var", isDelegated = false)
|
||||||
|
moduleDeclaredNames.add(nameToken.value)
|
||||||
|
predeclaredTopLevelValueNames.add(nameToken.value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
"class", "object" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
||||||
|
scopeSeedNames.add(nameToken.value)
|
||||||
|
moduleDeclaredNames.add(nameToken.value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
"enum" -> {
|
||||||
|
val next = nextNonWs()
|
||||||
|
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
||||||
|
scopeSeedNames.add(nameToken.value)
|
||||||
|
moduleDeclaredNames.add(nameToken.value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
while (cc.hasNext()) {
|
while (cc.hasNext()) {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
@ -355,66 +422,11 @@ class Compiler(
|
|||||||
Token.Type.RBRACKET -> if (bracketDepth > 0) bracketDepth--
|
Token.Type.RBRACKET -> if (bracketDepth > 0) bracketDepth--
|
||||||
Token.Type.ID -> if (depth == 0) {
|
Token.Type.ID -> if (depth == 0) {
|
||||||
if (parenDepth > 0 || bracketDepth > 0) continue
|
if (parenDepth > 0 || bracketDepth > 0) continue
|
||||||
when (t.value) {
|
if (t.value == "extern") {
|
||||||
"fun", "fn" -> {
|
handleTopLevelKeyword(nextNonWs())
|
||||||
val nameToken = nextNonWs()
|
continue
|
||||||
if (nameToken.type != Token.Type.ID) continue
|
|
||||||
val afterName = cc.peekNextNonWhitespace()
|
|
||||||
if (afterName.type == Token.Type.DOT) {
|
|
||||||
cc.nextNonWhitespace()
|
|
||||||
val actual = cc.nextNonWhitespace()
|
|
||||||
if (actual.type == Token.Type.ID) {
|
|
||||||
extensionNames.add(actual.value)
|
|
||||||
registerExtensionName(nameToken.value, actual.value)
|
|
||||||
declareSlotNameIn(plan, extensionCallableName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
|
||||||
moduleDeclaredNames.add(extensionCallableName(nameToken.value, actual.value))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
|
||||||
moduleDeclaredNames.add(nameToken.value)
|
|
||||||
}
|
|
||||||
"val", "var" -> {
|
|
||||||
val nameToken = nextNonWs()
|
|
||||||
if (nameToken.type != Token.Type.ID) continue
|
|
||||||
val afterName = cc.peekNextNonWhitespace()
|
|
||||||
if (afterName.type == Token.Type.DOT) {
|
|
||||||
cc.nextNonWhitespace()
|
|
||||||
val actual = cc.nextNonWhitespace()
|
|
||||||
if (actual.type == Token.Type.ID) {
|
|
||||||
extensionNames.add(actual.value)
|
|
||||||
registerExtensionName(nameToken.value, actual.value)
|
|
||||||
declareSlotNameIn(plan, extensionPropertyGetterName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
|
||||||
moduleDeclaredNames.add(extensionPropertyGetterName(nameToken.value, actual.value))
|
|
||||||
if (t.value == "var") {
|
|
||||||
declareSlotNameIn(plan, extensionPropertySetterName(nameToken.value, actual.value), isMutable = false, isDelegated = false)
|
|
||||||
moduleDeclaredNames.add(extensionPropertySetterName(nameToken.value, actual.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
declareSlotNameIn(plan, nameToken.value, isMutable = t.value == "var", isDelegated = false)
|
|
||||||
moduleDeclaredNames.add(nameToken.value)
|
|
||||||
predeclaredTopLevelValueNames.add(nameToken.value)
|
|
||||||
}
|
|
||||||
"class", "object" -> {
|
|
||||||
val nameToken = nextNonWs()
|
|
||||||
if (nameToken.type == Token.Type.ID) {
|
|
||||||
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
|
||||||
scopeSeedNames.add(nameToken.value)
|
|
||||||
moduleDeclaredNames.add(nameToken.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"enum" -> {
|
|
||||||
val next = nextNonWs()
|
|
||||||
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
|
||||||
if (nameToken.type == Token.Type.ID) {
|
|
||||||
declareSlotNameIn(plan, nameToken.value, isMutable = false, isDelegated = false)
|
|
||||||
scopeSeedNames.add(nameToken.value)
|
|
||||||
moduleDeclaredNames.add(nameToken.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
handleTopLevelKeyword(t)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -9070,7 +9082,10 @@ class Compiler(
|
|||||||
val effectiveEqToken = if (isProperty) null else eqToken
|
val effectiveEqToken = if (isProperty) null else eqToken
|
||||||
|
|
||||||
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
||||||
if (!isStatic && declaringClassNameCaptured == null && !actualExtern) declareLocalName(name, isMutable)
|
val isTopLevelExtern = actualExtern && declaringClassNameCaptured == null && slotPlanStack.size == 1
|
||||||
|
if (!isStatic && declaringClassNameCaptured == null && (!actualExtern || isTopLevelExtern)) {
|
||||||
|
declareLocalName(name, isMutable)
|
||||||
|
}
|
||||||
val declKind = if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
val declKind = if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
||||||
SymbolKind.MEMBER
|
SymbolKind.MEMBER
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -540,7 +540,8 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
|||||||
|
|
||||||
// Concurrency helpers
|
// Concurrency helpers
|
||||||
mod.classDoc(name = "Deferred", doc = "Represents a value that will be available in the future.", bases = listOf(type("Obj"))) {
|
mod.classDoc(name = "Deferred", doc = "Represents a value that will be available in the future.", bases = listOf(type("Obj"))) {
|
||||||
method(name = "await", doc = "Suspend until the value is available and return it.")
|
method(name = "cancel", doc = "Cancel the deferred if it is still active.")
|
||||||
|
method(name = "await", doc = "Suspend until the value is available and return it. Throws `CancellationException` if cancelled.")
|
||||||
}
|
}
|
||||||
mod.funDoc(
|
mod.funDoc(
|
||||||
name = "launch",
|
name = "launch",
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException as KotlinCancellationException
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
@ -33,12 +34,27 @@ open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
|||||||
scope.raiseError("Deferred constructor is not directly callable")
|
scope.raiseError("Deferred constructor is not directly callable")
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
|
addFnDoc(
|
||||||
|
name = "cancel",
|
||||||
|
doc = "Cancel this deferred if it is still active. Safe to call multiple times.",
|
||||||
|
returns = type("lyng.Void"),
|
||||||
|
moduleName = "lyng.stdlib"
|
||||||
|
) {
|
||||||
|
thisAs<ObjDeferred>().deferred.cancel()
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "await",
|
name = "await",
|
||||||
doc = "Suspend until completion and return the result value (or throw if failed).",
|
doc = "Suspend until completion and return the result value. Throws `CancellationException` if cancelled.",
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) { thisAs<ObjDeferred>().deferred.await() }
|
) {
|
||||||
|
try {
|
||||||
|
thisAs<ObjDeferred>().deferred.await()
|
||||||
|
} catch (e: KotlinCancellationException) {
|
||||||
|
requireScope().raiseError(ObjCancellationException(requireScope(), e.message ?: "Deferred was cancelled"))
|
||||||
|
}
|
||||||
|
}
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isCompleted",
|
name = "isCompleted",
|
||||||
doc = "Whether this deferred has completed (successfully or with an error).",
|
doc = "Whether this deferred has completed (successfully or with an error).",
|
||||||
@ -66,4 +82,3 @@ open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -278,6 +278,7 @@ open class ObjException(
|
|||||||
"AssertionFailedException",
|
"AssertionFailedException",
|
||||||
"ClassCastException",
|
"ClassCastException",
|
||||||
"IndexOutOfBoundsException",
|
"IndexOutOfBoundsException",
|
||||||
|
"CancellationException",
|
||||||
"IllegalArgumentException",
|
"IllegalArgumentException",
|
||||||
"IllegalStateException",
|
"IllegalStateException",
|
||||||
"NoSuchElementException",
|
"NoSuchElementException",
|
||||||
@ -311,6 +312,9 @@ class ObjClassCastException(scope: Scope, message: String) : ObjException("Class
|
|||||||
class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") :
|
class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") :
|
||||||
ObjException("IndexOutOfBoundsException", scope, message)
|
ObjException("IndexOutOfBoundsException", scope, message)
|
||||||
|
|
||||||
|
class ObjCancellationException(scope: Scope, message: String = "cancelled") :
|
||||||
|
ObjException("CancellationException", scope, message)
|
||||||
|
|
||||||
class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") :
|
class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") :
|
||||||
ObjException("IllegalArgumentException", scope, message)
|
ObjException("IllegalArgumentException", scope, message)
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,30 @@ class TestCoroutines {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeferredCancel() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
var reached = false
|
||||||
|
val d = launch {
|
||||||
|
delay(100)
|
||||||
|
reached = true
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
d.cancel()
|
||||||
|
d.cancel()
|
||||||
|
assertThrows(CancellationException) { d.await() }
|
||||||
|
|
||||||
|
delay(150)
|
||||||
|
|
||||||
|
assert(d.isCancelled)
|
||||||
|
assert(!d.isActive)
|
||||||
|
assert(!reached)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMutex() = runTest {
|
fun testMutex() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
@ -1,11 +1,48 @@
|
|||||||
package lyng.stdlib
|
package lyng.stdlib
|
||||||
|
|
||||||
|
/* Launch `code` asynchronously and return its result handle. */
|
||||||
|
extern fun launch(code): Deferred
|
||||||
|
/* Yield execution so other scheduled coroutines can run. */
|
||||||
|
extern fun yield(): void
|
||||||
|
/* Build a lazy asynchronous sequence. */
|
||||||
extern fun flow(builder: FlowBuilder.()->void): Flow
|
extern fun flow(builder: FlowBuilder.()->void): Flow
|
||||||
|
|
||||||
/* Built-in exception type. */
|
/* Built-in exception type. */
|
||||||
extern class Exception
|
extern class Exception
|
||||||
extern class IllegalArgumentException
|
extern class IllegalArgumentException
|
||||||
extern class NotImplementedException
|
extern class NotImplementedException
|
||||||
|
/* Raised when an awaited asynchronous task was cancelled before producing a value. */
|
||||||
|
extern class CancellationException : Exception
|
||||||
|
|
||||||
|
/* A handle to a running asynchronous task. */
|
||||||
|
extern class Deferred {
|
||||||
|
/* Cancel the task if it is still active. Safe to call multiple times. */
|
||||||
|
fun cancel(): void
|
||||||
|
/* Suspend until the task finishes and return its value.
|
||||||
|
Throws `CancellationException` if the task was cancelled. */
|
||||||
|
fun await(): Object
|
||||||
|
/* True when the task has finished, failed, or otherwise reached a terminal state. */
|
||||||
|
val isCompleted: Bool
|
||||||
|
/* True while the task is still running and has not been cancelled. */
|
||||||
|
val isActive: Bool
|
||||||
|
/* True when the task was cancelled. */
|
||||||
|
val isCancelled: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A deferred result that can be completed manually. */
|
||||||
|
extern class CompletableDeferred : Deferred {
|
||||||
|
fun complete(value: Object): void
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receiver passed into `flow { ... }` builders. */
|
||||||
|
extern class FlowBuilder {
|
||||||
|
fun emit(value: Object): void
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A cold asynchronous iterable sequence. */
|
||||||
|
extern class Flow<T> : Iterable<T> {
|
||||||
|
}
|
||||||
|
|
||||||
extern class Delegate
|
extern class Delegate
|
||||||
extern class Iterable<T> {
|
extern class Iterable<T> {
|
||||||
fun iterator(): Iterator<T>
|
fun iterator(): Iterator<T>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user