Java Continuations and GreenThreads

That’s cool, thanks! :slight_smile:

You can also rewrite the deployed jars with: http://pastebin.java-gaming.org/dd4093e4c42

That bytecode manipulation, it’s like crack! :wink:

I released the Java 6 versions of the library, can you verify it works?

[x] http://indiespot.net/files/projects/continuationslib/continuations-matthiasm-java6.jar
[x] http://indiespot.net/files/projects/continuationslib/continuations-riven-java6.jar
[x] http://indiespot.net/files/projects/continuationslib/continuations-riven-test-java6.jar

Yep, works great with Java 6, thanks! :slight_smile:

I got what this lib is doing now, but I wasn’t able to run my code. It got me this error:
Calling function not instrumented
In Coroutine.yield();.

I guess it is because of the bytecode not being changed. Having a yield() function simply reporting an error doesn’t make sense.

So I looked into continuation-agent.jar and found that MAINFEST. It had the “premain” stuff included, but I didn’t know how to use it. Either I did something wrong when I tried to run my exported .jar using a edited MAINFEST, or this doesn’t work.

I’ve also tried to look at ant. I didn’t use ant before, so this wasn’t easy for me, and I didn’t got it working in those couple of hours. I don’t know how to “integrate” the ant code Matthias Mann showed in his blog post.

Is there a way to activate the java code agent through code?

You can attach the java agent by following these instructions: :point:

http://indiespot.net/files/projects/continuationslib/usage.txt

Do I read this correctly - mixing green threads and real threads is problematic? I must misunderstand, as obviously this would mean not being able to take advantage of the multiple hardware threads of modern processors…?

Ah… I read the thread again, I think I get it now.

You need a VirtualProcessor per native thread.

I search literally everywhere. In MatthiasM’s blog, everywhere in this topic… I just didn’t got the Idea to look for a README or usage.txt in the Download directory… :confused: my bad

:slight_smile:

The file is also in the zip, which similarly contains the license files.
Out of all distributed files, files like README.txt are most often immediately skipped :point:

It’s actually not that odd that you couldn’t find it at Matthias Mann’s blog, as the agent code was originally mine and Matthiasm included it later.

Works perfectly here :slight_smile: Glad I could understand the basics of Green Threads and Continuations.

I just wonder, what if I want all my entities (which run on a VirtualThread each) have a delta time delivered. Just like I usually do it with a tick(float delta) method. How can I do that?
I don’t actually have the problem, since there is [icode]Gdx.graphics.getDeltaTime()[/icode] but I don’t find ‘static’ to be a solution.

Ticked based approaches are the opposite of what green threads are meant to provide.

Having two “services” for each entity is what i prefer. One “high level script” (or “behavior script”) with continuations, that gives orders like this:

set_destination(target)
wait_until_target_reached_or_failed

Then a second, tick based loop, which handles the per-frame updates to position, direction, path following etc. This second service can run in a separate native thread, as you are suggesting, without using green threads. Then the two threads exchange messages. The path generation can be done on the script thread because that thread is not time sensitive, and generating a path is a one-time big time expense, while path-following is not. If some script event is delayed, thats not a problem. The bot might just stand a couple of more milliseconds where its path ended before continuing with its next action, but that is not game-breaking. But statistically, bots will be always following a path, and will rarely require attention from the script thread. Attacking the player, however, must be done on the tick thread, because the bot must react to player right away. What the script thread can do, is to set the bots reaction to player. Something like: “attack on sight”, “neutral”, “friendly”, “ignore”, “stop the path if attacked” etc. Then the condition checking and actual attacking is done by the per-frame thread. If the script wants the attack to end, it has to change the reaction type.

That’s indeed how you’d do it, except that I’m strongly suggesting to keep the second service (running the VirtualProcessor) in your main thread too. It makes sure that all memory read/write access is done on the same thread, which means your code is much simpler to comprehend and debug, as everything is deterministic.


while(true) {
   virtualProcessor.tick(); // instruct unit movement, wait for arrival

   gameLogic.tick(); // actually move the units around

   engine.render();
}


public class Unit implements VirtualRunnable {
   private VirtualCondition targetReached = new VirtualCondition();
   private int xTarget, yTarget;

   public void moveTo(int x, int y) {
      xTarget = x;
      yTarget = y;
   }

   public void tick() {
      x += Integer.signum(xTarget-x);
      y += Integer.signum(yTarget-y);

      if(x==xTarget && y==yTarget) { 
         targetReached.signal();
      }
   }

   @Override
   public void run() throws SuspendedExecution {
      while(...) { // move back and forth
         this.awaitArrivalAt(13,14);
         this.awaitArrivalAt(17,8);
      }
   }

   public void awaitArrivalAt(int x, int y) throws SuspendedExecution {
      this.moveTo(13,14);

      // this will fully suspend the green thread, there is no polling!
      targetReached.await();
   }
}

Yeah that is indeed simpler, but the path generation tends to be quite expensive. So some kind of scheduler is needed to make sure, that it doesn’t happen that in a single iteration a bunch of bots request a new path at the same time, because that would be noticeable. So path generation is a good candidate to move out to a second thread. If its on a separate thread, then that thread can generate paths non-stop. And since path generation is tied to what the bot would actually do, the whole scripting is simpler and no “path generation queue” is necessary, if all is done with green threads. The bots will wait for their path until their turn comes to get it generated. I’m assuming that all this is running on a multicore CPU. With a single core, all should run on a single thread, like you described.

Preferably you’d do VirtualThread.yield() in your path-finder, as to let the AI of multiple units handle path-finding concurrently (on the same thread), The longer a unit searches, the longer that specific unit has to wait for the final path, without blocking any other unit.

From an AI point of view, this is ‘fair’, and technically it’s ‘nice’ (I love subjective terms).

I can’t stress this enough, keep all your logic on 1 native thread, if you value your sanity.

What I have right now is this:


@Override
public void run() throws SuspendedException {
    move(speed * Gdx.graphics.delta);
    VirtualThread.yield();
}

So you suggest not to do that, since it is ‘ticky’ ( ::slight_smile: )… What about this?


@Override
public void run() throws SuspendedException { 
    move(speed);
    // Probably also with subtracting the time the AI computation took:
    VirtualThread.sleep(16);
}

Well… this is awesome… I just Imagine what would happen. We wouldn’t have a delta-time dependent game loop in the end, but a ‘multiple-update render-once’ loop. That fancy one, which doesn’t make the 125 FPS jumps theagentd linked happen…

Please look very closely at the example code I posted here:

Uh, what? Did I miss something?