Implement Virtual Thread and Continuation API

It makes the following API functional:
Thread.startVirtualThread(Runnable)
Thread.ofVirtual()

The following APIs should be Virtual-Thread-compatible:
Thread.sleep
LockSupport.park*
ReentrantLock

The feature is behind the virtual_thread_impl_v1 flag

Flag: com.android.art.flags.virtual_thread_impl_v1
Bug: 346542404
Test: 2394-continuation-yields
Test: 2395-virtual-thread-reentrantlock
Test: 2395-virtual-thread-sleep-api
Test: CtsLibcoreOjTestCases:java.lang.Thread.virtual
Change-Id: I15734380d27c11fb23b73890995df41f3d963dab
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 79246ca..bdf1aa7 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -376,6 +376,7 @@
         "native/java_lang_reflect_Proxy.cc",
         "native/java_util_concurrent_atomic_AtomicLong.cc",
         "native/jdk_internal_misc_Unsafe.cc",
+        "native/jdk_internal_vm_Continuation.cc",
         "native/libcore_io_Memory.cc",
         "native/libcore_util_CharsetUtils.cc",
         "native/org_apache_harmony_dalvik_ddmc_DdmServer.cc",
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 31b8986..cf6a2e6 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1376,7 +1376,15 @@
 void FillVirtualThreadFrame(Thread* self, ShadowFrame* frame) {
   ScopedAssertNoThreadSuspension ns("No thread suspension when filling virtual thread frame)");
   ObjPtr<mirror::Object> jpeer = self->GetPeer();
-  ObjPtr<mirror::Object> v_context = WellKnownClasses::java_lang_Thread_target->GetObject(jpeer);
+  ObjPtr<mirror::Object> v_context;
+  if (self->AreVirtualThreadFlagsEnabled(kContinuation)) {
+    ObjPtr<mirror::Object> cont = WellKnownClasses::java_lang_Thread_cont->GetObject(jpeer);
+    DCHECK(!cont.IsNull());
+    v_context =
+        WellKnownClasses::jdk_internal_vm_Continuation_virtualThreadContext->GetObject(cont);
+  } else {
+    v_context = WellKnownClasses::java_lang_Thread_target->GetObject(jpeer);
+  }
   DCHECK(v_context->GetClass()->DescriptorEquals("Ldalvik/system/VirtualThreadContext;"))
       << frame->GetMethod()->PrettyMethod();
   ObjPtr<mirror::Object> parked_states =
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index ea6ff4e..f129a52 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -261,6 +261,7 @@
   VirtualThreadPark(soa.Decode<mirror::Object>(v_context),
                     soa.Decode<mirror::Object>(parked_states),
                     soa.Decode<mirror::Throwable>(vm_error),
+                    false,
                     reason);
 }
 
