From a6a2ff1b0e37c54563ce80807eb7a246400eca67 Mon Sep 17 00:00:00 2001 From: Zhai Mo Date: Wed, 30 Aug 2023 11:37:11 +0800 Subject: [PATCH] [Wisp] Fix dead loop when killing threads waiting for objectmonitor. Summary: Threads waiting for objectmonitor will skip calling WispTask::park when async exception is pending, so mannualy clear exception before WispThread::park Test Plan: jtreg test/jdk/com/alibaba/rcm/TestDeadLoopKillObjectMonitor.java Reviewed-by: yulei Issue: https://github.com/dragonwell-project/dragonwell17/issues/146 --- src/hotspot/share/runtime/coroutine.cpp | 17 +++++ src/hotspot/share/runtime/thread.cpp | 15 ++++ src/hotspot/share/runtime/thread.hpp | 3 + .../rcm/TestDeadLoopKillObjectMonitor.java | 74 +++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 test/jdk/com/alibaba/rcm/TestDeadLoopKillObjectMonitor.java diff --git a/src/hotspot/share/runtime/coroutine.cpp b/src/hotspot/share/runtime/coroutine.cpp index 839fce9919a..8b60f5ce1f5 100644 --- a/src/hotspot/share/runtime/coroutine.cpp +++ b/src/hotspot/share/runtime/coroutine.cpp @@ -735,6 +735,15 @@ void WispThread::park(long millis, const ObjectWaiter* ow) { JavaThread* jt = ((WispThread*) ow->_thread)->thread(); assert(jt == Thread::current(), "current"); + assert (!jt->has_pending_exception(), "should not have any pending exception"); + if (jt->has_async_exception_condition()) { + assert(UseWisp2 && Wisp2ThreadStop, "pre-condition"); + assert(jt->has_aync_thread_death_exception(), "must be wisp_thread_death_exception"); + // JavaCalls will be skipped if current thread has a pending exception + // mannually clear all async exceptions before calling into java + jt->clear_aync_thread_death_exception(); + } + if (ow->_timeout > 0) { millis = ow->_timeout; assert(!ow->_using_wisp_park, "invariant"); @@ -821,6 +830,14 @@ void WispThread::unpark(int task_id, bool using_wisp_park, bool proxy_unpark, Pa return; } + if (jt->has_async_exception_condition()) { + assert(UseWisp2 && Wisp2ThreadStop, "pre-condition"); + assert(jt->has_aync_thread_death_exception(), "must be wisp_thread_death_exception"); + // JavaCalls will be skipped if current thread has a pending exception + // mannually clear all async exceptions before calling into java + jt->clear_aync_thread_death_exception(); + } + wisp_thread->_unpark_status = WispThread::_wisp_unpark_begin; JavaValue result(T_VOID); JavaCallArguments args; diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 0a168e29c00..dfb14ca1a22 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1652,6 +1652,21 @@ JavaThread* JavaThread::active() { } } +bool JavaThread::has_aync_thread_death_exception() { + assert(EnableCoroutine && Wisp2ThreadStop, "pre-condition"); + return _pending_async_exception == Universe::wisp_thread_death_exception(); +} + +void JavaThread::clear_aync_thread_death_exception() { + assert(UseWisp2 && Wisp2ThreadStop, "pre-condition"); + if (_pending_async_exception != NULL + && _pending_async_exception == Universe::wisp_thread_death_exception()) { + _pending_async_exception = NULL; + set_async_exception_condition(_no_async_condition); + clear_suspend_flag(_has_async_exception); + } +} + bool JavaThread::is_lock_owned(address adr) const { if (Thread::is_lock_owned(adr)) return true; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 7cf89bded06..db51d5f261c 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1643,6 +1643,9 @@ class JavaThread: public Thread { bool has_attached_via_jni() const { return is_attaching_via_jni() || _jni_attach_state == _attached_via_jni; } inline void set_done_attaching_via_jni(); + bool has_aync_thread_death_exception(); + void clear_aync_thread_death_exception(); + // Stack dump assistance: // Track the class we want to initialize but for which we have to wait // on its init_lock() because it is already being initialized. diff --git a/test/jdk/com/alibaba/rcm/TestDeadLoopKillObjectMonitor.java b/test/jdk/com/alibaba/rcm/TestDeadLoopKillObjectMonitor.java new file mode 100644 index 00000000000..f26d76aacbf --- /dev/null +++ b/test/jdk/com/alibaba/rcm/TestDeadLoopKillObjectMonitor.java @@ -0,0 +1,74 @@ +/* + * @test + * @library /test/lib + * @build TestDeadLoopKillObjectMonitor RcmUtils + * @summary test RCM TestKillThreads + * @modules java.base/com.alibaba.wisp.engine:+open + * @modules java.base/com.alibaba.rcm.internal:+open + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseWisp2 -XX:+Wisp2ThreadStop -XX:ActiveProcessorCount=1 TestDeadLoopKillObjectMonitor + */ + +import com.alibaba.rcm.Constraint; +import com.alibaba.rcm.ResourceContainer; +import com.alibaba.rcm.ResourceType; +import com.alibaba.rcm.internal.RCMUnsafe; +import com.alibaba.wisp.engine.WispResourceContainerFactory; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +public class TestDeadLoopKillObjectMonitor { + + private static Object obj = new Object(); + + public static void main(String[] args) throws Exception { + ResourceContainer container = RcmUtils.createContainer(Collections.singletonList( + ResourceType.CPU_PERCENT.newConstraint(100))); + + CountDownLatch latch = new CountDownLatch(1); + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + synchronized (obj) { + try { + Thread.sleep(1000); + Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + latch.countDown(); + } + }); + t2.setDaemon(true); + t2.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }); + t1.setDaemon(true); + t1.start(); + + Thread.sleep(100); + container.run(() -> { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + while (true) + synchronized (obj) { + System.out.println("rr"); + } + } + }); + t.start(); + }); + + Thread.sleep(100); + RCMUnsafe.killThreads(container); + } + +}