JDK-8163553 : java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2016-08-10
  • Updated: 2017-05-04
  • Resolved: 2016-11-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b146Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
java/lang/ThreadGroup/Stop.java
test may fail with java.lang.LinkageError, intermittently.

This starts happening since JDK9 build 129.
With 9/b128, 9/b127 and 9/b125, 50,000 times of run, all pass
With 9/b129, 8 (out of the total 50,000) times failed with following error.

Exception in thread "Thread-0" java.lang.LinkageError
        at java.lang.invoke.MethodHandleNatives.linkMethodImpl(java.base@9-ea/MethodHandleNatives.java:386)
        at java.lang.invoke.MethodHandleNatives.linkMethod(java.base@9-ea/MethodHandleNatives.java:366)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$Node.compareAndSetWaitStatus(java.base@9-ea/AbstractQueuedSynchronizer.java:521)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.doReleaseShared(java.base@9-ea/AbstractQueuedSynchronizer.java:727)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(java.base@9-ea/AbstractQueuedSynchronizer.java:1369)
        at java.util.concurrent.CountDownLatch.countDown(java.base@9-ea/CountDownLatch.java:292)
        at Stop.lambda$main$0(Stop.java:40)
        at java.lang.Thread.run(java.base@9-ea/Thread.java:843)
Caused by: java.lang.ThreadDeath
        at java.lang.Thread.stop(java.base@9-ea/Thread.java:948)
        at java.lang.ThreadGroup.stopOrSuspend(java.base@9-ea/ThreadGroup.java:698)
        at java.lang.ThreadGroup.stop(java.base@9-ea/ThreadGroup.java:610)
        at Stop.lambda$main$1(Stop.java:56)
        ... 1 more
Comments
Replace the lambda expression with a string concatenation expression (which uses indy concat) and similar behaviour can be observed.
24-08-2016

Just "lucky" in the sense that no indies are being used. It's easy to reproduce (far easier that the original failure) when modifying the first thread by adding a lambda after the latch count down: final Thread second = new Thread(group, () -> { ready.countDown(); final Runnable r = () -> { System.out.println("RUN"); }; r.run(); Exception in thread "Thread-0" java.lang.BootstrapMethodError: call site initialization exception at java.lang.invoke.CallSite.makeSite(java.base/CallSite.java:347) at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(java.base/MethodHandleNatives.java:250) at java.lang.invoke.MethodHandleNatives.linkCallSite(java.base/MethodHandleNatives.java:240) at Stop.lambda$test$1(Stop.java:15) at java.lang.Thread.run(java.base/Thread.java:843) Caused by: java.lang.ThreadDeath at java.lang.Thread.stop(java.base/Thread.java:948) at java.lang.ThreadGroup.stopOrSuspend(java.base/ThreadGroup.java:698) at java.lang.ThreadGroup.stop(java.base/ThreadGroup.java:610) at Stop.lambda$test$2(Stop.java:32) ... 1 more
22-08-2016

From JVMS: Linking Exceptions If resolution of the symbolic reference to the call site specifier throws an exception E, the invokedynamic instruction throws a BootstrapMethodError that wraps E. Otherwise, during the continuing resolution of the call site specifier, if invocation of the bootstrap method completes abruptly (��2.6.5) because of a throw of exception E, the invokedynamic instruction throws a BootstrapMethodError that wraps E. (This can occur if the bootstrap method has the wrong arity, parameter type, or return type, causing java.lang.invoke.MethodHandle . invoke to throw java.lang.invoke.WrongMethodTypeException.) Otherwise, during the continuing resolution of the call site specifier, if the result from the bootstrap method invocation is not a reference to an instance of java.lang.invoke.CallSite, the invokedynamic instruction throws a BootstrapMethodError. Otherwise, during the continuing resolution of the call site specifier, if the type descriptor of the target of the call site object is not semantically equal to the method descriptor in the call site specifier, the invokedynamic instruction throws a BootstrapMethodError. --- My opinion is that the above incorrectly covers the case of asynchronous exceptions (ThreadDeath, StackOverflowError, maybe OOME) when it should really only be concerned with synchronous exceptions that pertain specifically to the actual resolution process. That said we have not noticed this wrapping problem before so it seems that the INDY resolution process is happening well before we execute the test code - or else we have been lucky in that INDY has not been involved as no lambdas were being used? Indiscriminant wrapping of exceptions is a problem for any test of exception throwing. But it would also be very confusing to user code, or rather to the user who does not want nor expect to ever see a BootstrapMethodError when executing normal Java code.
22-08-2016

