[concurrency-interest] Hung progress in ThreadPoolExecutor ExecutorCompletionService when slave threads killed.

David Holmes davidcholmes at aapt.net.au
Mon May 14 03:21:19 EDT 2012


If it is any consolation AppContext uses a similar approach and can also hit problems because of stop(). But at least there we don't attempt to reuse any objects that were in use by the threads that got killed. I confess I'm not completely clear on your Executor usage here.

Generally Thread.stop does far more damage than incidental non-malicious threads; and malicious threads can just ignore any exception you throw at them via Thread.stop.

Aside: for a while now there's been some background thought on how to fix this for the more common cases of StackOverflowError and possibly OutOfMemoryError. Handling OOME is a somewhat more tractable problem at least in lower-level library code. StackOverflowError is rather nasty and will probably need some VM assistance.

David
-----

> -----Original Message-----
> From: Dawid Weiss [mailto:dawid.weiss at gmail.com]
> Sent: Monday, 14 May 2012 5:00 PM
> To: dholmes at ieee.org
> Cc: Dr Heinz M. Kabutz; Zhong Yu; concurrency-interest
> Subject: Re: [concurrency-interest] Hung progress in ThreadPoolExecutor
> ExecutorCompletionService when slave threads killed.
> 
> 
> Thanks for the discussion.
> 
> I was thinking much like Heinz -- that the lock should be reacquired
> (and was surprised that it wasn't). David's arguments are clear to me
> though.
> 
> While thread.stop() is an ugly, ugly way to fix stuff there is really
> no alternative to it if you have a situation in which you run
> third-party code and you don't want to allow any background threads to
> survive (other than spawning a separate jvm, but that's another
> issue). It is an interesting use case on its own -- what I need is a
> facility like a "domain context" that could be shut down when no
> longer needed. But with executors it is nearly impossible -- even if
> you run in a separate class loader that could be gc-ed entirely, if an
> executor is stored in a static reference, it keeps refs to slave
> threads and these in turn cannot be killed (when they're interrupted
> new ones fill up the thread buffer again). Of course it's also
> possible to create a "chuck norris" type code which cannot (or is hard
> to) be killed (catching ThreadDeath, respinning in a try-catch) but
> this is rather uncommon while executors are everywhere.
> 
> There seems to be no way to clean up the slave threads and thus
> somehow close the pool (interrupting them just refills the buffer with
> new ones). Sure, I could override bootclasspath or do runtime bytecode
> rewriting trickery to detect when executors are created (and then
> shutdown them nicely) but it is so dirty and doesn't really protect
> against similar situations in custom code. Eh.
> 
> Dawid
> 
> 
> 
> On Mon, May 14, 2012 at 7:23 AM, David Holmes 
> <davidcholmes at aapt.net.au> wrote:
> > Heinz,
> >
> > Dr Heinz M. Kabutz writes:
> >> When you stop() a thread that has called wait(), it first reacquires
> >> the monitor before bailing out.  The monitor is then automatically
> >> unlocked again when you leave the scope.
> >
> > This is possible because the monitor reacquisition happens in the VM's
> > native code, not part of any Java code sequence.
> >
> >> However, Condition.await() does *not* reacquire the lock when you
> >> stop() the thread.  When unlock() is then called on Lock as you exit
> >> the scope, an IllegalMonitorStateException is then thrown.  I would
> >> love to know if that is deliberate or a coding bug?
> >
> > It is unavoidable due to the fact this is implemented in Java 
> code. People
> > will say "put the lock re-acquisition in a finally block, that 
> will fix it",
> > but that will only fix the case where the stop() hits while the 
> thread is
> > actually blocked. If the stop() hits during any of the queue 
> management code
> > you can easily get corrupt data structures.
> >
> > It is impractical, if not impossible, to non-trivial write 
> async-exception
> > safe code.
> >
> > David
> > -----
> >
> >> Anyway, my bet is that this is the culprit.  You can also see the
> >> await() method in the stack trace.
> >>
> >> Heinz
> >>
> >> On 14/05/2012, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> >> > If an exception can occur any where any time, it is impossible to
> >> > write code that can survive it and maintain invariants.
> >> >
> >> >     lock
> >> >         x++;
> >> >         y++;
> >> >     unlock
> >> >
> >> > If we have to worry that some exception may be thrown between x++ and
> >> > y++, we cannot write any program at all.
> >> >
> >> > ThreadDeath is exactly such an exception.
> >> >
> >> > Others include OutOfMemoryError and StackOverflowError - 
> they are more
> >> > predictable, but still, we cannot afford to worry about them on every
> >> > `new` and every method call.
> >> >
> >> > Zhong Yu
> >> > _______________________________________________
> >> > Concurrency-interest mailing list
> >> > Concurrency-interest at cs.oswego.edu
> >> > http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >> >
> >>
> >>
> >> --
> >> Dr Heinz M. Kabutz (PhD CompSci)
> >> Author of "The Java(tm) Specialists' Newsletter"
> >> Sun Java Champion
> >> IEEE Certified Software Development Professional
> >> http://www.javaspecialists.eu
> >> Tel: +30 69 75 595 262
> >> Skype: kabutz
> >> _______________________________________________
> >> Concurrency-interest mailing list
> >> Concurrency-interest at cs.oswego.edu
> >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>
> >
> > _______________________________________________
> > Concurrency-interest mailing list
> > Concurrency-interest at cs.oswego.edu
> > http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> 




More information about the Concurrency-interest mailing list