JDK-4998314 : compute_compiled_exc_handler() called with pending exception
Type:Bug
Component:hotspot
Sub-Component:compiler
Affected Version:1.4.2_02
Priority:P3
Status:Closed
Resolution:Fixed
OS:generic
CPU:generic
Submitted:2004-02-20
Updated:2020-01-02
Resolved:2004-04-06
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.
See comments section; RE might decide this belongs in compiler2
if code is specific to c2 (opto/runtime). Filing under hotspot/runtime_system
for initial evaluation.
SUGGESTED FIX
###@###.### 2004-03-31
Cleanup pending exceptions after exit from VM (after return from
handle_exception_C_helper()): overwrite a processed exception
(if it is not ThreadDeath) with a pending exception and process it
(call handle_exception_C_helper() again).
http://analemma.sfbay.sun.com/net/prt-archiver.sfbay/export2/archived_workspaces/main/c2_baseline/2004/20040329193754.kvn.4895131/workspace/webrevs/webrev-2004.03.29/index.html
src/share/vm/opto/runtime.cpp
***************
*** 1046,1057 ****
debug_only(NoHandleMark __hm;)
nmethod* nm = NULL;
address handler_address = NULL;
! {
// Enter the VM
ResetNoHandleMark rnhm;
handler_address = handle_exception_C_helper(thread, nm);
! }
// Back in java: Use no oops, DON'T safepoint
// Now check to see if the handler we are returning is in a now
--- 1046,1070 ----
debug_only(NoHandleMark __hm;)
nmethod* nm = NULL;
address handler_address = NULL;
! bool overwrite_exception = false;
! do {
// Enter the VM
ResetNoHandleMark rnhm;
handler_address = handle_exception_C_helper(thread, nm);
! overwrite_exception = false;
! if (thread->has_pending_exception()) {
! // A pending exception could be created during exit from VM (4998314).
! // If a processed exception is not ThreadDeath overwrite it with
! // the pending exception and process it.
! if (!thread->exception_oop()->is_a(SystemDictionary::threaddeath_klass())) {
! thread->set_exception_oop(thread->pending_exception());
! overwrite_exception = true;
! }
! thread->clear_pending_exception(); // Cleanup a pending exception.
! }
! } while (overwrite_exception);
!
// Back in java: Use no oops, DON'T safepoint
// Now check to see if the handler we are returning is in a now
08-07-2004
EVALUATION
From looking at this in the SA I believe the what occurs is this. A thread is has the following call stack
java/net/SocketInputStream.socketRead0
java/net/SocketInputStream.read([BII)I
IE/Iona/Orbix2/CORBA/SocketConnection
IE/Iona/Orbix2/CORBA/ClientConnection.run()
java/lang/Thread.run()
at least the top 2 frames are compiled. socketRead0 is an native method, though I don't think that's required for this bug to occur. socketRead0 returns with an exception, java.net.SocketException: Socket closed. This is the oop at 0xd708bc38 in the core file. The compiled native wrapper dispatches the exception correctly and eventually calls into the ExceptionBlob which calls OptoRuntime::handle_exception_C which is a JRT_ENTRY, meaning it can safepoint on it's way out of the VM. The exception lookup code decides that it should return to the exception handler of the called compiled frame, read. On the way out of the VM a safepoint occurs and Thread.stop() is called in another thread on this thread. This installs an instance of java/lang/ThreadDeath in the pending async exception field. This is oop 0xd7087d98 in the core file. Part of the logic in JavaThread::send_thread_stop is to deoptimize the caller frame under certain conditions, namely if the last frame is a runtime stub or a safepoint blob. In this case the last frame is the exception blob which is neither of these. So the ThreaDeath get's installed in pending exception and we return to the compiled Java code. C2's exception dispatch path keeps the current exception in the thread's exception_oop field. So know the thread has both an exception the exception_oop and in the pending_exception field. When it reenters the exception blob from SocketInputStream.read it calls compute_compiled_exc_handler which eventually dies when it installs the ExceptionMark because the pending exception is set. So the straightforward fix is to add the exception blob to things that cause the caller frame to deopt in send_thread_stop.
###@###.### 2004-03-02
###@###.### 2004-03-23
The small test (in the comments) confirms this evaluation.