Similar behaviour will likely occur for linking of an invokedynamic instruction e.g. if a ThreadDeath is produced when linking the indy associated with a lambda expression. The code to make a call site (linking the indy by invoking the bootstrap method handle) is wrapped in the following: try { ... } catch (Throwable ex) { BootstrapMethodError bex; if (ex instanceof BootstrapMethodError) bex = (BootstrapMethodError) ex; else bex = new BootstrapMethodError("call site initialization exception", ex); throw bex; } But then there is code in src/share/vm/interpreter/linkResolver.cpp implying the certain exception pass through: static void wrap_invokedynamic_exception(TRAPS) { if (HAS_PENDING_EXCEPTION) { if (TraceMethodHandles) { tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); PENDING_EXCEPTION->print(); } if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { // throw these guys, since they are already wrapped return; } if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { // intercept only LinkageErrors which might have failed to wrap return; } // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. Handle nested_exception(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception) } }
19-08-2016

Yes you are right - looking at all the LinkageError subclasses most refer to being thrown by the Java Virtual Machine - except for ExceptionInInitializerError and IncompatibleClassChangeError, and BootstrapMethodError (though I suppose this isn't really thrown by the VM anyway). As for the public constructors ...
11-08-2016

"""An ExceptionInInitializerError is thrown to indicate that an exception occurred during evaluation of a static initializer or the initializer for a static variable.""" This seems unclear to me. Thrown by whom? Perhaps the static initializer block is supposed to do a try/catch/wrap in ExceptionInInitializerError? Does it get thrown when class initialization is first attempted, or later, when the "broken" class is accessed? If only the VM is supposed to throw the exception, why does it have a public constructor? Changing s/is thrown/is thrown by the virtual machine/ would be one improvement in clarity.
11-08-2016

Of course the other option, which I was sure we already used extensively is just to unwrap the cause of the ReflectiveOperationException and rethrow it directly if it is an Error or RuntimeException, else rewrap in " new Error("unexpected checked exception",cause)
11-08-2016

public class ExceptionInInitializerError extends LinkageError Signals that an unexpected exception has occurred in a static initializer. An ExceptionInInitializerError is thrown to indicate that an exception occurred during evaluation of a static initializer or the initializer for a static variable. --- Seems fairly clear to me, particularly is it is a LinkageError: public class LinkageError extends Error Subclasses of LinkageError indicate that a class has some dependency on another class; however, the latter class has incompatibly changed after the compilation of the former class. --- These are not exceptions user code explicitly throws. In the j.u.c cases the ReflectiveOperationException has to be caught but of course it should never actually be possible in released code. In such circumstances I prefer to use InternalError over Error, but the former is a VirtualMachineError so not really appropriate in this case.
11-08-2016

I think I agree that java code should never throw ExceptionInInitializerError, but the javadoc should say that, and currently does not make it clear. Perhaps code that catches ReflectiveOperationException should rethrow with a more specific error than Error, perhaps LinkageError? j.u.c. code should be less tolerant of exceptions during initialization than it currently is, e.g. in ForkJoinPool initialization.
11-08-2016

@Martin: you should never explicitly throw ExceptionInInitializerError, it is thrown by the runtime in response to an uncaught exception in a static initializer. As ReflectiveOperationException is checked you must catch it and convert to an unchecked exception type - so Error is fine. And it will then be wrapped in an ExceptionInInitializerError.
10-08-2016

Unless mandated by a spec (such as the case for ExceptionInInitializerError) I think general Errors should be passed through. But it does get very subjective depending on the exact API involved. The current code creates havoc for any test that tries to provoke explicit exceptions (OOME, StackOverflow, ThreadDeath) but will instead get a LinkageError if it happens to trigger while the j.l.invoke code is on the stack (which is now quite likely due to the j.u.c changes). @Paul: the default uncaughtExceptionHandler ignores ThreadDeath (ie it is considred a legitimate means for a thread to terminate and not an exception that has to be reported).
10-08-2016

In j.u.c. init code we convert checked exceptions like this: } catch (ReflectiveOperationException e) { throw new Error(e); but maybe we should be throwing LinkageError or ExceptionInInitializerError
10-08-2016

David, perhaps we should just re-throw all errors for MethodHandle and VarHandle linkage failure? or perhaps those for LinkageError, VirtualMachineError and ThreadDeath? I am guessing the propagation of ThreadDeath will not result in the test failing?
10-08-2016

I'm of one-mind now - see also JDK-8163294. I don't think this code should be wrapping everything in LinkageErrors! ThreadDeath, StackOverflowError, OutOfMemoryError should all be passed through without wrapping.
10-08-2016

Martin/Doug: yet more fallout from the recent j.u.c changes. I'm off two minds whether ThreadDeath should be special-cased - opinions?
10-08-2016

The j.u.c utilities are now using MethodHandles/Varhandles in their implementation. The ThreadDeath is hitting the target thread whilst in the j.l.invoke code. That code has: static MemberName linkMethodImpl(Class<?> callerClass, int refKind, Class<?> defc, String name, Object type, Object[] appendixResult) { try { if (refKind == REF_invokeVirtual) { if (defc == MethodHandle.class) { return Invokers.methodHandleInvokeLinkerMethod( name, fixMethodType(callerClass, type), appendixResult); } else if (defc == VarHandle.class) { return varHandleOperationLinkerMethod( name, fixMethodType(callerClass, type), appendixResult); } } } catch (Throwable ex) { if (ex instanceof LinkageError) throw (LinkageError) ex; else throw new LinkageError(ex.getMessage(), ex); } throw new LinkageError("no such method "+defc.getName()+"."+name+type); } it catches all Errors and RuntimeExceptions and wraps them in a LinkageError. That's not appropriate for ThreadDeath.
10-08-2016

I see JDK-8163294 also reported java.lang.LinkageError, and may related some changes in 9/b129. Is this issue dup of JDK-8163294? Please evaluate.
10-08-2016