example of cooperative fibers, ie single threaded, non-fork-join, non-thread-pool

185 views
Skip to first unread message

blue/zero

unread,
Mar 26, 2016, 1:34:09 PM3/26/16
to quasar-pulsar-user
i've taken a look at quasar a couple other times and always been overwhelmed by the lack of documentation and the amount of black magic happening under the hood. decided to take advantage of the long weekend to finally grok it

for stateless operation such as a web server, you can just use the fork/join scheduler and things work out of the box. but the real power of fibers is the ability to manually schedule them - obviating the need for locks in many cases. there's not much documentation or many examples of doing this, and based on some of the comments from pron (eg, "we won't be recreating a continuation") on this mailing list i nearly concluded that it couldn't be done

after 16 hours yesterday, things finally started to work. here's a "hello world" for fibers using a manual scheduler, park, unpark and start. this all runs in a single thread (2 scheduler threads are created but i don't believe they ever do any work) without recursion. my "scheduler" is trivial, but could be extended to a state machine (one of the most powerful metaphors for using fibers) in a straight-forward way




// copyright 2016
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberExecutorScheduler;
import co.paralleluniverse.fibers.SuspendExecution;
import java.util.stream.Stream;

/** 
 * demo of non-thread-pool quasar, ie manual control over fiber execution -
 * this is the most basic operation of fibers, a "hello world" for fibers
 */
public class DemoQuasar {
    public static class MyFiber extends Fiber<Void> {
        long num = 0;
        MyFiber(FiberExecutorScheduler exe) { super(exe); }
        protected Void run() throws SuspendExecution, InterruptedException {
            num *= 3;
            Fiber.park();
            num *= 7;
            return null;
        }
    }
    public static void main(String[] args) throws Exception {
        FiberExecutorScheduler exe = new FiberExecutorScheduler("demo", cmd -> cmd.run());

        MyFiber [] fibs = new MyFiber[100000];
        for (int ii=0; ii < fibs.length; ii++)
            (fibs[ii] = new MyFiber(exe)).num = ii;

        Runnable sum = () -> System.out.println(
                Stream.of(fibs).mapToLong(x->x.num).sum()*1.0/fibs.length);

        sum.run();
        for (Fiber fib : fibs) fib.start();
        sum.run();
        for (Fiber fib : fibs) fib.unpark();
        sum.run();
    }
}

Fabio Tudone

unread,
Mar 27, 2016, 1:31:13 AM3/27/16
to quasar-pulsar-user
Hi,


On Saturday, March 26, 2016 at 8:34:09 PM UTC+3, blue/zero wrote:
i've taken a look at quasar a couple other times and always been overwhelmed by the lack of documentation and the amount of black magic happening under the hood. decided to take advantage of the long weekend to finally grok it

I guess you're referring to lack of documentation about the specific use case you describe below because I think the user documentation is pretty complete, including references to examples, templates and tests, and there's also quite some info about the inner workings.

If you refer to the code itself I can share my experience: after (quite) a while working with it, I now see it's well organized, readable and documented in pretty much the right spots; it's just that the inherent complexity of what it does is all but trivial. Of course nothing is perfect and suggestions (or even better contributions) are always welcome.

Or maybe you are referring to something else?

for stateless operation such as a web server, you can just use the fork/join scheduler and things work out of the box. but the real power of fibers is the ability to manually schedule them - obviating the need for locks in many cases. there's not much documentation or many examples of doing this, and based on some of the comments from pron (eg, "we won't be recreating a continuation") on this mailing list i nearly concluded that it couldn't be done

The park/unpark API isn't an organic continuations API and is meant for special use cases so that's why is not documented extensively; a continuations API will be facilitated by automatic instrumentation (which is supposed to land with or shortly after Java 9 as it depends on some features being developed there). But of course Quasar includes several tests about park/unpark so anyone interested can start from there (and/or ask for advice here).

