Resource TaskChain - Proper Async Operations and more!

Discussion in 'Spigot Plugin Development' started by Aikar, Oct 30, 2016.

  1. Looks good!
  2. Bump for greatness.
  3. Could you add a timeout method for when any single part of the chain (or the total chain time) is taking too long? It would be super cool if there was something like:
    Code (Text):
        .timeout(5, TimeUnit.SECONDS)
        .async(() -> {
            // Do some database getting/setting
    • Like Like x 1
  4. MiniDigger


    I think it's more logical to first do the action and then place the timeout tho.
  5. TaskChain is a series of instructions. The next task wouldnt run until the previous one finished.

    I think this is maybe something better handled in user land.
    your DB calling code should have a way to configure a timeout.

    But you could use an async executing task, set a booleanfor hasFired = false;
    dispatch db query, and then also dispath a timeout task

    in timeout, call chain.abort(), and set hasFired = true; and optionally call next
    then in db query, when it does finally return, make sure hasFired is still false before calling next.
    • Like Like x 1
  6. long overdue bump. been so busy lately. We've got some cool issues open atm on github that soon as my IRL opens up, i might work on soon.
  7. Are shared chains meant to save data between chains? It was a while ago, but I recall trying to set data in one shared chain and then only being able to get it in that same shared chain. For example if I had chain1 and chain2 (both are shared chains with the same name), chain1's task data would be independent of chain2's task data. I think it would make sense to have the same data globally across the same shared chain, so I'm not sure if that's a bug or a feature.
  8. No, they are called Shared Chains because they share the same execution pipeline (meaning they append)

    However, Once the shared chain ends and no others are pending, nothing exists in memory about that chain.

    So it's not possible to retain shared data between them. It would also create a complicated mess about who controls the data. If you have 2 threads creating a shared chain, who is creating the data?

    This sounds like risky app design.

    Could you explain what you're trying to do, and maybe I can provide a better solution?
  9. Currently I execute some code in pre-login (using current() to make it run on the player's login thread) and then have another chain that runs more code (code that required an actual Player object as opposed to just the UUID that pre-login provides) during the login phase. So this has been working perfectly fine for a while. I just have a temporary cache of login data that pre-login writes to and login reads from. It would just be simpler and cleaner for this specific use-case if I could use the same shared chain and set chain data instead of using my login cache.

    But that doesn't work because, like you said, once there's nothing appending, the chain ends. And there's nothing appending by the time the pre-login chain ends and the login chain starts. Even if I save the shared chain variable globally and append to that chain, it doesn't work. So to clarify it's all good and I have a decent solution. I was just curious about how that all works.
  10. The cache approach is what I use myself and recommend.

    TC would not be a good solution for this, it would ultimately be the same thing as the cache approach, just without expiration, which would lead to memory leaks.
    • Like Like x 1
  11. President Trump has invaded.
  12. Bump for better control flow!
  13. Hey I always do:

    Code (Text):
    new Thread(() -> {
        //Do whatever
    Is there any difference? Maybe less output but that isn't really needed.
  14. MiniDigger


    There are many differences.
    First, you should not use thread in bukkit at all. Use the bukkit scheduler.
    Then, task chain only gets useful if you need to switch between running stuff on main thread and async. Imagine you want to spawn some entity from data in a db. Then you need to asyncly get the data and one you got it, spawn the entity on the main thread. If you do that with the scheduler, you would have a nested anonymous inner class, which is extremely ugly. Taskchain fixes that and provides many more useful stuff for use cases like that.
  15. To rephrase some of what MiniDigger said cleaner lol:
    1. Cleaner and easier to read code. The style you mentioned will require multiple levels of nesting callbacks to switch thread scope
    2. Efficiency of Thread pools. It holds threads waiting for them to be reused, reducing thread overhead
    3. Context Aware switching: You state your intent (async or sync) and TC worries about "Do I need to switch?"
    4. Having readily available utility like this encourages a re-usable functional code design, encouraging you to design your code more async friendly (since it's not as difficult to do it anymore)
    5. Concurrency logic help with Shared Chains (enforce that 2 actions of a related nature can not happen at the same time)
  16. FrostedSnowman

    Resource Staff

    a bit confused after looking at examples, but how would i use a callback/future to return something from an async chain execution?

    Code (Java):

    public String foo() {
         return string from async chain operation here
  17. You'd need to return the task, or you'll stop the thread it's ran on.
  18. FrostedSnowman

    Resource Staff

    how do i return a value from the chain? how do i use the built in callback / future
  19. by calling syncLast or asyncLast.
    to get the value directly from that method you'd stop the current thread..
  20. Is it also able to make a aSync task spread over ex 10 seconds. because I want to send 51*51*51 packets to a player. and doing this with a new thread doesnt keep the tps high.