35public final class Coroutine {
37 private final Continuation m_backingContinuation;
39 private boolean m_cancelOnForkFailure =
true;
43 boolean inUnmountableState() {
55 Coroutine(
Scheduler scheduler, ContinuationScope scope, Consumer<Coroutine> callback) {
57 m_backingContinuation =
new Continuation(scope, () -> callback.accept(
this));
80 m_cancelOnForkFailure = cancelOnForkFailure;
92 return m_cancelOnForkFailure;
97 boolean isInterruptRequested() {
100 return m_state instanceof ForkFailed;
121 boolean isCancellationRequested() {
122 return m_state instanceof Canceled;
125 ForkResult getForkResult() {
126 if (m_state instanceof ForkFailed(ForkResult failure)) {
143 m_backingContinuation.yield();
172 public final class ForkResult {
178 Collection<ScheduleResult.Successful> successes, List<ScheduleResult.Failure> failures) {
179 m_forkedCommands = List.copyOf(successes);
180 m_failedCommands = List.copyOf(failures);
189 return m_failedCommands.isEmpty();
212 return !m_forkedCommands.isEmpty() && !m_failedCommands.isEmpty();
223 if (m_forkedCommands.isEmpty()) {
227 var tracker = CommandRunTracker.of(m_scheduler, m_forkedCommands);
228 while (tracker.isAnyRunning()) {
229 Coroutine.this.yield();
240 return m_failedCommands;
250 return m_forkedCommands;
261 private ForkResult checkAllForkable(Collection<? extends Command> commands) {
268 for (var command : commands) {
276 var result =
new ForkResult(successes, failures);
277 if (result.failed()) {
278 if (m_cancelOnForkFailure) {
281 m_state = forkFailed(result);
314 private ForkResult doFork(Collection<? extends Command> commands) {
315 var successes =
new ArrayList<ScheduleResult.Successful>();
316 var fails =
new ArrayList<ScheduleResult.Failure>();
317 for (var command : commands) {
318 var result = m_scheduler.schedule(command);
320 case ScheduleResult.Successful s -> successes.add(s);
321 case ScheduleResult.Failure f -> fails.add(f);
326 if (!fails.isEmpty() && m_cancelOnForkFailure) {
335 m_state = forkFailed(result);
383 requireNonNullParam(commands,
"commands",
"Coroutine.fork");
384 for (
int i = 0; i < commands.length; i++) {
385 requireNonNullParam(commands[i],
"commands[" + i +
"]",
"Coroutine.fork");
388 var forkableCheck = checkAllForkable(Arrays.asList(commands));
389 if (forkableCheck.failed()) {
390 return forkableCheck;
393 return doFork(Arrays.asList(commands));
453 requireNonNullParam(command,
"command",
"Coroutine.await");
456 var scheduleResult = m_scheduler.schedule(command);
457 switch (scheduleResult) {
459 int id = forked.runId();
463 var cond = parkedWhile(() -> m_scheduler.runId(forked.command()) ==
id);
464 if (!cond.conditionMet()) {
469 return new ForkResult(List.of(forked), List.of());
474 var result =
new ForkResult(List.of(), List.of(failed));
475 if (m_cancelOnForkFailure) {
476 m_state = forkFailed(result);
505 requireNonNullParam(commands,
"commands",
"Coroutine.awaitAll");
507 for (
Command command : commands) {
508 requireNonNullParam(command,
"commands[" + i +
"]",
"Coroutine.awaitAll");
512 var forkableCheck = checkAllForkable(commands);
513 if (forkableCheck.failed()) {
514 return forkableCheck;
517 var forkResult = doFork(commands);
518 if (forkResult.failed()) {
522 var tracker = CommandRunTracker.of(m_scheduler, forkResult.getForkedCommands());
523 var cond = parkedWhile(tracker::isAnyRunning);
524 if (!cond.conditionMet()) {
550 return awaitAll(Arrays.asList(commands));
573 requireNonNullParam(commands,
"commands",
"Coroutine.awaitAny");
575 for (
Command command : commands) {
576 requireNonNullParam(command,
"commands[" + i +
"]",
"Coroutine.awaitAny");
580 var forkableCheck = checkAllForkable(commands);
581 if (forkableCheck.failed()) {
582 return forkableCheck;
585 var forkResult = doFork(commands);
586 if (forkResult.failed()) {
590 var tracker = CommandRunTracker.of(m_scheduler, forkResult.getForkedCommands());
591 var cond = parkedWhile(tracker::areAllRunning);
592 if (!cond.conditionMet()) {
603 var command =
fork.command();
604 int runId =
fork.runId();
606 if (m_scheduler.runId(command) == runId) {
609 m_scheduler.cancel(command);
634 return awaitAny(Arrays.asList(commands));
670 public void wait(Time duration) {
673 requireNonNullParam(duration,
"duration",
"Coroutine.wait");
676 if (duration.in(Seconds) > 0) {
725 requireNonNullParam(condition,
"condition",
"Coroutine.waitUntil");
727 var cond = parkedUntil(condition);
728 if (!cond.conditionMet()) {
775 requireNonNullParam(condition,
"condition",
"Coroutine.waitUntil");
776 requireNonNullParam(timeout,
"timeout",
"Coroutine.waitUntil");
778 if (condition.getAsBoolean()) {
790 if (condition.getAsBoolean()) {
812 private boolean isMounted() {
813 return m_backingContinuation.isMounted();
816 private void requireMounted() {
826 throw new IllegalStateException(
"Coroutines can only be used by the command bound to them");
832 void runToYieldPoint() {
833 m_backingContinuation.run();
837 Continuation.mountContinuation(m_backingContinuation);
841 return m_backingContinuation.isDone();