RFR (S): 8218483: Crash in "assert(_daemon_threads_count->get_value() > daemon_count) failed: thread count mismatch 5 : 5"

David Holmes david.holmes at oracle.com
Wed Apr 3 07:14:01 UTC 2019


Hi Thomas,

On 3/04/2019 4:37 pm, Thomas Stüfe wrote:
> Hi David,
> 
> 
> On Tue, Apr 2, 2019 at 10:57 PM David Holmes <david.holmes at oracle.com 
> <mailto:david.holmes at oracle.com>> wrote:
> 
>     Hi Thomas,
> 
>     Thanks for taking a look at this.
> 
>     On 3/04/2019 5:41 am, Thomas Stüfe wrote:
>      > Hi David,
>      >
>      > first thanks for the good analysis!
>      >
>      > Is this not a problem with the usage of setDaemon():
>      >
>      >
>     https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#setDaemon(boolean)
>      >
>      > "This method must be invoked before the thread is started."
> 
>     Not the usage as such, but there is a problem with setDaemon - as per:
> 
>     https://bugs.openjdk.java.net/browse/JDK-8221657
> 
>     The test that causes the crash in the VM deliberately tests a case
>     where
>     it expects to get the IllegalThreadStateException.
> 
>      > I think the real solution would be for setDaemon to distinguish
>     between
>      > not-yet-started, running and finished. It should not use
>     isAlive(). It
>      > should throw an exception if it has been started, regardless of
>     whether
>      > it finished already or not.
> 
>     Yes that fix is needed at the Java level. The use of isAlive()
>     pre-dates
>     the existence of Thread.State.
> 
>     But a change at the Java level may be some time coming given this is a
>     day one bug in the spec and implementation of Thread.setDaemon, so I
>     wanted to address this quickly in the VM as we are seeing these crashes
>     in testing.
> 
> 
> I think a simple patch could be very simply using
> 
> if (threadStatus != 0)
> 
> instead of
> 
> isAlive()
> 
> in Thread.setDaemon?

Sure the fix is trivial (plus the method needs to be synchronized), but 
that assumes that this spec inconsistency:

      * <p> This method must be invoked before the thread is started.
      *
      * @throws  IllegalThreadStateException
      *          if this thread is {@linkplain #isAlive alive}

is resolved in favour of the first statement. They may decide that after 
25 years it's better to maintain the "not alive" semantics and permit 
you to modify a terminated thread.

> We do this in other places in Thread.java too.
> 
> --
> 
> Also I think it makes sense to scan for similar errors in the code base 
> (isAlive being used as "has-been-started") and fix those too.
> 
> For example:
> 
> ApplicationShutdownHook.java:
> 
> static synchronized void add(Thread hook) {
>      if(hooks == null)
>          throw new IllegalStateException("Shutdown in progress");
> 
>      if (hook.isAlive())
>          throw new IllegalArgumentException("Hook already running");
> 
>      if (hooks.containsKey(hook))
>          throw new IllegalArgumentException("Hook previously registered");
> 
>      hooks.put(hook, hook);
> }
> 
> would register a terminated thread as shutdown hook. I found similar 
> looking code in ThreadPoolExecutor.

Yeah that's a nasty bug - you can register a shutdown hook that will 
result in other shutdown hooks not getting started!

> I really think the jdk would be really the right place to fix this.

And it may get fixed there eventually. Meanwhile I just want to stop 
these fairly new assertions from triggering.

Thanks,
David

> 
>     Thanks,
>     David
> 
>      > Not sure. Its late, I may not be thinking straight.
>      >
>      > Cheers, Thomas
>      >
>      >
>      >
>      > On Tue, Apr 2, 2019 at 12:33 AM David Holmes
>     <david.holmes at oracle.com <mailto:david.holmes at oracle.com>
>      > <mailto:david.holmes at oracle.com
>     <mailto:david.holmes at oracle.com>>> wrote:
>      >
>      >     Bug: https://bugs.openjdk.java.net/browse/JDK-8218483
>      >     webrev: http://cr.openjdk.java.net/~dholmes/8218483/webrev/
>      >
>      >     A bug in Thread.setDaemon (JDK-8221657) means that the daemon
>     state
>      >     of a
>      >     thread can change after the thread is !isAlive() at the Java
>     level. If
>      >     this happens before the VM call to
>     ThreadService::remove_thread then we
>      >     have a situation where we incremented the thread counters
>     when the
>      >     thread was not a daemon, and we decrement the thread counters
>     when the
>      >     thread is a daemon - and so the counters are out of sync and the
>      >     assertion fires.
>      >
>      >     The simple fix is to capture the daemon state of the thread
>     while it is
>      >     still alive and to pass that through to Threads::remove and thus
>      >     ThreadService::remove_thread.
>      >
>      >     Testing:
>      >         - manual test with modified VM (to delay Threads::remove
>     call)
>      >     as per
>      >     the bug report
>      >         - mach 5 tiers 1-3
>      >
>      >     Thanks,
>      >     David
>      >
> 


More information about the hotspot-runtime-dev mailing list