diff --git a/README.md b/README.md index 0bc1d95..1ba9a56 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,31 @@ public static Gen primes() { } ``` +## Use this library + +```kotlin +// settings.gradle.kts +sourceControl { + gitRepository(URI.create("https://github.com/ParkerTenBroeck/coroutines.git")) { + producesModule("com.parkertenbroeck.coroutines:lib") + } +} + +// build.gradle.kts +implementation("com.parkertenbroeck.coroutines:lib:0.1.0") +``` + +This library requires the application be ran with a custom class loader, there is a utility provided to make this easier. +```java +public static void main(String[] args) { + // loads the current class with a custom class loader and calls *this* method with the provided arguments. + // *this* method - the one used to call `runWithCoroutine` + RT.runWithCoroutine(CoroutineClassLoader.Config.builtin(), (Object) args); + + // past this point Coroutines will be created on methods which match the criteria +} +``` + ## How does it know what functions to transform? Detection is done by @@ -61,6 +86,10 @@ public static Future regular_function() { } ``` +### Why not annotations? +Unfortunately annotations cannot be put on lambdas. + + ## Oddities ```java @@ -81,7 +110,38 @@ public static Future echo(🔷@Cancellation("close") Socket s ## Things to watch out for -### Future errors are not type checked +### Await function works specifically for the Future type + +```java +class MyFuture implements Future{ + public Object poll(Waker waker){ /* code */ } +} + +void Future wrong() { + new MyFuture().await();//⚡ + return Future.ret(); +} +``` +Internally the await call will become a `invokevirtual` call to `await` on `MyFuture`. Which will not currently be recognized as a "special" method to transform. + +To avoid this use static methods to return a future and make manually implemented futures have private/protected constructors +```java +class MyFuture implements Future{ + private MyFuture(){} + public static Future make(){ + return new MyFuture(); + } + public Object poll(Waker waker){ /* code */ } +} + +void Future wrong() { + MyFuture.make().await(); + return Future.ret(); +} +``` + + +### Future exceptions are not type checked ```java public static Future wrong() throws Exception { Waker.waker().wake(); @@ -130,7 +190,7 @@ public static Future add_reorder() { ### synchronized -when a generator/future method is declared as `synchronized` the `next`/`poll`/`cancel` functions will all be synchronized over the instance or class which the method was declared in. +when a coroutine method is declared as `synchronized` the `next`/`poll`/`cancel` functions will all be synchronized over the instance or class the method was declared in. it is important to note that the monitor is **NOT** held across suspend points where the function returns. @@ -146,22 +206,7 @@ static Future unsync(Object value) { ## How this works -This library requires the application be ran with a custom class loader as follows (this can be done manually if needed) -```java -public static void main(String[] args) { - // loads the current class with a custom class loader and calls *this* method with the provided arguments. - // *this* method - the one used to call `runWithCoroutine` - RT.runWithCoroutine(CoroutineClassLoader.Config.builtin(), (Object) args); - - // 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. - -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 +Say we have some async function like this. ```java public static Future example(@Cancellation("close") Socket socket, String message) throws IOException { try(socket){ @@ -170,6 +215,8 @@ public static Future example(@Cancellation("close") Socket so return Future.ret(); } ``` +The loader will see the methods which it needs to transform, then modify the bytecode, transforming the function into a class where state across suspend points is saved/restored (local variables & the current state of the stack). + This is a (modified) decompiled version of the generated method. ```java public static Future example(Socket socket, String message) { @@ -273,10 +320,10 @@ public static Future example(Socket socket, String message) { }; } ``` -### Steps -- A functions signature and code is determined to be "of interest" -- When a function is found we wish to modify it first builds a frame(locals, stack, annotations) for every area of interest in the function -- The number of unique locations which can be suspended from are kept track of -- A switch is built which handles each state the function can resume from setting up any locals/stack that needs to be resumed as well as setting up the code for saving locals/stack state -- the locations which special methods are found are modified to perform their particular function and potentially save state and return -- (Futures) generate cancellation code for locals which have specified so, and cancellation for a pending future. + +## Potential additions + +- Type checking during transformations +- Better debugger support (Currently line stepping is generally supported but most breakpoint sets are not working) +- Annotations (possibly integration during build time) +- More library features (async)