Skip to content

Commit

Permalink
Replace virtual threads with raw Continuations
Browse files Browse the repository at this point in the history
Because continuations are hidden in an internal JDK package, we need to wrap them in our own classes. Requires force-opening the JDK modules to our own (unnamed) module.
  • Loading branch information
SamCarlberg committed May 11, 2024
1 parent d24144a commit d570544
Show file tree
Hide file tree
Showing 20 changed files with 794 additions and 918 deletions.
6 changes: 6 additions & 0 deletions wpilibNewCommands/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,10 @@ test {
outputs.upToDateWhen {false}
showStandardStreams = true
}
doFirst {
jvmArgs = [
'--add-opens', 'java.base/jdk.internal.vm=ALL-UNNAMED',
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import edu.wpi.first.units.Measure;
import edu.wpi.first.units.Time;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -107,6 +109,17 @@ default boolean requires(HardwareResource resource) {
return requirements().contains(resource);
}

/**
* Checks if this command conflicts with another command.
*
* @param other the commands to check against
* @return true if both commands require at least one of the same resource, false if both commands
* have completely different requirements
*/
default boolean conflictsWith(AsyncCommand other) {
return !Collections.disjoint(requirements(), other.requirements());
}

/**
* Creates an async command that does not require any hardware; that is, it does not affect the
* state of any physical objects. This is useful for commands that do some house cleaning work
Expand All @@ -126,7 +139,7 @@ default void schedule() {

/** Cancels this command, if running on the default async scheduler. */
default void cancel() {
AsyncScheduler.getInstance().cancelAndWait(this, true);
AsyncScheduler.getInstance().cancel(this);
}

/** Checks if this command is currently scheduled to be running on the default async scheduler. */
Expand All @@ -135,65 +148,20 @@ default boolean isScheduled() {
}

default AsyncCommand withTimeout(Measure<Time> timeout) {
final AsyncCommand original = this;

return new AsyncCommand() {
@Override
public void run() throws Exception {
AsyncScheduler.getInstance().schedule(AsyncCommand.noHardware(() -> {
AsyncCommand.pause(timeout);
AsyncScheduler.getInstance().cancelAndWait(this, true);
}).named("Timer for " + original.name()));

original.run();
}

@Override
public String name() {
return original.name();
}

@Override
public Set<HardwareResource> requirements() {
return original.requirements();
}

@Override
public int priority() {
return original.priority();
}

@Override
public RobotDisabledBehavior robotDisabledBehavior() {
return original.robotDisabledBehavior();
}
};
return ParallelGroup.race(name(), this, new WaitCommand(timeout));
}

static AsyncCommandBuilder requiring(HardwareResource requirement, HardwareResource... rest) {
return new AsyncCommandBuilder().requiring(requirement).requiring(rest);
}

/**
* Pauses the execution thread for the given duration. This is intended to be used by commands to
* pause themselves and allow other commands to run.
*
* @param duration how long the command should pause for. Shorter durations provide higher
* resolution for control loops, but incur higher CPU use and may cause contention
* if too many commands with low pause times are running concurrently.
* @throws InterruptedException if the command was interrupted by another command while paused
*/
static void pause(Measure<Time> duration) throws InterruptedException {
AsyncScheduler.getInstance().pauseCurrentCommand(duration);
static void yield() {
AsyncScheduler.getInstance().yield();
}

/**
* Pauses the execution thread for the {@link AsyncScheduler#DEFAULT_UPDATE_PERIOD default period}
* to allow commands to pause themselves and allow other commands to run.
*
* @throws InterruptedException if the command was interrupted by another command while paused.
*/
static void pause() throws InterruptedException {
AsyncCommand.pause(AsyncScheduler.DEFAULT_UPDATE_PERIOD);
static void park() {
while (true) {
AsyncCommand.yield();
}
}
}

This file was deleted.

Loading

0 comments on commit d570544

Please sign in to comment.