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:
sergeychandClaude Sonnet 4.6 committed 2026-04-18 03:00:40 +03:00
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
}
}
}
}