The tl;dr is that most folks agree a direct LockSupport.park/unpark implementation would be the most efficient, though there's more impl work to do than just leveraging e.g. ArrayBlockingQueue. I have done some experiments with raw parking, but I was never happy with the result. We should re-examine.
The text was updated successfully, but these errors were encountered:
I have played around with Generators in Java and built something very similar to this. After reading the thread on the concurrency-dev list, I re-wrote it to use a custom synchronizer that directly uses LockSupport to park/unpark threads.
The Google Code project it lives at is mostly a dumping ground for me doing experiments and toying around with ideas (lots of collections, concurrency, reflection, etc.). It's all available via Apache 2.0 license, but I'm happy for applicable parts of this to be used in JRuby and licensed under GPL/LGPL/EPL.
Warning, I've done a very shallow amount of testing, most of it exploratory (here is a sample harness that verifies simple use case plus proper interruption/clean-up of generator threads on GC). I haven't done benchmarks yet, but (anecdotally) it seems much faster than my previous approach, which used two SynchronousQueues.
I know this code is a bit raw and not yet sufficiently tested (or reviewed, since concurrency and the JMM can be non-trivial to get right). If you want help forcing it into shape, I might be able to lend some more time. Let me know.
Note that the javadoc I wrote might be outdated. It was describing performance woes when it was using SynchronousQueue. I also, at one point, was creating a new thread for each generator, which was also painful.
The code in its current incarnation uses a cached thread pool -- which effectively creates a new thread for each concurrent generator, but re-uses a thread after a generator on that thread completes.
A big downside to having to use add'l threads for this is when the main "consumer" thread never exhausts the generator (e.g. stops calling next, leaving generator in suspended state). In this case, the thread could be leaked.
The code I wrote works around that using a finalizer and weak references, to allow the main Sequence object (likely analogous to Fiber) to be GC'ed, at which point the corresponding suspended thread will be interrupted and throw a SequenceAbandonedException to terminate itself. But that relies on GC which is unpredictable, so extra suspended threads could survive long enough that heavy use of fibers could likely trivially cause an OOME (either OS limit for number of threads or out of address space for allocating the new thread's stack).