seth lytle

unread,
Mar 27, 2016, 12:57:33 PM3/27/16
to Fabio Tudone, quasar-pulsar-user
I guess you're referring to lack of documentation about the specific use case you describe

"co-operative" isn't a specific use case, it's fundamental to a fiber, which is what quasar purports to be
  • your links mention FiberExecutorScheduler exactly once, and only to say "even on a particular thread" and link the javadocs
  • those javadocs provide no usage guidance and comprise in total 3 short sentences that do nothing but rehash the method and parameter names
  • the javadocs for Fiber have no description for interrupted, park, parkAndUnpark, sleep, yield, and yieldAndUnpark - all static methods in the public API
  • tests aren't a meaningful way to document a public API because they are injected into a package, exposing the internal API

is that enough documentation of your lack of documentation to get you to drop the condescending attitude, or should i go on ?

as for the code itself, i agree with you. i found an example that used sleep() that ran successfully and then followed thru in a debugger to figure out park/unpark. looked like a reasonable level of abstraction 

 
suggestions (or even better contributions) are always welcome 

this was my purpose in posting the example
 
and/or ask for advice here 

then let me be more explicit
  • does my example work because i'm using the park/unpark API correctly ?
  • if i want to abandon an unfinished computation, is it sufficient to null out the fiber or do i need to do something else to free up some hidden resource ?

after getting this example to work, i went back and tracked down (thanks git) why my naive attempts failed and found what i'm pretty sure is a bug in the instrumentation. the example prints "hello" only, but if you change park() to Fiber.park() then it prints "hello world"


import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberExecutorScheduler;
import co.paralleluniverse.fibers.SuspendExecution;

public class DemoQuasar {
    public static class MyFiber extends Fiber<String> {
        MyFiber() { super(new FiberExecutorScheduler("myrunner", cmd -> cmd.run())); }
        protected String run() throws SuspendExecution, InterruptedException {
            System.out.println("hello");
            park();
            System.out.println("world");
            return null;
        }
    }
    public static void main(String[] args) throws Exception {
        Fiber fiber = new MyFiber();
        fiber.start();
        fiber.unpark();
    }
}





 

--
You received this message because you are subscribed to the Google Groups "quasar-pulsar-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quasar-pulsar-u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quasar-pulsar-user/af8f3528-fe74-477c-a8e6-1fea4a684be3%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

pron

unread,
Mar 27, 2016, 3:04:08 PM3/27/16
to quasar-pulsar-user, fa...@paralleluniverse.co
> is that enough documentation of your lack of documentation to get you to drop the condescending attitude, or should i go on ?

This kind of tone will not be tolerated on this mailing list. You may ask questions politely and respectfully or not at all.

park/unpark and other low-level methods are under-documented because we want to strongly discourage their direct use, which is hard to get right in the presence of concurrency (the same goes for parking/unparking threads), and also because of Quasar-specific requirements, such that park/unpark must be called with the class name prefix (it is not a bug). In some other cases, it is simply an oversight, but hey, this is an open source project, and we gladly take PRs. Whichever is the case, we are glad to help, provided that the discussion is civil. FYI, Quasar fibers are preemptive, not cooperative.

Ron

Fabio Tudone

unread,
Mar 27, 2016, 3:17:34 PM3/27/16
to quasar-pulsar-user, fa...@paralleluniverse.co
On Sunday, March 27, 2016 at 7:57:33 PM UTC+3, blue/zero wrote:

I guess you're referring to lack of documentation about the specific use case you describe

"co-operative" isn't a specific use case, it's fundamental to a fiber, which is what quasar purports to be

I haven't found yet a definition of "fiber" more rigorous than "lightweight threads w/cooperative multitasking" but anyway "cooperative multitasking" means that generally fibers "yield" control (as opposed to being preempted by a controlling entity, e.g. the OS) and not that they execute on a single thread (on the opposite, by default Quasar schedules on a FJP using a number of threads equal to the number of cores), so I still see this as a specific use case.

