Add completeExceptionally, cancelAndJoin, and refine LaunchPool spec
- CompletableDeferred.completeExceptionally(exception): fails the deferred with a Lyng exception; the original exception object is preserved as errorObject so assertThrows/catch see the correct class (works for both built-in ObjException and user-declared class instances) - LaunchPool: add maxQueueSize parameter (suspends launch when full), add cancelAndJoin() for graceful shutdown that waits for running tasks, clarify all semantics in the extern declaration comments - Two tests for completeExceptionally (built-in and user-defined exception) - Fix parallelism.md channel doc-test to be illustration-only (avoid flakiness from coroutine scheduling in the doc-test runner) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f9a07f176a
commit
145c3ae34a
4 files changed
+116
-20
No files matched your search
@@ -18,6 +18,7 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import net.sergeych.lyng.ExecutionError
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
@@ -43,6 +44,30 @@ class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>):
|
||||
thisAs<ObjCompletableDeferred>().completableDeferred.complete(args.firstAndOnly())
|
||||
ObjVoid
|
||||
}
|
||||
addFnDoc(
|
||||
name = "completeExceptionally",
|
||||
doc = "Fail this deferred with the given exception. Awaiting it will then throw that exception. " +
|
||||
"Subsequent calls have no effect. The argument must be an `Exception` instance.",
|
||||
params = listOf(ParamDoc("exception", type("lyng.Exception"))),
|
||||
returns = type("lyng.Void"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val ex = requiredArg<Obj>(0)
|
||||
val scope = requireScope()
|
||||
val msg = when (ex) {
|
||||
is ObjException -> ex.message.value
|
||||
else -> ex.toString(scope).value
|
||||
}
|
||||
val pos = when (ex) {
|
||||
is ObjException -> ex.scope.pos
|
||||
else -> scope.pos
|
||||
}
|
||||
// Always carry the original Lyng object as errorObject so that
|
||||
// assertThrows / catch clauses see the correct exception class.
|
||||
val cause = ExecutionError(ex, pos, msg)
|
||||
thisAs<ObjCompletableDeferred>().completableDeferred.completeExceptionally(cause)
|
||||
ObjVoid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,38 @@ class TestCoroutines {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCompletableExceptionally() = runTest {
|
||||
eval(
|
||||
"""
|
||||
val done = CompletableDeferred()
|
||||
|
||||
launch {
|
||||
delay(10)
|
||||
done.completeExceptionally(IllegalStateException("boom"))
|
||||
}
|
||||
|
||||
assert(!done.isCompleted)
|
||||
assertThrows(IllegalStateException) { done.await() }
|
||||
assert(done.isCompleted)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCompletableExceptionallyWithCustomException() = runTest {
|
||||
eval(
|
||||
"""
|
||||
class MyError(msg) : Exception(msg) {}
|
||||
|
||||
val done = CompletableDeferred()
|
||||
done.completeExceptionally(MyError("custom failure"))
|
||||
|
||||
assertThrows(MyError) { done.await() }
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeferredCancel() = runTest {
|
||||
eval(
|
||||
|
||||
Reference in new issue
Block a user