[concurrency-interest] ArrayBlockingQueue (and possibly others) not waking on interrupt?
David M. Lloyd
david.lloyd at redhat.com
Mon Dec 1 17:12:51 EST 2014
On 12/01/2014 02:17 PM, Charles Oliver Nutter wrote:
> Sense and sanity prevail! Given the new information that this is
> probably my fault, I dug a bit deeper...and found a race within my own
> code. The thread interrupt logic in JRuby was firing before an
> unblocker function was installed, causing the interrupt to just set
> flags and not forcibly interrupt the thread. The blocking call then
> proceeded and the flags were not checked again until after it had
> completed. Fixed it by adding a second flag check *after* the
> unblocker has been set but *before* making the blocking call, and it
> seems solid now.
> Incidentally, I have a question...
> This logic is being used to emulate fibers (coroutines) in JRuby. A
> fiber belongs to a given thread, and there may be many active fibers
> within a thread, but none of them run concurrently. Every transfer
> into a fiber is an explicit hand-off. If you're familiar with Python
> generators or Go's goroutines, you get the idea. I've struggled to
> find the right data structure to efficiently implement this hand-off.
> * Exchanger was just about the slowest way. I never figured out why.
> * Manually implementing it with park/unpark worked, but I could never
> get it perfect. Might have been this race or another race, so I may
> * new ArrayBlockingQueue(1) ultimately ended up being the fastest by a
> wide margin.
> I believe I've asked this before, but what's the fastest way to
> *explicitly* transfer control to another thread in an interruptible
I think probably writing a work item to a thread-specific field and
unparking the thread must be the fastest possible way to give a specific
waiting thread some specific work. I don't think that unpark does
anything in terms of memory visibility though (in any event I'm not
seeing anything in the javadoc for LockSupport) so the field probably
would have to be volatile for this to work in its simplest form.
Tracking and deciding which among multiple threads (if you have more
than one in a pool situation) are ready for work adds a bunch of
complexity though, as does coping with having no workers available (e.g.
do you make the producer wait, or employ a pending tasks queue?).
Follow this path a surprisingly short distance and you're facing the
same problems that ThreadPoolExecutor attempts to solve.
More information about the Concurrency-interest