[vm] Prevent GC when there are outstanding pointers from Dart_TypedDataAcquireData.
Bug: https://github.com/dart-lang/sdk/issues/37256
Change-Id: I1dcd2e32d8308c5a7169731169a048b8f1bfcc79
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106023
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index b999e21..bd50a0f 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -499,10 +499,9 @@
ASSERT(IsLibrary(kernel_library) ||
IsAdministrative(CanonicalNameParent(kernel_library)));
{
- NoSafepointScope no_safepoint_scope(thread_);
- RawLibrary* raw_lib;
name_index_handle_ = Smi::New(kernel_library);
- raw_lib = info_.LookupLibrary(thread_, name_index_handle_);
+ RawLibrary* raw_lib = info_.LookupLibrary(thread_, name_index_handle_);
+ NoSafepointScope no_safepoint_scope(thread_);
if (raw_lib != Library::null()) {
return raw_lib;
}
@@ -521,10 +520,9 @@
RawClass* TranslationHelper::LookupClassByKernelClass(NameIndex kernel_class) {
ASSERT(IsClass(kernel_class));
{
- NoSafepointScope no_safepoint_scope(thread_);
- RawClass* raw_class;
name_index_handle_ = Smi::New(kernel_class);
- raw_class = info_.LookupClass(thread_, name_index_handle_);
+ RawClass* raw_class = info_.LookupClass(thread_, name_index_handle_);
+ NoSafepointScope no_safepoint_scope(thread_);
if (raw_class != Class::null()) {
return raw_class;
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 4ebdac9..ee31194 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1465,13 +1465,14 @@
Thread* T = Thread::Current();
Isolate* I = T->isolate();
CHECK_ISOLATE(I);
- NoSafepointScope no_safepoint_scope;
- if (I->sticky_error() != Error::null()) {
- TransitionNativeToVM transition(T);
- Dart_Handle error = Api::NewHandle(T, I->sticky_error());
- return error;
+ {
+ NoSafepointScope no_safepoint_scope;
+ if (I->sticky_error() == Error::null()) {
+ return Api::Null();
+ }
}
- return Dart_Null();
+ TransitionNativeToVM transition(T);
+ return Api::NewHandle(T, I->sticky_error());
}
DART_EXPORT void Dart_NotifyIdle(int64_t deadline) {
@@ -3695,7 +3696,8 @@
intptr_t size_in_bytes = 0;
void* data_tmp = NULL;
bool external = false;
- // If it is an external typed data object just return the data field.
+ T->IncrementNoSafepointScopeDepth();
+ START_NO_CALLBACK_SCOPE(T);
if (RawObject::IsExternalTypedDataClassId(class_id)) {
const ExternalTypedData& obj =
Api::UnwrapExternalTypedDataHandle(Z, object);
@@ -3705,13 +3707,10 @@
data_tmp = obj.DataAddr(0);
external = true;
} else if (RawObject::IsTypedDataClassId(class_id)) {
- // Regular typed data object, set up some GC and API callback guards.
const TypedData& obj = Api::UnwrapTypedDataHandle(Z, object);
ASSERT(!obj.IsNull());
length = obj.Length();
size_in_bytes = length * TypedData::ElementSizeInBytes(class_id);
- T->IncrementNoSafepointScopeDepth();
- START_NO_CALLBACK_SCOPE(T);
data_tmp = obj.DataAddr(0);
} else {
ASSERT(RawObject::IsTypedDataViewClassId(class_id));
@@ -3724,8 +3723,6 @@
val = view_obj.offset_in_bytes();
intptr_t offset_in_bytes = val.Value();
const auto& obj = Instance::Handle(view_obj.typed_data());
- T->IncrementNoSafepointScopeDepth();
- START_NO_CALLBACK_SCOPE(T);
if (TypedData::IsTypedData(obj)) {
const TypedData& data_obj = TypedData::Cast(obj);
data_tmp = data_obj.DataAddr(offset_in_bytes);
@@ -3769,10 +3766,8 @@
!RawObject::IsTypedDataClassId(class_id)) {
RETURN_TYPE_ERROR(Z, object, 'TypedData');
}
- if (!RawObject::IsExternalTypedDataClassId(class_id)) {
- T->DecrementNoSafepointScopeDepth();
- END_NO_CALLBACK_SCOPE(T);
- }
+ T->DecrementNoSafepointScopeDepth();
+ END_NO_CALLBACK_SCOPE(T);
if (FLAG_verify_acquired_data) {
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
WeakTable* table = I->api_state()->acquired_table();
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index e500239..4841f0d 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -2337,6 +2337,7 @@
void* data;
intptr_t len;
result = Dart_TypedDataAcquireData(array, &type, &data, &len);
+ EXPECT(!Thread::Current()->IsAtSafepoint());
EXPECT_VALID(result);
EXPECT_EQ(expected_type, type);
EXPECT_EQ(kLength, len);
@@ -2345,14 +2346,12 @@
EXPECT_EQ(i, dataP[i]);
}
- if (!is_external) {
- // Now try allocating a string with outstanding Acquires and it should
- // return an error.
- result = NewString("We expect an error here");
- EXPECT_ERROR(result,
- "Internal Dart data pointers have been acquired, "
- "please release them using Dart_TypedDataReleaseData.");
- }
+ // Now try allocating a string with outstanding Acquires and it should
+ // return an error.
+ result = NewString("We expect an error here");
+ EXPECT_ERROR(result,
+ "Internal Dart data pointers have been acquired, "
+ "please release them using Dart_TypedDataReleaseData.");
// Now modify the values in the directly accessible array and then check
// it we see the changes back in dart.
@@ -2361,6 +2360,7 @@
}
// Release direct access to the typed data object.
+ EXPECT(!Thread::Current()->IsAtSafepoint());
result = Dart_TypedDataReleaseData(array);
EXPECT_VALID(result);
@@ -2369,6 +2369,29 @@
EXPECT_VALID(result);
}
+class BackgroundGCTask : public ThreadPool::Task {
+ public:
+ BackgroundGCTask(Isolate* isolate, Monitor* monitor, bool* done)
+ : isolate_(isolate), monitor_(monitor), done_(done) {}
+ virtual void Run() {
+ Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask);
+ for (intptr_t i = 0; i < 10; i++) {
+ Thread::Current()->heap()->CollectAllGarbage(Heap::kDebugging);
+ }
+ Thread::ExitIsolateAsHelper();
+ {
+ MonitorLocker ml(monitor_);
+ *done_ = true;
+ ml.Notify();
+ }
+ }
+
+ private:
+ Isolate* isolate_;
+ Monitor* monitor_;
+ bool* done_;
+};
+
static void TestTypedDataDirectAccess1() {
const char* kScriptChars =
"import 'dart:typed_data';\n"
@@ -2397,20 +2420,35 @@
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
- // Test with an regular typed data object.
- Dart_Handle list_access_test_obj;
- list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
- EXPECT_VALID(list_access_test_obj);
- TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kInt8, false);
+ Monitor monitor;
+ bool done = false;
+ Dart::thread_pool()->Run<BackgroundGCTask>(Isolate::Current(), &monitor,
+ &done);
- // Test with an external typed data object.
- uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- intptr_t data_length = ARRAY_SIZE(data);
- Dart_Handle ext_list_access_test_obj;
- ext_list_access_test_obj =
- Dart_NewExternalTypedData(Dart_TypedData_kUint8, data, data_length);
- EXPECT_VALID(ext_list_access_test_obj);
- TestDirectAccess(lib, ext_list_access_test_obj, Dart_TypedData_kUint8, true);
+ for (intptr_t i = 0; i < 10; i++) {
+ // Test with an regular typed data object.
+ Dart_Handle list_access_test_obj;
+ list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(list_access_test_obj);
+ TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kInt8, false);
+
+ // Test with an external typed data object.
+ uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ intptr_t data_length = ARRAY_SIZE(data);
+ Dart_Handle ext_list_access_test_obj;
+ ext_list_access_test_obj =
+ Dart_NewExternalTypedData(Dart_TypedData_kUint8, data, data_length);
+ EXPECT_VALID(ext_list_access_test_obj);
+ TestDirectAccess(lib, ext_list_access_test_obj, Dart_TypedData_kUint8,
+ true);
+ }
+
+ {
+ MonitorLocker ml(&monitor);
+ while (!done) {
+ ml.Wait();
+ }
+ }
}
TEST_CASE(DartAPI_TypedDataDirectAccess1Unverified) {
diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc
index 3888cdf..d3e3771 100644
--- a/runtime/vm/heap/safepoint.cc
+++ b/runtime/vm/heap/safepoint.cc
@@ -113,8 +113,14 @@
if (FLAG_trace_safepoint && num_attempts > 10) {
// We have been waiting too long, start logging this as we might
// have an issue where a thread is not checking in for a safepoint.
- OS::PrintErr("Attempt:%" Pd " waiting for %d threads to check in\n",
- num_attempts, number_threads_not_at_safepoint_);
+ for (Thread* current = isolate()->thread_registry()->active_list();
+ current != NULL; current = current->next()) {
+ if (!current->IsAtSafepoint()) {
+ OS::PrintErr("Attempt:%" Pd
+ " waiting for thread %s to check in\n",
+ num_attempts, current->os_thread()->name());
+ }
+ }
}
}
}
diff --git a/runtime/vm/heap/safepoint.h b/runtime/vm/heap/safepoint.h
index 906933a..4f3cfb8 100644
--- a/runtime/vm/heap/safepoint.h
+++ b/runtime/vm/heap/safepoint.h
@@ -290,7 +290,9 @@
explicit TransitionNativeToVM(Thread* T) : TransitionSafepointState(T) {
// We are about to execute vm code and so we are not at a safepoint anymore.
ASSERT(T->execution_state() == Thread::kThreadInNative);
- T->ExitSafepoint();
+ if (T->no_callback_scope_depth() == 0) {
+ T->ExitSafepoint();
+ }
T->set_execution_state(Thread::kThreadInVM);
}
@@ -298,7 +300,9 @@
// We are returning to native code and so we are at a safepoint.
ASSERT(thread()->execution_state() == Thread::kThreadInVM);
thread()->set_execution_state(Thread::kThreadInNative);
- thread()->EnterSafepoint();
+ if (thread()->no_callback_scope_depth() == 0) {
+ thread()->EnterSafepoint();
+ }
}
private:
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index fe394e4..11e65e9 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1756,11 +1756,6 @@
}
}
-#if !defined(PRODUCT)
- // Clean up debugger resources.
- debugger()->Shutdown();
-#endif
-
// Close all the ports owned by this isolate.
PortMap::ClosePorts(message_handler());
@@ -1859,6 +1854,9 @@
HandleScope handle_scope(thread);
ServiceIsolate::SendIsolateShutdownMessage();
KernelIsolate::NotifyAboutIsolateShutdown(this);
+#if !defined(PRODUCT)
+ debugger()->Shutdown();
+#endif
}
if (heap_ != nullptr) {
@@ -2552,7 +2550,7 @@
pause_loop_monitor_ = new Monitor();
}
Dart_EnterScope();
- MonitorLocker ml(pause_loop_monitor_);
+ MonitorLocker ml(pause_loop_monitor_, false);
Dart_MessageNotifyCallback saved_notify_callback = message_notify_callback();
set_message_notify_callback(Isolate::WakePauseEventHandler);
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index b5001d1..4f60e0f 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -2131,16 +2131,16 @@
RawLibrary* result;
name_index_handle_ = Smi::New(library);
{
- NoSafepointScope no_safepoint_scope(thread_);
result = kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
+ NoSafepointScope no_safepoint_scope(thread_);
if (result != Library::null()) {
return result;
}
}
const String& url = H.DartString(H.CanonicalNameString(library));
{
- NoSafepointScope no_safepoint_scope(thread_);
result = Library::LookupLibrary(thread_, url);
+ NoSafepointScope no_safepoint_scope(thread_);
if (result == Library::null()) {
return result;
}
@@ -2154,9 +2154,9 @@
RawLibrary* KernelLoader::LookupLibrary(NameIndex library) {
name_index_handle_ = Smi::New(library);
{
- NoSafepointScope no_safepoint_scope(thread_);
RawLibrary* result =
kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
+ NoSafepointScope no_safepoint_scope(thread_);
if (result != Library::null()) {
return result;
}
@@ -2192,9 +2192,9 @@
RawClass* KernelLoader::LookupClass(const Library& library, NameIndex klass) {
name_index_handle_ = Smi::New(klass);
{
- NoSafepointScope no_safepoint_scope(thread_);
RawClass* raw_class =
kernel_program_info_.LookupClass(thread_, name_index_handle_);
+ NoSafepointScope no_safepoint_scope(thread_);
if (raw_class != Class::null()) {
return raw_class;
}
diff --git a/runtime/vm/lockers.cc b/runtime/vm/lockers.cc
index 7a43e4a..79f6f25 100644
--- a/runtime/vm/lockers.cc
+++ b/runtime/vm/lockers.cc
@@ -12,6 +12,11 @@
int64_t millis) {
ASSERT(thread == Thread::Current());
ASSERT(thread->execution_state() == Thread::kThreadInVM);
+#if defined(DEBUG)
+ if (no_safepoint_scope_) {
+ thread->DecrementNoSafepointScopeDepth();
+ }
+#endif
thread->set_execution_state(Thread::kThreadInBlockedState);
thread->EnterSafepoint();
Monitor::WaitResult result = monitor_->Wait(millis);
@@ -26,6 +31,11 @@
monitor_->Enter();
}
thread->set_execution_state(Thread::kThreadInVM);
+#if defined(DEBUG)
+ if (no_safepoint_scope_) {
+ thread->IncrementNoSafepointScopeDepth();
+ }
+#endif
return result;
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ad74bbe..1ff3a45 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2139,6 +2139,7 @@
ASSERT(Utils::IsAligned(size, kObjectAlignment));
Thread* thread = Thread::Current();
ASSERT(thread->execution_state() == Thread::kThreadInVM);
+ ASSERT(thread->no_safepoint_scope_depth() == 0);
ASSERT(thread->no_callback_scope_depth() == 0);
Heap* heap = thread->heap();
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index ed4dcc2..80ba87f 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4,6 +4,9 @@
#include "vm/service.h"
+#include <memory>
+#include <utility>
+
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include "platform/globals.h"
@@ -13,6 +16,7 @@
#include "vm/compiler/jit/compiler.h"
#include "vm/cpu.h"
#include "vm/dart_api_impl.h"
+#include "vm/dart_api_message.h"
#include "vm/dart_api_state.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
@@ -1164,14 +1168,11 @@
json_cobj.value.as_string = const_cast<char*>(event->ToCString());
list_values[1] = &json_cobj;
- // In certain cases (e.g. in the implementation of Dart_IsolateMakeRunnable)
- // we do not have a current isolate/thread.
- auto thread = Thread::Current();
- if (thread != nullptr) {
- TransitionVMToNative transition(thread);
- Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
- } else {
- Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
+ ApiMessageWriter writer;
+ std::unique_ptr<Message> msg = writer.WriteCMessage(
+ &list_cobj, ServiceIsolate::Port(), Message::kNormalPriority);
+ if (msg != nullptr) {
+ PortMap::PostMessage(std::move(msg));
}
}
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index ab8071e..b13b149 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -736,6 +736,7 @@
}
void EnterSafepoint() {
+ ASSERT(no_safepoint_scope_depth() == 0);
// First try a fast update of the thread state to indicate it is at a
// safepoint.
if (!TryEnterSafepoint()) {
@@ -765,6 +766,7 @@
}
void CheckForSafepoint() {
+ ASSERT(no_safepoint_scope_depth() == 0);
if (IsSafepointRequested()) {
BlockForSafepoint();
}