From b852aaed88a161018f7b8df89009490f54091a80 Mon Sep 17 00:00:00 2001 From: joeyleeeeeee97 Date: Wed, 20 Jan 2021 10:06:36 +0800 Subject: [PATCH] [Wisp] Support preempt in interpreter Summary: Preempt check could be left out when thread fall into interpreter or unhandled thread_state, this patch added check in interpreter and cleared preempted flag when needed. Test Plan: com/alibaba/wisp/thread/PreemptTest.java Reviewed-by: leiyu, zhengxiaolinX Issue: https://github.com/alibaba/dragonwell8/issues/204 --- .../vm/interpreter/interpreterRuntime.cpp | 3 + .../recorder/checkpoint/jfrCheckpointBlob.cpp | 93 ++++++++++++++ .../recorder/checkpoint/jfrCheckpointBlob.hpp | 59 +++++++++ .../checkpoint/types/jfrTypeSetWriter.hpp | 108 ++++++++++++++++ .../jfr/recorder/repository/jfrChunkState.cpp | 119 ++++++++++++++++++ .../jfr/recorder/repository/jfrChunkState.hpp | 60 +++++++++ src/share/vm/prims/unsafe.cpp | 11 +- src/share/vm/runtime/coroutine.cpp | 80 ++++++++---- src/share/vm/runtime/coroutine.hpp | 1 + src/share/vm/runtime/safepoint.cpp | 7 +- src/share/vm/runtime/safepoint.hpp | 2 + 11 files changed, 507 insertions(+), 36 deletions(-) create mode 100644 src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp create mode 100644 src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp create mode 100644 src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp create mode 100644 src/share/vm/jfr/recorder/repository/jfrChunkState.cpp create mode 100644 src/share/vm/jfr/recorder/repository/jfrChunkState.hpp diff --git a/src/share/vm/interpreter/interpreterRuntime.cpp b/src/share/vm/interpreter/interpreterRuntime.cpp index 8cacdfad6..6afdb307b 100644 --- a/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/src/share/vm/interpreter/interpreterRuntime.cpp @@ -1062,6 +1062,9 @@ IRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* thread)) // then we may have JVMTI work to do. JvmtiExport::at_single_stepping_point(thread, method(thread), bcp(thread)); } + if (EnableCoroutine) { + Coroutine::after_safepoint(thread); + } IRT_END IRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread *thread, oopDesc* obj, diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp new file mode 100644 index 000000000..aaf3352f3 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" + +JfrCheckpointBlob::JfrCheckpointBlob(const u1* checkpoint, size_t size) : + _checkpoint(JfrCHeapObj::new_array(size)), + _size(size), + _next(), + _written(false) { + assert(checkpoint != NULL, "invariant"); + assert(_checkpoint != NULL, "invariant"); + memcpy(const_cast(_checkpoint), checkpoint, size); +} + +JfrCheckpointBlob::~JfrCheckpointBlob() { + JfrCHeapObj::free(const_cast(_checkpoint), _size); +} + +const JfrCheckpointBlobHandle& JfrCheckpointBlob::next() const { + return _next; +} + +void JfrCheckpointBlob::write_this(JfrCheckpointWriter& writer) const { + writer.bytes(_checkpoint, _size); +} + +void JfrCheckpointBlob::exclusive_write(JfrCheckpointWriter& writer) const { + if (!_written) { + write_this(writer); + _written = true; + } + if (_next.valid()) { + _next->exclusive_write(writer); + } +} + +void JfrCheckpointBlob::write(JfrCheckpointWriter& writer) const { + write_this(writer); + if (_next.valid()) { + _next->write(writer); + } +} + +void JfrCheckpointBlob::reset_write_state() const { + if (_written) { + _written = false; + } + if (_next.valid()) { + _next->reset_write_state(); + } +} + +void JfrCheckpointBlob::set_next(const JfrCheckpointBlobHandle& ref) { + if (_next == ref) { + return; + } + assert(_next != ref, "invariant"); + if (_next.valid()) { + _next->set_next(ref); + return; + } + _next = ref; +} + +JfrCheckpointBlobHandle JfrCheckpointBlob::make(const u1* checkpoint, size_t size) { + const JfrCheckpointBlob* cp_blob = new JfrCheckpointBlob(checkpoint, size); + assert(cp_blob != NULL, "invariant"); + return JfrCheckpointBlobReference::make(cp_blob); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp new file mode 100644 index 000000000..50617ecb6 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP + +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrRefCountPointer.hpp" + +class JfrCheckpointBlob; +class JfrCheckpointWriter; + +typedef RefCountPointer JfrCheckpointBlobReference; +typedef RefCountHandle JfrCheckpointBlobHandle; + +class JfrCheckpointBlob : public JfrCHeapObj { + template + friend class RefCountPointer; + private: + const u1* _checkpoint; + const size_t _size; + JfrCheckpointBlobHandle _next; + mutable bool _written; + + JfrCheckpointBlob(const u1* checkpoint, size_t size); + ~JfrCheckpointBlob(); + const JfrCheckpointBlobHandle& next() const; + void write_this(JfrCheckpointWriter& writer) const; + + public: + void write(JfrCheckpointWriter& writer) const; + void exclusive_write(JfrCheckpointWriter& writer) const; + void reset_write_state() const; + void set_next(const JfrCheckpointBlobHandle& ref); + static JfrCheckpointBlobHandle make(const u1* checkpoint, size_t size); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp new file mode 100644 index 000000000..e18a56752 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP +#define SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP + +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "memory/allocation.hpp" + +template +class JfrArtifactWriterHost : public StackObj { + private: + WriterImpl _impl; + JfrCheckpointWriter* _writer; + JfrCheckpointContext _ctx; + jlong _count_offset; + int _count; + bool _skip_header; + public: + JfrArtifactWriterHost(JfrCheckpointWriter* writer, + JfrArtifactSet* artifacts, + bool class_unload, + bool skip_header = false) : _impl(writer, artifacts, class_unload), + _writer(writer), + _ctx(writer->context()), + _count(0), + _skip_header(skip_header) { + assert(_writer != NULL, "invariant"); + if (!_skip_header) { + _writer->write_type((JfrTypeId)ID); + _count_offset = _writer->reserve(sizeof(u4)); // Don't know how many yet + } + } + + ~JfrArtifactWriterHost() { + if (_count == 0) { + // nothing written, restore context for rewind + _writer->set_context(_ctx); + return; + } + assert(_count > 0, "invariant"); + if (!_skip_header) { + _writer->write_count(_count, _count_offset); + } + } + + bool operator()(typename WriterImpl::Type const & value) { + this->_count += _impl(value); + return true; + } + + int count() const { return _count; } + void add(int count) { _count += count; } +}; + +typedef int(*artifact_write_operation)(JfrCheckpointWriter*, JfrArtifactSet*, const void*); + +template +class JfrArtifactWriterImplHost { + private: + JfrCheckpointWriter* _writer; + JfrArtifactSet* _artifacts; + bool _class_unload; + public: + typedef T Type; + JfrArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) : + _writer(writer), _artifacts(artifacts), _class_unload(class_unload) {} + int operator()(T const& value) { + return op(this->_writer, this->_artifacts, value); + } +}; + +template +class JfrPredicatedArtifactWriterImplHost : public JfrArtifactWriterImplHost { + private: + Predicate _predicate; + typedef JfrArtifactWriterImplHost Parent; + public: + JfrPredicatedArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) : + Parent(writer, artifacts, class_unload), _predicate(class_unload) {} + int operator()(T const& value) { + return _predicate(value) ? Parent::operator()(value) : 0; + } +}; + +#endif // SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPESETWRITER_HPP diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp b/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp new file mode 100644 index 000000000..1ae0888f7 --- /dev/null +++ b/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/dcmd/jfrDcmds.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/repository/jfrChunkState.hpp" +#include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTimeConverter.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" + +JfrChunkState::JfrChunkState() : + _path(NULL), + _start_ticks(0), + _start_nanos(0), + _previous_start_ticks(0), + _previous_start_nanos(0), + _previous_checkpoint_offset(0) {} + +JfrChunkState::~JfrChunkState() { + reset(); +} + +void JfrChunkState::reset() { + if (_path != NULL) { + JfrCHeapObj::free(_path, strlen(_path) + 1); + _path = NULL; + } + set_previous_checkpoint_offset(0); +} + +void JfrChunkState::set_previous_checkpoint_offset(jlong offset) { + _previous_checkpoint_offset = offset; +} + +jlong JfrChunkState::previous_checkpoint_offset() const { + return _previous_checkpoint_offset; +} + +jlong JfrChunkState::previous_start_ticks() const { + return _previous_start_ticks; +} + +jlong JfrChunkState::previous_start_nanos() const { + return _previous_start_nanos; +} + +void JfrChunkState::update_start_ticks() { + _start_ticks = JfrTicks::now(); +} + +void JfrChunkState::update_start_nanos() { + _start_nanos = (jlong)(os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC); +} + +void JfrChunkState::save_current_and_update_start_ticks() { + _previous_start_ticks = _start_ticks; + update_start_ticks(); +} + +void JfrChunkState::save_current_and_update_start_nanos() { + _previous_start_nanos = _start_nanos; + update_start_nanos(); +} + +void JfrChunkState::update_time_to_now() { + save_current_and_update_start_nanos(); + save_current_and_update_start_ticks(); +} + +jlong JfrChunkState::last_chunk_duration() const { + return _start_nanos - _previous_start_nanos; +} + +static char* copy_path(const char* path) { + assert(path != NULL, "invariant"); + const size_t path_len = strlen(path); + char* new_path = JfrCHeapObj::new_array(path_len + 1); + strncpy(new_path, path, path_len + 1); + return new_path; +} + +void JfrChunkState::set_path(const char* path) { + assert(JfrStream_lock->owned_by_self(), "invariant"); + if (_path != NULL) { + JfrCHeapObj::free(_path, strlen(_path) + 1); + _path = NULL; + } + if (path != NULL) { + _path = copy_path(path); + } +} + +const char* JfrChunkState::path() const { + return _path; +} diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkState.hpp b/src/share/vm/jfr/recorder/repository/jfrChunkState.hpp new file mode 100644 index 000000000..6912e0500 --- /dev/null +++ b/src/share/vm/jfr/recorder/repository/jfrChunkState.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP +#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP + +#include "jni.h" +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrTypes.hpp" + +class JfrChunkState : public JfrCHeapObj { + friend class JfrChunkWriter; + private: + char* _path; + jlong _start_ticks; + jlong _start_nanos; + jlong _previous_start_ticks; + jlong _previous_start_nanos; + jlong _previous_checkpoint_offset; + + void update_start_ticks(); + void update_start_nanos(); + void save_current_and_update_start_ticks(); + void save_current_and_update_start_nanos(); + + JfrChunkState(); + ~JfrChunkState(); + void reset(); + jlong previous_checkpoint_offset() const; + void set_previous_checkpoint_offset(jlong offset); + jlong previous_start_ticks() const; + jlong previous_start_nanos() const; + jlong last_chunk_duration() const; + void update_time_to_now(); + void set_path(const char* path); + const char* path() const; +}; + +#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP diff --git a/src/share/vm/prims/unsafe.cpp b/src/share/vm/prims/unsafe.cpp index 80c172290..03830afd7 100644 --- a/src/share/vm/prims/unsafe.cpp +++ b/src/share/vm/prims/unsafe.cpp @@ -1481,7 +1481,7 @@ JVM_ENTRY(jboolean, CoroutineSupport_stealCoroutine(JNIEnv* env, jclass klass, j // The lock will also block coroutine switch operation, // so we must finish the steal operation as soon as possible. Coroutine* coro = (Coroutine*) coroPtr; - if (coro == NULL || coro->enable_steal_count() != coro->java_call_counter()) { + if (coro == NULL || coro->enable_steal_count() != coro->java_call_counter()|| coro->is_yielding()) { return false; // an Exception throws and the coroutine being stealed is exited } assert(coro->thread() != thread, "steal from self"); @@ -1503,14 +1503,11 @@ JVM_ENTRY(jboolean, CoroutineSupport_stealCoroutine(JNIEnv* env, jclass klass, j return true; JVM_END -JVM_ENTRY (void, CoroutineSupport_checkAndThrowException0(JNIEnv* env, jclass klass, jlong coroPtr)) +JVM_ENTRY (jboolean, CoroutineSupport_shouldThrowException0(JNIEnv* env, jclass klass, jlong coroPtr)) assert(EnableCoroutine, "pre-condition"); Coroutine* coro = (Coroutine*)coroPtr; assert(coro == thread->current_coroutine(), "coroutine is current"); - if (!coro->is_yielding() && coro->clinit_call_count() == 0) { - ThreadToNativeFromVM ttnfv(thread); - throw_new(env, "ThreadDeath"); - } + return !coro->is_yielding() && coro->clinit_call_count() == 0; JVM_END /// JVM_RegisterUnsafeMethods @@ -1869,7 +1866,7 @@ JNINativeMethod coroutine_support_methods[] = { {CC"getNextCoroutine", CC"(J)"COR, FN_PTR(CoroutineSupport_getNextCoroutine)}, {CC"moveCoroutine", CC"(JJ)V", FN_PTR(CoroutineSupport_moveCoroutine)}, {CC"markThreadCoroutine", CC"(J"COBA")V", FN_PTR(CoroutineSupport_markThreadCoroutine)}, - {CC"checkAndThrowException0", CC"(J)V", FN_PTR(CoroutineSupport_checkAndThrowException0)}, + {CC"shouldThrowException0", CC"(J)Z", FN_PTR(CoroutineSupport_shouldThrowException0)}, }; #define COMPILE_CORO_METHODS_BEFORE (3) diff --git a/src/share/vm/runtime/coroutine.cpp b/src/share/vm/runtime/coroutine.cpp index 3616cd482..ba6a2bbdc 100644 --- a/src/share/vm/runtime/coroutine.cpp +++ b/src/share/vm/runtime/coroutine.cpp @@ -559,6 +559,7 @@ void Coroutine::print_stack_on(outputStream* st) { bool WispThread::_wisp_booted = false; Method* WispThread::parkMethod = NULL; Method* WispThread::unparkMethod = NULL; +Method* WispThread::yieldMethod = NULL; Method* WispThread::runOutsideWispMethod = NULL; GrowableArray* WispThread::_proxy_unpark = NULL; @@ -567,6 +568,7 @@ void WispThread::set_wisp_booted(Thread* thread) { // The flow should be changed. CallInfo callinfo; KlassHandle kh = KlassHandle(thread, SystemDictionary::com_alibaba_wisp_engine_WispTask_klass()); + KlassHandle tkh = KlassHandle(thread, SystemDictionary::Thread_klass()); LinkResolver::resolve_static_call(callinfo, kh, vmSymbols::park_name(), vmSymbols::long_void_signature(), KlassHandle(), false, true, thread); methodHandle method = callinfo.selected_method(); @@ -579,6 +581,12 @@ void WispThread::set_wisp_booted(Thread* thread) { assert(method.not_null(), "should have thrown exception"); unparkMethod = method(); + LinkResolver::resolve_static_call(callinfo, tkh, + vmSymbols::yield_name(), vmSymbols::void_method_signature(), KlassHandle(), false, true, thread); + method = callinfo.selected_method(); + assert(method.not_null(), "should have thrown exception"); + yieldMethod = method(); + LinkResolver::resolve_static_call(callinfo, kh, vmSymbols::runOutsideWisp_name(), vmSymbols::runnable_void_signature(), KlassHandle(), false, true, thread); method = callinfo.selected_method(); @@ -919,18 +927,39 @@ const char* WispThread::print_blocking_status(int status) { void Coroutine::after_safepoint(JavaThread* thread) { assert(Thread::current() == thread, "sanity check"); - guarantee(thread->safepoint_state()->is_running(), "safepoint should finish"); + + //The only two entries are: + //1. SafepointSynchronize::handle_polling_page_exception(at_safepoint) + //2. InterpreterRuntime::at_safepoint(at_call_back) + // call_back state would block in JavaCall -> ThreadState::trans + // current yielding call triggers once safepoint ends. + if (thread->safepoint_state()->is_at_safepoint()) { + return; + } + // In InterpreterRuntime::at_safepoint: thread state is _thread_in_vm now. + // So VM Thread could make the safepoint state of current thread `running -> call_back` + // We should assert at first _running and then _call_back here. + assert(thread->safepoint_state()->is_running() || + thread->safepoint_state()->is_at_call_back(), "illegal safepoint state"); Coroutine* coroutine = thread->current_coroutine(); - if (thread->thread_state() != _thread_in_Java || - // indicates we're inside compiled code or interpreter. - // rather than thread state transition. - coroutine->_is_yielding || !thread->wisp_preempted() || - thread->has_pending_exception() || thread->has_async_condition() || - coroutine->in_critical(thread)) { + if (coroutine->_is_yielding || !thread->wisp_preempted()) { return; } - + // filter unsupported state + JavaThreadState origin_thread_state = thread->thread_state(); + if ((origin_thread_state != _thread_in_Java && origin_thread_state != _thread_in_vm) + || thread->has_pending_exception() || thread->has_async_condition()) { + // clear preempted for next preempt + thread->set_wisp_preempted(false); + return; + } + // prevent preempting wisp internal + if (coroutine->in_critical(thread)) { + return; + } + // preempt only triggered by SafepointSynchronize::handle_polling_page_exception and + // InterpreterRuntime::at_safepoint. oop wisp_task = thread->current_coroutine()->_wisp_task; if (wisp_task != NULL) { // expose to perfCount and jstack int cnt = com_alibaba_wisp_engine_WispTask::get_preemptCount(wisp_task); @@ -947,27 +976,26 @@ void Coroutine::after_safepoint(JavaThread* thread) { // - The preempt mechanism should be enabled during "other" coroutines are executing thread->set_wisp_preempted(false); - ThreadInVMfromJava tiv(thread); + if (origin_thread_state == _thread_in_Java) { + ThreadStateTransition::transition_from_java(thread, _thread_in_vm); + } + assert(thread->thread_state() == _thread_in_vm, "illegal thread state"); JavaValue result(T_VOID); JavaCallArguments args; - JavaCalls::call_static(&result, - KlassHandle(thread, SystemDictionary::Thread_klass()), - vmSymbols::yield_name(), - vmSymbols::void_method_signature(), - &args, - thread); - coroutine->_is_yielding = false; - - if (thread->has_pending_exception() - && (thread->pending_exception()->klass() == SystemDictionary::OutOfMemoryError_klass() - || thread->pending_exception()->klass() == SystemDictionary::StackOverflowError_klass())) { - // throw expected vm error - return; + JavaCalls::call(&result, methodHandle(WispThread::yieldMethod), &args, thread); + if (origin_thread_state == _thread_in_Java) { + ThreadStateTransition::transition(thread, _thread_in_vm, _thread_in_Java); } + assert(thread->thread_state() == origin_thread_state, "illegal thread state"); + coroutine->_is_yielding = false; - if (thread->has_pending_exception() || thread->has_async_condition()) { - guarantee(thread->pending_exception()->klass() == SystemDictionary::ThreadDeath_klass(), - "thread_death expected"); + if (thread->has_pending_exception()) { + guarantee(thread->pending_exception()->klass() == SystemDictionary::OutOfMemoryError_klass() || + thread->pending_exception()->klass() == SystemDictionary::StackOverflowError_klass() || + thread->pending_exception()->klass() == SystemDictionary::ThreadDeath_klass(), + "Only SOF/OOM/ThreadDeath/TenantDeath happens here"); + // If it's a SOF / OOM / ThreadDeath / TenantDeath exception, we'd clear it + // because polling page stub shouldn't have a pending exception. thread->clear_pending_exception(); } } @@ -1216,4 +1244,4 @@ bool clear_interrupt_for_wisp(Thread* thread) { thread->osthread()->set_interrupted(false); return interrupted; -} \ No newline at end of file +} diff --git a/src/share/vm/runtime/coroutine.hpp b/src/share/vm/runtime/coroutine.hpp index 5c592f3d7..081b2e9a8 100644 --- a/src/share/vm/runtime/coroutine.hpp +++ b/src/share/vm/runtime/coroutine.hpp @@ -346,6 +346,7 @@ class WispThread: public JavaThread { friend class Coroutine; private: static bool _wisp_booted; + static Method* yieldMethod; static Method* parkMethod; static Method* unparkMethod; static Method* runOutsideWispMethod; diff --git a/src/share/vm/runtime/safepoint.cpp b/src/share/vm/runtime/safepoint.cpp index 96a2f052d..6b12a8997 100644 --- a/src/share/vm/runtime/safepoint.cpp +++ b/src/share/vm/runtime/safepoint.cpp @@ -1166,6 +1166,10 @@ void ThreadSafepointState::handle_polling_page_exception() { SafepointSynchronize::block(thread()); set_at_poll_safepoint(false); + if (EnableCoroutine) { + Coroutine::after_safepoint(thread()); + } + // If we have a pending async exception deoptimize the frame // as otherwise we may never deliver it. if (thread()->has_async_condition()) { @@ -1190,9 +1194,6 @@ void ThreadSafepointState::handle_polling_page_exception() { fatal("Exception installed and deoptimization is pending"); } } - if (EnableCoroutine) { - Coroutine::after_safepoint(thread()); - } } } diff --git a/src/share/vm/runtime/safepoint.hpp b/src/share/vm/runtime/safepoint.hpp index d72b19817..35a138bf6 100644 --- a/src/share/vm/runtime/safepoint.hpp +++ b/src/share/vm/runtime/safepoint.hpp @@ -231,6 +231,8 @@ class ThreadSafepointState: public CHeapObj { JavaThread* thread() const { return _thread; } suspend_type type() const { return _type; } bool is_running() const { return (_type==_running); } + bool is_at_safepoint() const{ return (_type == _at_safepoint);} + bool is_at_call_back() const{ return (_type == _call_back);} JavaThreadState orig_thread_state() const { return _orig_thread_state; } // Support for safepoint timeout (debugging)