As an aside, yield calls are not necessarily always in control of the developer: in Quasar's case they're performed by concurrency primitives and in the past Quasar even went further by automatically inserting such calls at specific points during instrumentation (I think there's a discussion about this topic and about why it's not done anymore in this very group). This means that it's not accurate even saying that fibers (well, at least Quasar's) can only multitask cooperatively.

There's a GitHub question with a great deal of information about a very similar use case, including elaboration on caveats and the current intention of not exposing continuations directly: https://github.com/puniverse/quasar/issues/99

  • your links mention FiberExecutorScheduler exactly once, and only to say "even on a particular thread" and link the javadocs
  • those javadocs provide no usage guidance and comprise in total 3 short sentences that do nothing but rehash the method and parameter names
  • the javadocs for Fiber have no description for interrupted, park, parkAndUnpark, sleep, yield, and yieldAndUnpark - all static methods in the public API
  • tests aren't a meaningful way to document a public API because they are injected into a package, exposing the internal API
is that enough documentation of your lack of documentation to get you to drop the condescending attitude, or should i go on ?

My goal was to understand better what you meant with "lack of documentation", I think I do now.
  • does my example work because i'm using the park/unpark API correctly ?
It seems correct to me, more generally Strand.park() parks a strand (currently, fiber or thread) and Strand.unpark() unparks it. You don't join the fiber at the end but you don't need to in this case because the main thread is executing it so there's no chance it's going to exit before the fiber completes run().
  • if i want to abandon an unfinished computation, is it sufficient to null out the fiber or do i need to do something else to free up some hidden resource ?
In this case it looks like nothing holds onto the fiber, not even the FiberExecutorScheduler instance, so there's nothing else to do.

Sean Kelly

unread,
Apr 8, 2016, 2:03:17 AM4/8/16
to quasar-pulsar-user
On Saturday, March 26, 2016 at 10:34:09 AM UTC-7, blue/zero wrote:
i've taken a look at quasar a couple other times and always been overwhelmed by the lack of documentation and the amount of black magic happening under the hood. decided to take advantage of the long weekend to finally grok it

for stateless operation such as a web server, you can just use the fork/join scheduler and things work out of the box. but the real power of fibers is the ability to manually schedule them - obviating the need for locks in many cases. there's not much documentation or many examples of doing this, and based on some of the comments from pron (eg, "we won't be recreating a continuation") on this mailing list i nearly concluded that it couldn't be done

after 16 hours yesterday, things finally started to work. here's a "hello world" for fibers using a manual scheduler, park, unpark and start. this all runs in a single thread (2 scheduler threads are created but i don't believe they ever do any work) without recursion. my "scheduler" is trivial, but could be extended to a state machine (one of the most powerful metaphors for using fibers) in a straight-forward way

I think this is one of the great advantages of fibers in general, as I find the generator and coroutine model to be extremely powerful. However, I also think that having fibers backed by a thread pool was a deliberate design decision here, and so while it may be possible to make fibers work this way using Quasar, I don't know that you actually want to. Particularly when server-type things are concerned, you really want a fork/join pool design anyway, so I might relegate this one to things in-thread fibers are good for, like generators. Also, some thought should be given to the possibility of your type of fiber interacting with a fork/join fiber (you can back different fibers by different schedulers, right?), or fibers calling other fibers in general. Things get potentially a bit weird when you have an in-thread fiber spawning another in-thread fiber. It's possible that Quasar totally supports this, but I'd give it some really careful thought to be sure.

blue

unread,
Apr 12, 2016, 12:31:22 AM4/12/16
to Fabio Tudone, quasar-pulsar-user
pron wrote:
FYI, Quasar fibers are preemptive, not cooperative

fabio wrote:
I haven't found yet a definition of "fiber" more rigorous than "lightweight threads w/cooperative multitasking" 