diff --git a/runtime/native/jdk_internal_vm_Continuation.cc b/runtime/native/jdk_internal_vm_Continuation.cc
new file mode 100644
index 0000000..26a44fa
--- /dev/null
+++ b/runtime/native/jdk_internal_vm_Continuation.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jdk_internal_vm_Continuation.h"
+
+#include "art_method-inl.h"
+#include "dex/invoke_type.h"
+#include "handle_scope-inl.h"
+#include "jni.h"
+#include "mirror/object.h"
+#include "mirror/throwable.h"
+#include "native/native_util.h"
+#include "nativehelper/jni_macros.h"
+#include "obj_ptr-inl.h"
+#include "obj_ptr.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+#include "virtual_thread_common.h"
+#include "well_known_classes.h"
+
+namespace art HIDDEN {
+
+static jint Continuation_doYieldNative([[maybe_unused]] JNIEnv* env,
+                                       jobject,
+                                       jobject v_context,
+                                       jobject parked_states,
+                                       jobject vm_error) {
+  ScopedObjectAccess soa(env);
+
+  PinningReason reason = kNoReason;
+  bool success = VirtualThreadPark(soa.Decode<mirror::Object>(v_context),
+                                   soa.Decode<mirror::Object>(parked_states),
+                                   soa.Decode<mirror::Throwable>(vm_error),
+                                   true,
+                                   reason);
+
+  if (!success && reason == kNoReason) {
+    // Expect a pending exception, and return to the java level to handle it
+    DCHECK(Thread::Current()->IsExceptionPending());
+    return kNoReason;
+  }
+
+  return reason;
+}
+
+static void Continuation_enterSpecial(
+    JNIEnv* env, jobject, jobject cont, jboolean j_is_continue, jboolean j_is_virtual_thread) {
+  Thread* self = Thread::Current();
+  DCHECK(j_is_virtual_thread);
+  DCHECK(!self->AreVirtualThreadFlagsEnabled(kIsVirtual));
+
+  ScopedObjectAccess soa(env);
+  ObjPtr<mirror::Object> continuation = soa.Decode<mirror::Object>(cont);
+  ObjPtr<mirror::Object> v_context =
+      WellKnownClasses::jdk_internal_vm_Continuation_virtualThreadContext->GetObject(continuation);
+  ObjPtr<mirror::Object> parked_states =
+      WellKnownClasses::dalvik_system_VirtualThreadContext_parkedStates->GetObject(v_context);
+
+  bool is_continue = j_is_continue;
+  DCHECK_NE(parked_states.IsNull(), is_continue) << "Likely a bug in the Continuation.java";
+  if (parked_states.IsNull() == is_continue) {
+    const char* msg = is_continue ? "Can't continue without the saved stack"
+                                  : "saved stack shouldn't exist when a continuation start.";
+    self->ThrowNewException("Ljava/lang/IllegalStateException;", msg);
+    return;
+  }
+
+  uint8_t flags_mask = kIsVirtual | kContinuation | (is_continue ? kUnparking : 0);
+  self->SetVirtualThreadFlags(flags_mask, true);
+
+  WellKnownClasses::jdk_internal_vm_Continuation_enter->InvokeStatic<'V', 'L', 'Z'>(
+      self, continuation, is_continue);
+
+  // When a virtual thread is parked, clear the VirtualThreadParkingError used to
+  // unwind the native stack.
+  if (self->IsExceptionPending() &&
+      self->AreVirtualThreadFlagsEnabled(VirtualThreadFlag::kParking)) {
+    DCHECK(self->GetException()->GetClass()->DescriptorEquals(
+        "Ldalvik/system/VirtualThreadParkingError;"));
+    self->ClearException();
+  }
+
+  self->SetVirtualThreadFlags(kIsVirtual | kContinuation | kParking, false);
+}
+
+static JNINativeMethod gMethods[] = {
+    NATIVE_METHOD(
+        Continuation,
+        doYieldNative,
+        "(Ldalvik/system/VirtualThreadContext;"
+        "Ldalvik/system/VirtualThreadParkedStates;Ldalvik/system/VirtualThreadParkingError;)I"),
+    NATIVE_METHOD(Continuation, enterSpecial, "(Ljdk/internal/vm/Continuation;ZZ)V"),
+};
+
+void register_jdk_internal_vm_Continuation(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("jdk/internal/vm/Continuation");
+}
+
+}  // namespace art
diff --git a/runtime/native/jdk_internal_vm_Continuation.h b/runtime/native/jdk_internal_vm_Continuation.h
new file mode 100644
index 0000000..1c0c904
--- /dev/null
+++ b/runtime/native/jdk_internal_vm_Continuation.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+#include "base/macros.h"
+
+namespace art HIDDEN {
+
+void register_jdk_internal_vm_Continuation(JNIEnv* env);
+
+}  // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 056e3e8..de32746 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -56,10 +56,10 @@
 #include "base/aborting.h"
 #include "base/arena_allocator.h"
 #include "base/atomic.h"
+#include "base/calloc_arena_pool.h"
 #include "base/dumpable.h"
 #include "base/file_utils.h"
 #include "base/flags.h"
-#include "base/calloc_arena_pool.h"
 #include "base/mem_map_arena_pool.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
@@ -146,6 +146,7 @@
 #include "native/java_lang_reflect_Proxy.h"
 #include "native/java_util_concurrent_atomic_AtomicLong.h"
 #include "native/jdk_internal_misc_Unsafe.h"
+#include "native/jdk_internal_vm_Continuation.h"
 #include "native/libcore_io_Memory.h"
 #include "native/libcore_util_CharsetUtils.h"
 #include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
@@ -2460,6 +2461,7 @@
   register_java_lang_VMClassLoader(env);
   register_java_util_concurrent_atomic_AtomicLong(env);
   register_jdk_internal_misc_Unsafe(env);
+  register_jdk_internal_vm_Continuation(env);
   register_libcore_io_Memory(env);
   register_libcore_util_CharsetUtils(env);
   register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
