Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is the value of this? #54

Open
itboy2009 opened this issue Oct 29, 2020 · 13 comments
Open

What is the value of this? #54

itboy2009 opened this issue Oct 29, 2020 · 13 comments

Comments

@itboy2009
Copy link

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
Copy link

@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
Copy link
Author

@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
{
/**
* Here build a thread pool with only one thread
* If the await() method does not block, then the thread can be reused
*/
private static final ExecutorService workThreadPool = Executors.newFixedThreadPool(1);

/**
 * This simulates a blocking io operation
 */
private int blockIO() throws Exception
{
    HttpURLConnection httpURLConnection = (HttpURLConnection) new URL("https://github.com/electronicarts/ea-async/issues/54").openConnection();
    httpURLConnection.connect();
    InputStream in = httpURLConnection.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    StringBuilder responseStringBuffer = new StringBuilder();
    String line;
    while( (line = reader.readLine()) != null ){
        responseStringBuffer.append(line);
    }
    int contentLength = responseStringBuffer.length();
    return contentLength;
}

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

public void otherMethodUseWorkThreadPool()
{
    workThreadPool.submit(() -> {
        for(int i=0;i<5;i++) {
            try
            {
                TimeUnit.MILLISECONDS.sleep(200);
                System.out.println(String.format("threadId: %s", Thread.currentThread().getId()));
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    });
}

public void test() throws InterruptedException
{
    ExecutorService dispatchThreadPool = Executors.newFixedThreadPool(2);

    dispatchThreadPool.submit(() -> {
        String result = await(blockSomeThingFuture()); // block thread
        System.out.println(result);
    });

    TimeUnit.MILLISECONDS.sleep(50);

    dispatchThreadPool.submit(() -> {
        otherMethodUseWorkThreadPool(); // if await() don't block any threads.  This code should be executed first
    });

    dispatchThreadPool.awaitTermination(100, TimeUnit.SECONDS);
}

public static void main(String args[]) throws InterruptedException
{
    Async.init();
    DemoCases demoCases = new DemoCases();
    demoCases.test();
}

}`

@HaloFour
Copy link

@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
Copy link
Author

@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
Copy link

gary-archer commented Dec 15, 2020

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:

  • Javascript / TypeScript / NodeJS use promises
  • C# uses tasks
  • Kotlin and Swift use Coroutines

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
Copy link

papercuptech commented Jan 12, 2021

@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
Copy link

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

@itboy2009
Copy link
Author

itboy2009 commented Jan 13, 2021

@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
Copy link
Author

itboy2009 commented Jan 13, 2021

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
Copy link

@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
Copy link

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
Copy link

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
Copy link

vipcxj commented Sep 15, 2021

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants