electronicarts / ea-async

EA Async implements async-await methods in the JVM.
https://go.ea.com/ea-async
Other
1.38k stars 129 forks source link

What is the value of this? #54

Open itboy2009 opened 4 years ago

itboy2009 commented 4 years ago

await(blockSomeThingFuture());

just only equals to

this.blockSomeThingFuture().toCompletableFuture().join(); // block!!!

Why is it so complicated?

Or I didn’t understand, I look forward to your sharing

HaloFour commented 4 years ago

@itboy2009

The point of the project is to make it look very easy to write non-blocking code.

The project includes a class transformer that rewrites the bytecode of your methods so that they don't actually block any threads. When the execution of the method hits a call to await() on CompletableFuture that isn't done it wires up a callback on whenComplete and returns from the entire method immediately. When that callback is invoked it resumes within the middle of the method on a different thread by using a state machine.

itboy2009 commented 4 years ago

@HaloFour

First of all, thank you for sharing However, the test result is not what I expected The test results show that: await() will block the thread

here is my code:

`import com.ea.async.Async;

import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;

import static com.ea.async.Async.await;

public class DemoCases { /**

HaloFour commented 4 years ago

@itboy2009

The reason might be that the class transformer can't rewrite the class that's already executing. I'd suggest using the Java agent or the Maven plugin instead of relying on Async.init(). There are instruction on the README of this repository.

itboy2009 commented 4 years ago

@HaloFour

I have used agent, but the result is the same

I don't know where the problem is, You used it earlier, can you show your code?

gary-archer commented 3 years ago

I have found this library very useful, since it enables much simpler non blocking Rest API code than using a callback based model. Many other languages have this syntax, but EA Async is the only tool that enables it for Java. It is quite sad that this is not part of the language itself:

As an example, try to write the code in this class using a callback based syntax.

My blog has an advanced Java Spring Boot API that uses EA Async, and equivalent APIs in C# / NodeJS that implement the same requirements. For further Java coding details, see this blog post of mine on the Java non blocking coding model.

papercuptech commented 3 years ago

@itboy2009 can you try this?

public CompletableFuture<String> blockSomeThingFuture()
{
    System.out.println("blockSomeThingFuture(): enter");
    CompletableFuture<String> ret = CompletableFuture.supplyAsync(() -> {
        for(int i=0;i<5;i++) {
            try
            {
                System.out.println("blockSomeThingFuture(): before blockIO() call");
                int contentLength = blockIO();
                System.out.println(String.format("contentLength: %s, threadId: %s", contentLength, Thread.currentThread().getId()));
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        return "done";
    }, workThreadPool);
    System.out.println("blockSomeThingFuture(): exit");
    return ret;
}

And see if "blockSomeThingFuture(): exit" is output before "blockSomeThingFuture(): before blockIO() call"? If it isn't, then blockSomeThingFuture isn't returning to await() call before blocking thread

gary-archer commented 3 years ago

If it helps anyone I've put together a very simple Non Blocking Java Async Await API to demonstrate usage:

Code should be simple when using an async await coding model:

itboy2009 commented 3 years ago

@papercuptech

here is the output:

blockSomeThingFuture(): enter blockSomeThingFuture(): exit blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 blockSomeThingFuture(): before blockIO() call contentLength: 139181, threadId: 14 done

but, I don’t use ea-async, I can get the same output.

I mean, during the IO blocking period, ea-async did not release the thread to do other things

itboy2009 commented 3 years ago

If it helps anyone I've put together a very simple Non Blocking Java Async Await API to demonstrate usage:

Code should be simple when using an async await coding model:

  • No spinning up child threads
  • No callbacks / lambdas
  • No blocking code anywhere due to libraries such as Async HTTP Client

@gary-archer

public static <T> T myAwait(CompletableFuture completableFuture) {
    return (T) completableFuture.toCompletableFuture().join();
}

Use myAwait() instead of await(), you can get the same result

Is this the value of ea-async? I'm confused

HaloFour commented 3 years ago

@gary-archer

Are you using the maven or gradle plugins to transform the classes before attempting to decompile them?

I just tried the following simple project:

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;

import com.ea.async.Async;

public class Program {

    public static void main(String[] args) {
        Async.init();

        Test test = new Test();
        CompletableFuture<String> future = test.blockSomeThingFuture();

        Test.log("awaiting future");
        String result = Async.await(future);

        Test.log("complete: " + result);
    }
}

class Test {
    private static final Timer timer = new Timer();
    private static final Random random = new Random();

    public CompletableFuture<String> blockSomeThingFuture()
    {
        log("blockSomeThingFuture(): enter");
        try {
            Async.await(simulateIo());
            return CompletableFuture.completedFuture("Hello!");
        } finally {
            log("blockSomeThingFuture(): exit");
        }
    }

    private CompletableFuture<Void> simulateIo() {
        CompletableFuture<Void> future = new CompletableFuture<>();
        long delay = random.nextInt(1000) + 1000;
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                future.complete(null);
            }
        }, delay);
        return future;
    }

    public static void log(String message) {
        Thread currentThread = Thread.currentThread();
        System.out.printf("[%d:%s] %s%n", currentThread.getId(), currentThread.getName(), message);
    }
}

The output was:

[1:main] blockSomeThingFuture(): enter
[1:main] awaiting future
[14:Timer-0] blockSomeThingFuture(): exit
[1:main] complete: Hello!

You can see from the output that Test#blockSomeThingFuture returns immediately when it hits Async#await and the CompletableFuture<Void> is not yet done. Then the method resumes executing on the timer thread once the timer has elapsed and completed the future.

gary-archer commented 3 years ago

Thanks @HaloFour - I've used EA Async quite a bit in the past for sample purposes - the goals behind it are good - and when I've looked at decompiled code it has always looked correct.

A busy week at work this week so maybe I'm just confusing myself. Will look at your code at the weekend. Enjoying the discussion with you guys though ...

HaloFour commented 3 years ago

I haven't used ea-async in a while myself. My more recent projects are all based on Spring WebFlux and I never found a good synergy between the two nor do I feel like trying to adapt this project to work with Reactor.

One project you might want to keep your eyes on is Project Loom which looks to bring coroutine primitives and green threads to the Java runtime. Using them enables writing your own helper methods that behave like coroutines with parked green threads as the async state machine. I've emulated async/await using these primitives and it was pretty trivial and did not require an agent or byte code transformer. Although the main goal of the project is to make blocking I/O not such a bad thing by making it extremely cheap to block a green thread.

vipcxj commented 3 years ago

@HaloFour I release a new project JAsync implement async-await fashion in java which use Reactor as its low level framework. It is in the alpha stage. I need more suggest and test case. This project makes the developer's asynchronous programming experience as close as possible to the usual synchronous programming, including both coding and debugging.

98G39DXNF)ZI_C7`AP{7JZC

When debugging, you can see all variables in the monitor window just like when debugging normal code.