renammed from generators to coroutines

This commit is contained in:
Parker TenBroeck 2026-05-13 08:22:27 -04:00
parent ac83bc6e93
commit c8f3b6c01f
18 changed files with 63 additions and 67 deletions

View file

@ -1,6 +1,6 @@
# Generators/Futures for Java 24+ # Coroutines for Java 24+
Futures/Generators implemented as state machines integrated into standard java. Coroutines implemented as state machines integrated into standard java.
- Futures/Generators are lazy and only make progress when called upon - Futures/Generators are lazy and only make progress when called upon
- Integrated as much as possible into java to work with the type system as much as possible - Integrated as much as possible into java to work with the type system as much as possible
@ -150,16 +150,16 @@ This library requires the application be ran with a custom class loader as follo
```java ```java
public static void main(String[] args) { public static void main(String[] args) {
// loads the current class with a custom class loader and calls *this* method with the provided arguments. // loads the current class with a custom class loader and calls *this* method with the provided arguments.
// *this* method - the one used to call `runWithStateMachines` // *this* method - the one used to call `runWithCoroutine`
RT.runWithStateMachines(StateMachineClassLoader.Config.builtin(), (Object) args); RT.runWithCoroutine(CoroutineClassLoader.Config.builtin(), (Object) args);
// past this point generators will be created on methods which match the criteria // past this point Coroutines will be created on methods which match the criteria
} }
``` ```
Once finished any class loaded will be scanned for methods which match a particular signature. Once finished any class loaded will be scanned for methods which match a particular signature.
When matched a shim is introduced into the source method and a class is constructed which stores all state for the future/generator When matched a shim is introduced into the source method and a class is constructed which stores all state for the coroutine
A basic example here where we have some parameters A basic example here where we have some parameters
```java ```java

View file

@ -1,12 +1,12 @@
package basic; package basic;
import com.parkertenbroeck.bcsm.RT; import com.parkertenbroeck.bcsm.RT;
import com.parkertenbroeck.bcsm.loadtime.StateMachineClassLoader; import com.parkertenbroeck.bcsm.loadtime.CoroutineClassLoader;
import com.parkertenbroeck.generator.Gen; import com.parkertenbroeck.generator.Gen;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
RT.runWithStateMachines(StateMachineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args); RT.runWithCoroutine(CoroutineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args);
primes(); primes();
} }

View file

@ -2,11 +2,11 @@ package lexer;
import com.parkertenbroeck.generator.Gen; import com.parkertenbroeck.generator.Gen;
import com.parkertenbroeck.bcsm.RT; import com.parkertenbroeck.bcsm.RT;
import com.parkertenbroeck.bcsm.loadtime.StateMachineClassLoader; import com.parkertenbroeck.bcsm.loadtime.CoroutineClassLoader;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
RT.runWithStateMachines(StateMachineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args); RT.runWithCoroutine(CoroutineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args);
lexer(); lexer();
} }

View file

@ -2,11 +2,11 @@ package sockets;
import com.parkertenbroeck.async_runtime.Jokio; import com.parkertenbroeck.async_runtime.Jokio;
import com.parkertenbroeck.bcsm.RT; import com.parkertenbroeck.bcsm.RT;
import com.parkertenbroeck.bcsm.loadtime.StateMachineClassLoader; import com.parkertenbroeck.bcsm.loadtime.CoroutineClassLoader;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
RT.runWithStateMachines(StateMachineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args); RT.runWithCoroutine(CoroutineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args);
await(); await();
} }

View file

@ -0,0 +1,9 @@
package com.parkertenbroeck.async_runtime;
import java.util.concurrent.Executors;
public class AsyncExecutor {
void test(){
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
}
}

View file