diff --git a/runtime/thread.h b/runtime/thread.h
index 1669973..6e4e420 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -207,10 +207,14 @@
 enum VirtualThreadFlag : uint8_t {
   // This flag is set only when a virtual thread is running on the given carrier thread.
   kIsVirtual = 1u,
+  // This flag is set only when carrier thread enters a jdk.internal.vm.Continuation.
+  // In this case, the Continuation is the internal implementation details of virtual thread.
+  // Importantly, virtual thread frames are on top of the carrier thread frames.
+  kContinuation = 1u << 1,
   // The flag is set when a virtual thread is being parked and unmounted from the carrier thread.
-  kParking = 1u << 1,
+  kParking = 1u << 2,
   // The flag is set when a virtual thread is being unparked and mounted from the carrier thread.
-  kUnparking = 1u << 2,
+  kUnparking = 1u << 3,
 };
 
 // ART uses two types of ABI/code: quick and native.
diff --git a/runtime/virtual_thread_common.cc b/runtime/virtual_thread_common.cc
index ed5567f..228249b 100644
--- a/runtime/virtual_thread_common.cc
+++ b/runtime/virtual_thread_common.cc
@@ -41,23 +41,41 @@
 
 namespace art HIDDEN {
 
+inline static ArtMethod* get_enter_method(bool is_continuation_api) {
+  return is_continuation_api ? WellKnownClasses::jdk_internal_vm_Continuation_enterSpecial
+                             : nullptr;
+}
+
+inline static ArtMethod* get_park_method(bool is_continuation_api) {
+  if (is_continuation_api) {
+    return WellKnownClasses::jdk_internal_vm_Continuation_doYieldNative;
+  } else {
+    return WellKnownClasses::java_lang_Thread_parkVirtualInternal;
+  }
+}
+
 struct VirtualThreadParkingVisitor final : public StackVisitor {
-  explicit VirtualThreadParkingVisitor(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+  VirtualThreadParkingVisitor(Thread* thread, bool is_continuation_api)
+      REQUIRES_SHARED(Locks::mutator_lock_)
       : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames, true),
+        enter_method_(get_enter_method(is_continuation_api)),
+        park_method_(get_park_method(is_continuation_api)),
         shadow_frame_count_(0),
         reason_(kNoReason) {}
   bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
     ShadowFrame* shadow_frame = GetCurrentShadowFrame();
 
     if (shadow_frame == nullptr) {
-      // Stack walking continues only if the only non-interpreted frame is
-      // Thread.parkVirtualInternal until JIT and AOT frame is supported.
       ArtMethod** quick_frame = GetCurrentQuickFrame();
       ArtMethod* method = quick_frame != nullptr ? *quick_frame : nullptr;
       if (method != nullptr && method->IsNative()) {
-        if (method == WellKnownClasses::java_lang_Thread_parkVirtualInternal) {
-          // Continue walking for this park method.
+        if (method == park_method_) {
+          // Stack walking continues only if the only non-interpreted frame is
+          // a known method parking the virtual thread / yielding the continuation.
           return true;
+        } else if (method == enter_method_) {
+          // The rest of the stack belongs to the carrier thread.
+          return false;
         }
 
         reason_ = kNativeMethod;
@@ -93,6 +111,8 @@
     reinterpret_cast<VirtualThreadParkingVisitor*>(visitor)->reason_ = kMonitor;
   }
 
+  const ArtMethod* const enter_method_;
+  const ArtMethod* const park_method_;
   std::vector<const ShadowFrame*> shadow_frames_;
   size_t shadow_frame_count_;
   PinningReason reason_;
@@ -101,8 +121,15 @@
 bool VirtualThreadPark(ObjPtr<mirror::Object> v_context,
                        ObjPtr<mirror::Object> parked_states,
                        ObjPtr<mirror::Throwable> vm_error,
+                       bool is_continuation_api,
                        PinningReason& reason_) {
   Thread* self = Thread::Current();
+  if (self->AreVirtualThreadFlagsEnabled(kContinuation) != is_continuation_api) {
+    self->ThrowNewExceptionF("Ljava/lang/IllegalStateException;",
+                             "unmatched kContinuation value when Virtual Thread is parking: %d",
+                             is_continuation_api);
+    return false;
+  }
 
   StackHandleScope<9> hs(self);
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
@@ -112,7 +139,7 @@
   Handle<mirror::Object> opeer_h = hs.NewHandle(self->GetPeer());
   Handle<mirror::Throwable> vm_error_h = hs.NewHandle(vm_error);
 
-  VirtualThreadParkingVisitor dump_visitor(self);
+  VirtualThreadParkingVisitor dump_visitor(self, is_continuation_api);
   dump_visitor.WalkStack();
 
   DCHECK_NE(dump_visitor.reason_, kUnsupportedFrame) << "JIT / AOT frame isn't supported.";
diff --git a/runtime/virtual_thread_common.h b/runtime/virtual_thread_common.h
index 311be59..ac173b6 100644
--- a/runtime/virtual_thread_common.h
+++ b/runtime/virtual_thread_common.h
@@ -23,12 +23,16 @@
 
 namespace art HIDDEN {
 
+// LINT.IfChange
 enum PinningReason {
   kNoReason = 0,
   kNativeMethod = 3,
   kMonitor = 4,
   kUnsupportedFrame = 5,
 };
+// Update Continuation.Pinned and Continuation.pinnedReason(int)
+// This lint check doesn't work across 2 git projects until b/154647410 is fixed.
+// LINT.ThenChange(../../libcore/ojluni/src/main/java/jdk/internal/vm/Continuation.java)
 
 /**
  * @return true if parking is successful. False if the thread is pinned, or fails to park.
@@ -36,6 +40,7 @@
 bool VirtualThreadPark(ObjPtr<mirror::Object> v_context,
                        ObjPtr<mirror::Object> parked_states,
                        ObjPtr<mirror::Throwable> vm_error,
+                       bool is_continuation_api,
                        PinningReason& reason_) REQUIRES_SHARED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 1c189d4..9c3d26f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -118,6 +118,9 @@
 ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
 ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
 ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+ArtMethod* WellKnownClasses::jdk_internal_vm_Continuation_doYieldNative;
+ArtMethod* WellKnownClasses::jdk_internal_vm_Continuation_enter;
+ArtMethod* WellKnownClasses::jdk_internal_vm_Continuation_enterSpecial;
 ArtMethod* WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
 ArtMethod* WellKnownClasses::libcore_reflect_AnnotationMember_init;
 ArtMethod* WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -146,6 +149,7 @@
 ArtField* WellKnownClasses::java_lang_System_out;
 ArtField* WellKnownClasses::java_lang_System_err;
 ArtField* WellKnownClasses::java_lang_String_EMPTY;
+ArtField* WellKnownClasses::java_lang_Thread_cont;
 ArtField* WellKnownClasses::java_lang_Thread_parkBlocker;
 ArtField* WellKnownClasses::java_lang_Thread_daemon;
 ArtField* WellKnownClasses::java_lang_Thread_group;
@@ -178,6 +182,7 @@
 ArtField* WellKnownClasses::java_nio_ByteBuffer_offset;
 ArtField* WellKnownClasses::java_util_Collections_EMPTY_LIST;
 ArtField* WellKnownClasses::java_util_concurrent_ThreadLocalRandom_seeder;
+ArtField* WellKnownClasses::jdk_internal_vm_Continuation_virtualThreadContext;
 ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
 ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
 ArtField* WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
@@ -448,7 +453,7 @@
   java_lang_Long_value = CacheValueInBoxField(
       class_linker, self, "Ljava/lang/Long;", "J");
 
-  StackHandleScope<49u> hs(self);
+  StackHandleScope<50u> hs(self);
   Handle<mirror::Class> d_s_bdcl =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/BaseDexClassLoader;"));
   Handle<mirror::Class> d_s_dlcl =
@@ -531,6 +536,8 @@
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/concurrent/ThreadLocalRandom;"));
   Handle<mirror::Class> j_u_f_c =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/function/Consumer;"));
+  Handle<mirror::Class> j_i_v_cont =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljdk/internal/vm/Continuation;"));
   Handle<mirror::Class> j_i_m_fd =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljdk/internal/math/FloatingDecimal;"));
   Handle<mirror::Class> j_i_m_fd_btab = hs.NewHandle(FindSystemClass(
@@ -767,6 +774,23 @@
       pointer_size);
   jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars =
       CacheMethod(j_i_m_fd_btab.Get(), /*is_static=*/ false, "getChars", "([C)I", pointer_size);
+  jdk_internal_vm_Continuation_doYieldNative =
+      CacheMethod(j_i_v_cont.Get(),
+                  /*is_static=*/true,
+                  "doYieldNative",
+                  "(Ldalvik/system/VirtualThreadContext;Ldalvik/system/"
+                  "VirtualThreadParkedStates;Ldalvik/system/VirtualThreadParkingError;)I",
+                  pointer_size);
+  jdk_internal_vm_Continuation_enter = CacheMethod(j_i_v_cont.Get(),
+                                                   /*is_static=*/true,
+                                                   "enter",
+                                                   "(Ljdk/internal/vm/Continuation;Z)V",
+                                                   pointer_size);
+  jdk_internal_vm_Continuation_enterSpecial = CacheMethod(j_i_v_cont.Get(),
+                                                          /*is_static=*/true,
+                                                          "enterSpecial",
+                                                          "(Ljdk/internal/vm/Continuation;ZZ)V",
+                                                          pointer_size);
 
   libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(
       l_r_af.Get(),
@@ -866,6 +890,8 @@
   java_lang_System_err =
       CacheField(ToClass(java_lang_System), /*is_static=*/ true, "err", "Ljava/io/PrintStream;");
 
+  java_lang_Thread_cont =
+      CacheField(j_l_Thread.Get(), /*is_static=*/false, "cont", "Ljdk/internal/vm/Continuation;");
   java_lang_Thread_parkBlocker =
       CacheField(j_l_Thread.Get(), /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
   java_lang_Thread_daemon = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "daemon", "Z");
@@ -928,6 +954,11 @@
   java_util_concurrent_ThreadLocalRandom_seeder = CacheField(
       j_u_c_tlr.Get(), /*is_static=*/ true, "seeder", "Ljava/util/concurrent/atomic/AtomicLong;");
 
+  jdk_internal_vm_Continuation_virtualThreadContext =
+      CacheField(j_i_v_cont.Get(),
+                 /*is_static=*/false,
+                 "virtualThreadContext",
+                 "Ldalvik/system/VirtualThreadContext;");
   jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer =
       CacheField(j_i_m_fd_btab.Get(), /*is_static=*/ false, "buffer", "[C");
   jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = CacheField(
@@ -1052,6 +1083,9 @@
   jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D = nullptr;
   jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F = nullptr;
   jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars = nullptr;
+  jdk_internal_vm_Continuation_doYieldNative = nullptr;
+  jdk_internal_vm_Continuation_enter = nullptr;
+  jdk_internal_vm_Continuation_enterSpecial = nullptr;
   libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
   libcore_reflect_AnnotationMember_init = nullptr;
   org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = nullptr;
@@ -1071,6 +1105,7 @@
   java_lang_System_in = nullptr;
   java_lang_System_out = nullptr;
   java_lang_System_err = nullptr;
+  java_lang_Thread_cont = nullptr;
   java_lang_Thread_parkBlocker = nullptr;
   java_lang_Thread_daemon = nullptr;
   java_lang_Thread_group = nullptr;
@@ -1099,6 +1134,7 @@
   java_nio_ByteBuffer_offset = nullptr;
   java_util_Collections_EMPTY_LIST = nullptr;
   java_util_concurrent_ThreadLocalRandom_seeder = nullptr;
+  jdk_internal_vm_Continuation_virtualThreadContext = nullptr;
   jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer = nullptr;
   jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = nullptr;
   libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index a3b456a..3bbdf03 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -162,6 +162,9 @@
   static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
   static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
   static ArtMethod* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+  static ArtMethod* jdk_internal_vm_Continuation_doYieldNative;
+  static ArtMethod* jdk_internal_vm_Continuation_enter;
+  static ArtMethod* jdk_internal_vm_Continuation_enterSpecial;
   static ArtMethod* libcore_reflect_AnnotationFactory_createAnnotation;
   static ArtMethod* libcore_reflect_AnnotationMember_init;
   static ArtMethod* org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -191,6 +194,7 @@
   static ArtField* java_lang_System_in;
   static ArtField* java_lang_System_out;
   static ArtField* java_lang_System_err;
+  static ArtField* java_lang_Thread_cont;
   static ArtField* java_lang_Thread_parkBlocker;
   static ArtField* java_lang_Thread_daemon;
   static ArtField* java_lang_Thread_group;
@@ -223,6 +227,7 @@
   static ArtField* java_nio_ByteBuffer_offset;
   static ArtField* java_util_Collections_EMPTY_LIST;
   static ArtField* java_util_concurrent_ThreadLocalRandom_seeder;
+  static ArtField* jdk_internal_vm_Continuation_virtualThreadContext;
   static ArtField* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
   static ArtField* jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
   static ArtField* libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
diff --git a/test/2394-continuation-yields/build.py b/test/2394-continuation-yields/build.py
new file mode 100644
index 0000000..d531de7
--- /dev/null
+++ b/test/2394-continuation-yields/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2025 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+  if ctx.jvm:
+    return  # The test does not build on JVM
+  ctx.default_build()
diff --git a/test/2394-continuation-yields/expected-stderr.txt b/test/2394-continuation-yields/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2394-continuation-yields/expected-stderr.txt
diff --git a/test/2394-continuation-yields/expected-stdout.txt b/test/2394-continuation-yields/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2394-continuation-yields/expected-stdout.txt
diff --git a/test/2394-continuation-yields/info.txt b/test/2394-continuation-yields/info.txt
new file mode 100644
index 0000000..70263b5
--- /dev/null
+++ b/test/2394-continuation-yields/info.txt
@@ -0,0 +1 @@
+Tests for jdk.internal.vm.Continuation.
diff --git a/test/2394-continuation-yields/src/Main.java b/test/2394-continuation-yields/src/Main.java
new file mode 100644
index 0000000..6c0f8b9
--- /dev/null
+++ b/test/2394-continuation-yields/src/Main.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VirtualThreadContext;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.vm.Continuation;
+
+/**
+ * Continuation is an internal API in OpenJDK. It verifies that the Continuation run
+ * on different carrier threads before and after yielding.
+ */
+public class Main {
+
+    public static void main(String[] args) throws InterruptedException {
+        if (!com.android.art.flags.Flags.virtualThreadImplV1()) {
+            return;
+        }
+        // Exit if the thread throws any exception.
+        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
+            System.err.println("thread: " + t.getName());
+            e.printStackTrace(System.err);
+            System.exit(1);
+        });
+
+        testContinuationSleep();
+    }
+
+    private static void testContinuationSleep() throws InterruptedException {
+        Runnable continuationTask = Main::continuationTask;
+        long threadId = Long.MAX_VALUE - 1;
+        VirtualThreadContext virtualThreadContext = new VirtualThreadContext(continuationTask,
+                threadId);
+        Continuation cont = new Continuation(virtualThreadContext);
+
+        Timer timer = new Timer();
+        LinkedBlockingQueue<Object> blockingQueue = new LinkedBlockingQueue<>(1);
+        Runnable carrier2Task = () -> {
+            cont.run();
+            try {
+                blockingQueue.put(new Object());
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        };
+        Thread carrier2 = new Thread(carrier2Task);
+        TimerTask sleepTask = new TimerTask() {
+            @Override
+            public void run() {
+                carrier2.start();
+            }
+        };
+
+        Runnable carrier1Task = () -> {
+            cont.run();
+
+            timer.schedule(sleepTask, 100L);
+        };
+        Thread carrier1 = new Thread(carrier1Task);
+        carrier1.start();
+        carrier1.join(1000L);
+
+        blockingQueue.poll(1L, TimeUnit.SECONDS);
+        carrier2.join();
+
+        timer.cancel();
+    }
+
+    private static void continuationTask() {
+        long tid1 = getCarrierThreadId();
+        Continuation.yield(SharedSecrets.getJavaLangAccess().virtualThreadContinuationScope());
+        long tid2 = getCarrierThreadId();
+        if (tid1 == tid2) {
+            throw new RuntimeException("tid shouldn't be the same: "
+                    + tid1 + " != " + tid2);
+        }
+    }
+
+    private static long getCarrierThreadId() {
+        return SharedSecrets.getJavaLangAccess().currentCarrierThread().threadId();
+    }
+}
diff --git a/test/2394-continuation-yields/test-metadata.json b/test/2394-continuation-yields/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2394-continuation-yields/test-metadata.json
@@ -0,0 +1,5 @@
+{
+  "build-param": {
+    "jvm-supported": "false"
+  }
+}
diff --git a/test/2395-virtual-thread-reentrantlock/build.py b/test/2395-virtual-thread-reentrantlock/build.py
new file mode 100644
index 0000000..d531de7
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2025 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+  if ctx.jvm:
+    return  # The test does not build on JVM
+  ctx.default_build()
diff --git a/test/2395-virtual-thread-reentrantlock/expected-stderr.txt b/test/2395-virtual-thread-reentrantlock/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/expected-stderr.txt
diff --git a/test/2395-virtual-thread-reentrantlock/expected-stdout.txt b/test/2395-virtual-thread-reentrantlock/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/expected-stdout.txt
diff --git a/test/2395-virtual-thread-reentrantlock/info.txt b/test/2395-virtual-thread-reentrantlock/info.txt
new file mode 100644
index 0000000..ae55543
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/info.txt
@@ -0,0 +1 @@
+Tests for Virtual Thread.
diff --git a/test/2395-virtual-thread-reentrantlock/src/Main.java b/test/2395-virtual-thread-reentrantlock/src/Main.java
new file mode 100644
index 0000000..9531cb1
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/src/Main.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jdk.internal.access.SharedSecrets;
+
+/**
+ * Verify that {@link ReentrantLock} API running on Virtual Thread.
+ */
+public class Main {
+
+    public static void main(String[] args) throws InterruptedException, ClassNotFoundException {
+        if (!com.android.art.flags.Flags.virtualThreadImplV1()) {
+            return;
+        }
+        // Exit if the thread throws any exception.
+        Thread.setDefaultUncaughtExceptionHandler(HANDLER);
+
+        verifyCurrentThreadApi();
+        verifyReentrantLock();
+    }
+
+    private static final Thread.UncaughtExceptionHandler HANDLER = (t, e) -> {
+        System.err.println("thread: " + t.getName());
+        e.printStackTrace(System.err);
+        System.exit(1);
+    };
+
+    private static void verifyReentrantLock() throws InterruptedException {
+        ReentrantLock lock = new ReentrantLock();
+        AtomicBoolean v = new AtomicBoolean(false);
+        long timeoutThresholdNs = TimeUnit.SECONDS.toNanos(1);
+        lock.lock();
+        Thread vt = createDefaultVirtualThreadBuilder().start(() -> {
+            lock.lock();
+            v.set(true);
+            lock.unlock();
+        });
+        long startTime = System.nanoTime();
+        while (System.nanoTime() - startTime < timeoutThresholdNs) {
+            if (vt.getVirtualThreadContext().isUnmounted()) {
+                break;
+            }
+        }
+
+        assertEquals(true, vt.getVirtualThreadContext().isUnmounted());
+        assertEquals(false, v.get());
+
+        lock.unlock();
+        vt.join();
+
+        assertEquals(true, v.get());
+    }
+
+
+    static void assertEquals(boolean expected, boolean value) {
+        if (expected == value) {
+            return;
+        }
+        throw new AssertionError("assertEquals expected: " + expected
+                + ", value: " + value);
+    }
+
+    private static void verifyCurrentThreadApi() throws InterruptedException {
+        Thread vt = createDefaultVirtualThreadBuilder().start(() -> {
+            String threadClassName = Thread.currentThread().getClass().getName();
+            if (!("java.lang.VirtualThread".equals(threadClassName))) {
+                throw new AssertionError("Expect a VirtualThread instance");
+            }
+            if (Thread.currentThread().equals(getCarrierThread())) {
+                throw new AssertionError("Thread.currentThread() shouldn't "
+                        + "return a carrier thread.");
+            }
+        });
+        vt.join();
+    }
+
+    private static Thread getCarrierThread() {
+        return SharedSecrets.getJavaLangAccess().currentCarrierThread();
+    }
+
+    private static Thread.Builder.OfVirtual createDefaultVirtualThreadBuilder() {
+        return Thread.ofVirtual().uncaughtExceptionHandler(HANDLER);
+    }
+}
diff --git a/test/2395-virtual-thread-reentrantlock/test-metadata.json b/test/2395-virtual-thread-reentrantlock/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2395-virtual-thread-reentrantlock/test-metadata.json
@@ -0,0 +1,5 @@
+{
+  "build-param": {
+    "jvm-supported": "false"
+  }
+}
diff --git a/test/2395-virtual-thread-sleep-api/build.py b/test/2395-virtual-thread-sleep-api/build.py
new file mode 100644
index 0000000..d531de7
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2025 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+  if ctx.jvm:
+    return  # The test does not build on JVM
+  ctx.default_build()
diff --git a/test/2395-virtual-thread-sleep-api/expected-stderr.txt b/test/2395-virtual-thread-sleep-api/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/expected-stderr.txt
diff --git a/test/2395-virtual-thread-sleep-api/expected-stdout.txt b/test/2395-virtual-thread-sleep-api/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/expected-stdout.txt
diff --git a/test/2395-virtual-thread-sleep-api/info.txt b/test/2395-virtual-thread-sleep-api/info.txt
new file mode 100644
index 0000000..b61c3ea
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/info.txt
@@ -0,0 +1 @@
+Tests for Thread.sleep() API.
diff --git a/test/2395-virtual-thread-sleep-api/src/Main.java b/test/2395-virtual-thread-sleep-api/src/Main.java
new file mode 100644
index 0000000..5dd44b9
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/src/Main.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verify that {@link Thread#sleep(long)} API parks virtual threads.
+ */
+public class Main {
+
+    public static void main(String[] args) throws InterruptedException, ClassNotFoundException {
+        if (!com.android.art.flags.Flags.virtualThreadImplV1()) {
+            return;
+        }
+        // Exit if the thread throws any exception.
+        Thread.setDefaultUncaughtExceptionHandler(HANDLER);
+
+        testNSleepingThreads(2);
+        testNSleepingThreads(10);
+        testNSleepingThreads(100);
+        testNSleepingThreads(300);
+    }
+
+    private static final Thread.UncaughtExceptionHandler HANDLER = (t, e) -> {
+        System.err.println("thread: " + t.getName());
+        e.printStackTrace(System.err);
+        System.exit(1);
+    };
+
+    private static final int SLEEP_DURATION_MULTIPLIER = 10;
+
+    /**
+     * Start {@code numOfThreads} virtual threads sleeping for the given duration and wait until
+     * all threads join or time out.
+     * @param numOfThreads number of concurrent virtual threads sleeping
+     */
+    private static void testNSleepingThreads(int numOfThreads) throws InterruptedException {
+        long sleepDurationMs = Math.max(numOfThreads * SLEEP_DURATION_MULTIPLIER, 100);
+        long timeoutThresholdNs = TimeUnit.MILLISECONDS.toNanos(
+                sleepDurationMs * 3);
+        List<Thread> threads = new ArrayList<>(numOfThreads);
+        for (int i = 0; i < numOfThreads; i++) {
+            Thread vt = Thread.ofVirtual().uncaughtExceptionHandler(HANDLER).start(() -> {
+                try {
+                    Thread.sleep(sleepDurationMs);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+            threads.add(vt);
+        }
+
+        List<Thread> threadsToBeUnmounted = new ArrayList<>(threads);
+        long startTime = System.nanoTime();
+        while (System.nanoTime() - startTime < timeoutThresholdNs) {
+            if (threadsToBeUnmounted.isEmpty()) {
+                break;
+            }
+            // We can't assert that a virtual thread runs on different carrier thread before parking
+            // and after un-parking because it is backed by carrier threads from a thread pool.
+            // Instead, we verify that it's unmounted in a busy loop ,
+            threadsToBeUnmounted.removeIf(vt -> vt.getVirtualThreadContext().isUnmounted());
+        }
+
+        if (!threadsToBeUnmounted.isEmpty()) {
+            Thread vt = threadsToBeUnmounted.getFirst();
+            throw new AssertionError("Thread " + vt.threadId() + " wasn't unmounted. "
+                    + "Consider increasing SLEEP_DURATION_MULTIPLIER for slow test "
+                    + "configurations.");
+        }
+
+        for (Thread vt : threads) {
+            vt.join();
+        }
+    }
+}
diff --git a/test/2395-virtual-thread-sleep-api/test-metadata.json b/test/2395-virtual-thread-sleep-api/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2395-virtual-thread-sleep-api/test-metadata.json
@@ -0,0 +1,5 @@
+{
+  "build-param": {
+    "jvm-supported": "false"
+  }
+}
diff --git a/test/913-heaps/expected-stdout.txt b/test/913-heaps/expected-stdout.txt
index f54fe80..8101819 100644
--- a/test/913-heaps/expected-stdout.txt
+++ b/test/913-heaps/expected-stdout.txt
@@ -1,8 +1,8 @@
 ---
 true true
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=0,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780000, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780004, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780001, length=-1]
@@ -46,8 +46,8 @@
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780005, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780009, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780006, length=-1]
@@ -86,17 +86,17 @@
 5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123456780005, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123456780011, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
 ---
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=0,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123456780020, length=-1]
 3@1001 --(class)--> 1001@0 [size=123456780021, length=-1]
@@ -108,8 +108,8 @@
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123456780025, length=-1]
 3@1001 --(class)--> 1001@0 [size=123456780026, length=-1]
@@ -189,8 +189,8 @@
 ---
 ---- untagged objects
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=0,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780054, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780051, length=-1]
@@ -234,8 +234,8 @@
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780059, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780056, length=-1]
@@ -275,8 +275,8 @@
 6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
 ---
 ---- tagged classes
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780064, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780061, length=-1]
@@ -301,8 +301,8 @@
 5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
 ---
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]
 1002@0 --(interface)--> 2001@0 [size=123456780069, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123456780066, length=-1]
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 4b66f51..4a3db8c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1628,7 +1628,10 @@
                 "2390-virtual-thread-parking-error-leak",
                 "2391-virtual-thread-sleeps",
                 "2392-virtual-thread-pinning-jni",
-                "2393-virtual-thread-pinning-monitor"],
+                "2393-virtual-thread-pinning-monitor",
+                "2394-continuation-yields",
+                "2395-virtual-thread-reentrantlock",
+                "2395-virtual-thread-sleep-api"],
       "variant": "jvm",
       "description": ["Tests for ART-specific Virtual Thread APIs."]
     },