this is probably a big part of my confusion - quasar looks like a fiber library, but (the non-strongly-discouraged portion of) quasar fibers aren't fibers, and quasar is more of an alternative to JVM threads (with much lower costs for some use cases). the quasar docs overview doesn't actually mention fibers, but the rest of the document uses the term freely and most of the articles i read highlighted the fiber aspect (i'll make a separate post about something i found looking back over them while writing this reply)

it's a good thing i didn't know this when i started ... i might have given up. as is, i ended up porting my application to quasar pretty quickly once i had the hello-world demo working (i was already managing concurrency by effectively single-threading things). i hope you'll keep the (discouraged) portions that allow for cooperative scheduling of fibers in future versions of quasar. and thanks for taking a look at my usage of the park/unpark api

in addition to this manually scheduled portion, i have a thin stateless front end, and for that i used the default FJP scheduler, and i bridged the two using a quasar channel



 

--
You received this message because you are subscribed to the Google Groups "quasar-pulsar-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quasar-pulsar-u...@googlegroups.com.

Fabio Tudone

unread,
Apr 12, 2016, 2:13:01 AM4/12/16
to quasar-pulsar-user, fa...@paralleluniverse.co
On Tuesday, April 12, 2016 at 7:31:22 AM UTC+3, blue/zero wrote:
pron wrote:
FYI, Quasar fibers are preemptive, not cooperative

fabio wrote:
I haven't found yet a definition of "fiber" more rigorous than "lightweight threads w/cooperative multitasking" 

this is probably a big part of my confusion - quasar looks like a fiber library, but (the non-strongly-discouraged portion of) quasar fibers aren't fibers, and quasar is more of an alternative to JVM threads (with much lower costs for some use cases). the quasar docs overview doesn't actually mention fibers, but the rest of the document uses the term freely and most of the articles i read highlighted the fiber aspect (i'll make a separate post about something i found looking back over them while writing this reply)

Yes but these are not mathematical definitions, for example the "lightweight threads w/cooperative multitasking" one lacks context. From which perspective? From the perspective of Quasar itself definitely they multitask cooperatively, but not so from the user's perspective.

So what are they, fibers or not? Technically (and with that I mean from Quasar's perspective) they are fibers and that's why we call them that way; on the other hand they yield automatically and their current recommended use pattern even discourages the user from doing so manually, so if you look at them from this point of view they're definitely preemptive (and they were even more in the past). If for you this means they're not "fibers" (well, at least not "regular" ones) then OK, I understand your point, so be it.

I think it's more important that they're useful for the purposes they've been designed, and of course that this matches what's actually needed by users, rather than 100% compliant with a non-rigorous definition of exactly one word, no matter how widely used. Does this make it more difficult to grasp? Perhaps, but I think it's worth.

Then I agree with you that continuations and user-level cooperation and scheduling are needed too, we're well aware of that and we definitely want to provide them; still, what we consider a proper continuations API, one we are ready to bet on and support in the long term, just requires some more time (and one more feature that will likely come as part of Java 9).

it's a good thing i didn't know this when i started ... i might have given up. as is, i ended up porting my application to quasar pretty quickly once i had the hello-world demo working (i was already managing concurrency by effectively single-threading things). i hope you'll keep the (discouraged) portions that allow for cooperative scheduling of fibers in future versions of quasar. and thanks for taking a look at my usage of the park/unpark api

We tend to be conservative about changes because Quasar is production-ready (and used in production-grade software), we know the benefits of reliability and the consequences of being disrupted. On the other hand when a continuations API will be available for Java 9+ people that can move up should really do it.

in addition to this manually scheduled portion, i have a thin stateless front end, and for that i used the default FJP scheduler, and i bridged the two using a quasar channel

Glad you've found your way into this. Well done!

-- Fabio

Reply all
Reply to author
Forward
0 new messages