@ -7,15 +7,10 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
public class Delay implements Future<Void, RuntimeException> { public class Delay implements Future<Void, RuntimeException> {
private final static Timer timer; private final static Timer timer = new Timer(true);
private TimerTask task; private TimerTask task;
static {
timer = new Timer(true);
}
private int delay; private int delay;
private boolean ready;
protected Delay(int ms) { protected Delay(int ms) {
if (ms < 0) throw new IllegalArgumentException("async_example.Delay cannot be negative"); if (ms < 0) throw new IllegalArgumentException("async_example.Delay cannot be negative");
@ -33,16 +28,12 @@ public class Delay implements Future<Void, RuntimeException> {
@Override @Override
public synchronized Object poll(Waker waker) { public synchronized Object poll(Waker waker) {
if (delay == 0) { if (delay == 0) return null;
ready = true; if (delay > 0) {
delay = -1;
return null;
}
if (delay != -1) {
task = new TimerTask() { task = new TimerTask() {
@Override @Override
public void run() { public void run() {
ready = true; delay = 0;
waker.wake(); waker.wake();
} }
}; };
@ -50,7 +41,6 @@ public class Delay implements Future<Void, RuntimeException> {
delay = -1; delay = -1;
} }
if (ready) return null;
return Pending.INSTANCE; return Pending.INSTANCE;
} }
} }

View file

@ -1,7 +1,7 @@
package com.parkertenbroeck.bcsm; package com.parkertenbroeck.bcsm;
import com.parkertenbroeck.bcsm.loadtime.StateMachineClassLoader; import com.parkertenbroeck.bcsm.loadtime.CoroutineClassLoader;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Set; import java.util.Set;
@ -9,10 +9,10 @@ import java.util.Set;
import static java.lang.StackWalker.Option.*; import static java.lang.StackWalker.Option.*;
public class RT { public class RT {
public static void runWithStateMachines(StateMachineClassLoader.Config config, Object... params){ public static void runWithCoroutine(CoroutineClassLoader.Config config, Object... params){
if(RT.class.getClassLoader() instanceof StateMachineClassLoader)return; if(RT.class.getClassLoader() instanceof CoroutineClassLoader)return;
var loader = new StateMachineClassLoader(RT.class.getClassLoader(), config); var loader = new CoroutineClassLoader(RT.class.getClassLoader(), config);
try{ try{
StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES, SHOW_HIDDEN_FRAMES)); StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES, SHOW_HIDDEN_FRAMES));
var frame = walker.walk(s -> s.skip(1).findFirst()).get(); var frame = walker.walk(s -> s.skip(1).findFirst()).get();

View file

@ -12,9 +12,7 @@ import java.lang.reflect.AccessFlag;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.lang.constant.ConstantDescs.*; public abstract class CoroutineBuilder<T extends CoroutineBuilder<T>> {
public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
public final static String PARAM_PREFIX = "param_"; public final static String PARAM_PREFIX = "param_";
public final static String LOCAL_PREFIX = "local_"; public final static String LOCAL_PREFIX = "local_";
public final static String STATE_NAME = "state"; public final static String STATE_NAME = "state";
@ -50,7 +48,7 @@ public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
} }
} }
public StateMachineBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com, String namePostfix){ public CoroutineBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com, String namePostfix){
this.src_clm = src_clm; this.src_clm = src_clm;
this.src_mem = src_mem; this.src_mem = src_mem;
this.src_com = src_com; this.src_com = src_com;
@ -93,7 +91,7 @@ public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
for (int i = 0; i < params.length; i++) { for (int i = 0; i < params.length; i++) {
var param = params[i]; var param = params[i];
for(var attr : param_attrs.get(i)) for(var attr : param_attrs.get(i))
parameterVariableAnnotations.add(new ParameterVariableAnnotation(attr, StateMachineBuilder.PARAM_PREFIX + offset, param)); parameterVariableAnnotations.add(new ParameterVariableAnnotation(attr, CoroutineBuilder.PARAM_PREFIX + offset, param));
offset += TypeKind.from(param).slotSize(); offset += TypeKind.from(param).slotSize();
} }

View file

@ -14,14 +14,14 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
public class StateMachineClassLoader extends ClassLoader { public class CoroutineClassLoader extends ClassLoader {
private final HashMap<String, Class<?>> customClazzMap = new HashMap<>(); private final HashMap<String, Class<?>> customClazzMap = new HashMap<>();
private final List<String> skip; private final List<String> skip;
private final HashMap<ClassDesc, SMBB> builders; private final HashMap<ClassDesc, SMBB> builders;
private final String write_classes_path; private final String write_classes_path;
public interface SMBB{ public interface SMBB{
StateMachineBuilder<?> build(ClassModel src_clm, MethodModel src_mem, CodeModel src_com); CoroutineBuilder<?> build(ClassModel src_clm, MethodModel src_mem, CodeModel src_com);
} }
public static class Config{ public static class Config{
HashSet<String> skip = new HashSet<>(); HashSet<String> skip = new HashSet<>();
@ -59,11 +59,11 @@ public class StateMachineClassLoader extends ClassLoader {
} }
} }
public StateMachineClassLoader(ClassLoader parent) { public CoroutineClassLoader(ClassLoader parent) {
this(parent, Config.builtin()); this(parent, Config.builtin());
} }
public StateMachineClassLoader(ClassLoader parent, Config config) { public CoroutineClassLoader(ClassLoader parent, Config config) {
super(parent); super(parent);
skip = config.skip.stream().toList(); skip = config.skip.stream().toList();
builders = new HashMap<>(config.builders); builders = new HashMap<>(config.builders);
@ -93,7 +93,7 @@ public class StateMachineClassLoader extends ClassLoader {
var p = "/" + name.replace('.', '/') + ".class"; var p = "/" + name.replace('.', '/') + ".class";
try (var stream = StateMachineClassLoader.class.getResourceAsStream(p)) { try (var stream = CoroutineClassLoader.class.getResourceAsStream(p)) {
var bytes = Objects.requireNonNull(stream).readAllBytes(); var bytes = Objects.requireNonNull(stream).readAllBytes();
add(name, searchReplaceableMethods(bytes)); add(name, searchReplaceableMethods(bytes));
return customClazzMap.get(name); return customClazzMap.get(name);
@ -114,7 +114,7 @@ public class StateMachineClassLoader extends ClassLoader {
return ClassFile.of(ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES, ClassFile.StackMapsOption.STACK_MAPS_WHEN_REQUIRED).build(clm.thisClass().asSymbol(), cb -> { return ClassFile.of(ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES, ClassFile.StackMapsOption.STACK_MAPS_WHEN_REQUIRED).build(clm.thisClass().asSymbol(), cb -> {
for (var ce : clm) { for (var ce : clm) {
if (ce instanceof MethodModel mem && mem.code().isPresent()) { if (ce instanceof MethodModel mem && mem.code().isPresent()) {
StateMachineBuilder<?> builder = builders CoroutineBuilder<?> builder = builders
.getOrDefault( .getOrDefault(
mem.methodTypeSymbol().returnType(), mem.methodTypeSymbol().returnType(),
(_, _, _) -> null (_, _, _) -> null

View file

@ -16,7 +16,7 @@ public record Frame(FrameTracker.Type[] locals, FrameTracker.Type[] stack, int b
+ "]"; + "]";
} }
public void save_locals(StateMachineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int loc_off){ public void save_locals(CoroutineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int loc_off){
int slot = 0; int slot = 0;
for (var entry : locals) { for (var entry : locals) {
slot++;// <----- slot++;// <-----
@ -28,20 +28,20 @@ public record Frame(FrameTracker.Type[] locals, FrameTracker.Type[] stack, int b
sst.save_local(smb, cob, entry.toCD(), slot - smb.paramSlotOff + loc_off - 1); //minus one cause increment before here sst.save_local(smb, cob, entry.toCD(), slot - smb.paramSlotOff + loc_off - 1); //minus one cause increment before here
} }
} }
public void save_stack(StateMachineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int stack_off) { public void save_stack(CoroutineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int stack_off) {
for(int i = stack.length-1-stack_off; i >= 0; i --){ for(int i = stack.length-1-stack_off; i >= 0; i --){
if(stack[i].isCategory2_2nd())continue; if(stack[i].isCategory2_2nd())continue;
sst.save_stack(smb, cob, stack[i].toCD()); sst.save_stack(smb, cob, stack[i].toCD());
} }
} }
public SavedStateTracker save(StateMachineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int loc_off, int stack_off) { public SavedStateTracker save(CoroutineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int loc_off, int stack_off) {
save_locals(smb, cob, sst, loc_off); save_locals(smb, cob, sst, loc_off);
save_stack(smb, cob, sst, stack_off); save_stack(smb, cob, sst, stack_off);
return sst; return sst;
} }
public SavedStateTracker save(StateMachineBuilder smb, CodeBuilder cob, int loc_off, int stack_off) { public SavedStateTracker save(CoroutineBuilder smb, CodeBuilder cob, int loc_off, int stack_off) {
var sst = new SavedStateTracker(); var sst = new SavedStateTracker();
return save(smb, cob, sst, loc_off, stack_off); return save(smb, cob, sst, loc_off, stack_off);
} }

View file

@ -7,7 +7,6 @@ import java.lang.classfile.attribute.StackMapFrameInfo;
import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.instruction.*; import java.lang.classfile.instruction.*;
import java.lang.constant.*; import java.lang.constant.*;
import java.lang.reflect.AccessFlag;
import java.util.*; import java.util.*;
import static java.lang.constant.ConstantDescs.*; import static java.lang.constant.ConstantDescs.*;
@ -30,7 +29,7 @@ public class FrameTracker {
ITEM_LONG_2ND = 13, ITEM_LONG_2ND = 13,
ITEM_DOUBLE_2ND = 14; ITEM_DOUBLE_2ND = 14;
public static ArrayList<Frame> frames(StateMachineBuilder<?> smb, CodeModel src_com) { public static ArrayList<Frame> frames(CoroutineBuilder<?> smb, CodeModel src_com) {
var ft = new FrameTracker(smb, src_com); var ft = new FrameTracker(smb, src_com);
var frames = new ArrayList<Frame>(); var frames = new ArrayList<Frame>();
for(var coe : src_com){ for(var coe : src_com){
@ -163,7 +162,7 @@ public class FrameTracker {
final ArrayList<Type> stack = new ArrayList<>(); final ArrayList<Type> stack = new ArrayList<>();
final ArrayList<Type> locals = new ArrayList<>(); final ArrayList<Type> locals = new ArrayList<>();
final StateMachineBuilder smb; final CoroutineBuilder smb;
LineNumber current_line_number = null; LineNumber current_line_number = null;
int bci = 0; int bci = 0;
@ -176,7 +175,7 @@ public class FrameTracker {
final HashMap<Label, List<LocalVariableAnnotation>> annotationEndMap = new HashMap<>(); final HashMap<Label, List<LocalVariableAnnotation>> annotationEndMap = new HashMap<>();
FrameTracker(StateMachineBuilder smb, CodeModel com) { FrameTracker(CoroutineBuilder smb, CodeModel com) {
this.smb = smb; this.smb = smb;
int offset = 0; int offset = 0;

View file

@ -18,18 +18,18 @@ public class SavedStateTracker {
} }
private String get_name(StateMachineBuilder<?> smb, ClassDesc desc){ private String get_name(CoroutineBuilder<?> smb, ClassDesc desc){
var value = smb.lstate.stream() // find unused state var value = smb.lstate.stream() // find unused state
.filter(v -> saved.stream().noneMatch(s -> s.name().equals(v.name()))) .filter(v -> saved.stream().noneMatch(s -> s.name().equals(v.name())))
.filter(v -> v.cd().equals(desc)).findFirst(); .filter(v -> v.cd().equals(desc)).findFirst();
if(value.isPresent()) if(value.isPresent())
return value.get().name(); return value.get().name();
var name = StateMachineBuilder.LOCAL_PREFIX+smb.lstate.size(); var name = CoroutineBuilder.LOCAL_PREFIX+smb.lstate.size();
smb.lstate.add(new StateMachineBuilder.LState(name, desc)); smb.lstate.add(new CoroutineBuilder.LState(name, desc));
return name; return name;
} }
public SavedState save_stack(StateMachineBuilder<?> smb, CodeBuilder cob, ClassDesc desc){ public SavedState save_stack(CoroutineBuilder<?> smb, CodeBuilder cob, ClassDesc desc){
var name = get_name(smb, desc); var name = get_name(smb, desc);
if(TypeKind.from(desc).slotSize()==2){ if(TypeKind.from(desc).slotSize()==2){
@ -43,7 +43,7 @@ public class SavedStateTracker {
return s; return s;
} }
public SavedState save_local(StateMachineBuilder<?> smb, CodeBuilder cob, ClassDesc desc, int slot){ public SavedState save_local(CoroutineBuilder<?> smb, CodeBuilder cob, ClassDesc desc, int slot){
var name = get_name(smb, desc); var name = get_name(smb, desc);
cob.aload(0).loadLocal(TypeKind.from(desc), slot).putfield(smb.CD_this, name, desc); cob.aload(0).loadLocal(TypeKind.from(desc), slot).putfield(smb.CD_this, name, desc);
@ -63,7 +63,7 @@ public class SavedStateTracker {
throw new RuntimeException(); throw new RuntimeException();
} }
public SavedStateTracker restore(StateMachineBuilder<?> smb, CodeBuilder cob, SavedState s){ public SavedStateTracker restore(CoroutineBuilder<?> smb, CodeBuilder cob, SavedState s){
if(!saved.remove(s))throw new IllegalStateException(); if(!saved.remove(s))throw new IllegalStateException();
switch(s){ switch(s){
case LocalState(var name, var desc, int slot) -> case LocalState(var name, var desc, int slot) ->
@ -74,21 +74,21 @@ public class SavedStateTracker {
return this; return this;
} }
public void restore_stack(StateMachineBuilder<?> smb, CodeBuilder cob){ public void restore_stack(CoroutineBuilder<?> smb, CodeBuilder cob){
for(int i = saved.size()-1; i >= 0; i --){ for(int i = saved.size()-1; i >= 0; i --){
if(saved.get(i) instanceof StackState) if(saved.get(i) instanceof StackState)
restore(smb, cob, saved.get(i)); restore(smb, cob, saved.get(i));
} }
} }
public void restore_locals(StateMachineBuilder<?> smb, CodeBuilder cob){ public void restore_locals(CoroutineBuilder<?> smb, CodeBuilder cob){
for(int i = saved.size()-1; i >= 0; i --){ for(int i = saved.size()-1; i >= 0; i --){
if(saved.get(i) instanceof StackState) if(saved.get(i) instanceof StackState)
restore(smb, cob, saved.get(i)); restore(smb, cob, saved.get(i));
} }
} }
public void restore_all(StateMachineBuilder<?> smb, CodeBuilder cob) { public void restore_all(CoroutineBuilder<?> smb, CodeBuilder cob) {
while(!saved.isEmpty()) while(!saved.isEmpty())
restore(smb, cob, saved.getLast()); restore(smb, cob, saved.getLast());
} }

View file

@ -2,6 +2,6 @@ package com.parkertenbroeck.bcsm.loadtime;
import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeBuilder;
public interface SpecialMethodBuilder<T extends StateMachineBuilder<T>> { public interface SpecialMethodBuilder<T extends CoroutineBuilder<T>> {
SpecialMethodHandler<T> build(T smb, CodeBuilder cob, Frame frame, StateBuilder sb); SpecialMethodHandler<T> build(T smb, CodeBuilder cob, Frame frame, StateBuilder sb);
} }

View file

@ -2,7 +2,7 @@ package com.parkertenbroeck.bcsm.loadtime;
import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeBuilder;
public interface SpecialMethodHandler<T extends StateMachineBuilder> { public interface SpecialMethodHandler<T extends CoroutineBuilder> {
void build_prelude(T smb, CodeBuilder cob, Frame frame); void build_prelude(T smb, CodeBuilder cob, Frame frame);
default boolean removeCall() { default boolean removeCall() {

View file

@ -12,8 +12,8 @@ public class StateBuilder {
cob.labelBinding(label); cob.labelBinding(label);
} }
public void setState(StateMachineBuilder smb, CodeBuilder cob){ public void setState(CoroutineBuilder smb, CodeBuilder cob){
cob.aload(0).loadConstant(id).putfield(smb.CD_this, StateMachineBuilder.STATE_NAME, ConstantDescs.CD_int); cob.aload(0).loadConstant(id).putfield(smb.CD_this, CoroutineBuilder.STATE_NAME, ConstantDescs.CD_int);
} }
} }

View file

@ -11,7 +11,7 @@ import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> { public class FutureSMBuilder extends CoroutineBuilder<FutureSMBuilder> {
public final static ClassDesc CD_Future = ClassDesc.ofDescriptor(Future.class.descriptorString()); public final static ClassDesc CD_Future = ClassDesc.ofDescriptor(Future.class.descriptorString());
public final static ClassDesc CD_Waker = ClassDesc.ofDescriptor(Waker.class.descriptorString()); public final static ClassDesc CD_Waker = ClassDesc.ofDescriptor(Waker.class.descriptorString());

View file

@ -9,7 +9,7 @@ import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.util.List; import java.util.List;
public class GenSMBuilder extends StateMachineBuilder<GenSMBuilder> { public class GenSMBuilder extends CoroutineBuilder<GenSMBuilder> {
public final static ClassDesc CD_Gen = ClassDesc.ofDescriptor(Gen.class.descriptorString()); public final static ClassDesc CD_Gen = ClassDesc.ofDescriptor(Gen.class.descriptorString());
public final static ClassDesc CD_Res = ClassDesc.ofDescriptor(Gen.Res.class.descriptorString()); public final static ClassDesc CD_Res = ClassDesc.ofDescriptor(Gen.Res.class.descriptorString());

View file

@ -3,5 +3,5 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
} }
rootProject.name = "generators" rootProject.name = "coroutines"
include("lib", "app") include("lib", "app")