mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:49:47 +00:00
[vm] Cleanup Dart_Get/Set/HasStickyError API and use isolate's sticky error only as a backup for thread's sticky error
Both thread and isolate have sticky error fields. Dart API was using isolate's sticky error, while Dart code was using thread's sticky error. There was a one-way move of a thread's sticky error into isolate when thread was unscheduled. This causes a problem as error in the isolate may go unnoticed and repeated unscheduling/re-scheduling might end up overwriting the error in the isolate (which triggers the assertion). To solve this problem, this CL: * Cleans up Dart API which manipulates isolate's sticky error, so isolate's sticky error is never set directly. * When sceduling an isolate to a thread, sticky error is moved back from isolate (if any). With this changes, thread's sticky error is always used if thread is running, and isolate's sticky error is only used to hold sticky error while isolate has no thread. Fixes https://github.com/dart-lang/sdk/issues/35590 Change-Id: I99b128cac363ca2df75f6e64c083b1ec36c866ce Reviewed-on: https://dart-review.googlesource.com/c/89442 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
5b1daaac6c
commit
b10f179608
|
@ -705,11 +705,6 @@ char* BuildIsolateName(const char* script_name, const char* func_name) {
|
|||
static void OnIsolateShutdown(void* callback_data) {
|
||||
Dart_EnterScope();
|
||||
|
||||
Dart_Handle sticky_error = Dart_GetStickyError();
|
||||
if (!Dart_IsNull(sticky_error) && !Dart_IsFatalError(sticky_error)) {
|
||||
Log::PrintErr("%s\n", Dart_GetError(sticky_error));
|
||||
}
|
||||
|
||||
IsolateData* isolate_data = reinterpret_cast<IsolateData*>(callback_data);
|
||||
isolate_data->OnIsolateShutdown();
|
||||
|
||||
|
|
|
@ -1175,29 +1175,6 @@ DART_EXPORT bool Dart_IsPausedOnExit();
|
|||
*/
|
||||
DART_EXPORT void Dart_SetPausedOnExit(bool paused);
|
||||
|
||||
/**
|
||||
* Called when the embedder has caught a top level unhandled exception error
|
||||
* in the current isolate.
|
||||
*
|
||||
* NOTE: It is illegal to call this twice on the same isolate without first
|
||||
* clearing the sticky error to null.
|
||||
*
|
||||
* \param error The unhandled exception error.
|
||||
*/
|
||||
DART_EXPORT void Dart_SetStickyError(Dart_Handle error);
|
||||
|
||||
/**
|
||||
* Does the current isolate have a sticky error?
|
||||
*/
|
||||
DART_EXPORT bool Dart_HasStickyError();
|
||||
|
||||
/**
|
||||
* Gets the sticky error for the current isolate.
|
||||
*
|
||||
* \return A handle to the sticky error object or null.
|
||||
*/
|
||||
DART_EXPORT Dart_Handle Dart_GetStickyError();
|
||||
|
||||
/**
|
||||
* Handles the next pending message for the current isolate.
|
||||
*
|
||||
|
|
|
@ -1391,43 +1391,6 @@ DART_EXPORT void Dart_SetPausedOnExit(bool paused) {
|
|||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT void Dart_SetStickyError(Dart_Handle error) {
|
||||
Thread* thread = Thread::Current();
|
||||
DARTSCOPE(thread);
|
||||
Isolate* isolate = thread->isolate();
|
||||
CHECK_ISOLATE(isolate);
|
||||
NoSafepointScope no_safepoint_scope;
|
||||
if ((isolate->sticky_error() != Error::null()) && !::Dart_IsNull(error)) {
|
||||
FATAL1("%s expects there to be no sticky error.", CURRENT_FUNC);
|
||||
}
|
||||
if (!::Dart_IsUnhandledExceptionError(error) && !::Dart_IsNull(error)) {
|
||||
FATAL1("%s expects the error to be an unhandled exception error or null.",
|
||||
CURRENT_FUNC);
|
||||
}
|
||||
isolate->SetStickyError(Api::UnwrapErrorHandle(Z, error).raw());
|
||||
}
|
||||
|
||||
DART_EXPORT bool Dart_HasStickyError() {
|
||||
Thread* T = Thread::Current();
|
||||
Isolate* isolate = T->isolate();
|
||||
CHECK_ISOLATE(isolate);
|
||||
NoSafepointScope no_safepoint_scope;
|
||||
return isolate->sticky_error() != Error::null();
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Handle Dart_GetStickyError() {
|
||||
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;
|
||||
}
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
DART_EXPORT void Dart_NotifyIdle(int64_t deadline) {
|
||||
Thread* T = Thread::Current();
|
||||
CHECK_ISOLATE(T->isolate());
|
||||
|
@ -1608,10 +1571,12 @@ DART_EXPORT Dart_Handle Dart_RunLoop() {
|
|||
}
|
||||
}
|
||||
::Dart_EnterIsolate(Api::CastIsolate(I));
|
||||
if (I->sticky_error() != Object::null()) {
|
||||
{
|
||||
Thread* T = Thread::Current();
|
||||
TransitionNativeToVM transition(T);
|
||||
return Api::NewHandle(T, I->StealStickyError());
|
||||
if (T->sticky_error() != Object::null()) {
|
||||
TransitionNativeToVM transition(T);
|
||||
return Api::NewHandle(T, T->StealStickyError());
|
||||
}
|
||||
}
|
||||
if (FLAG_print_class_table) {
|
||||
HANDLESCOPE(Thread::Current());
|
||||
|
|
|
@ -3696,22 +3696,6 @@ VM_UNIT_TEST_CASE(DartAPI_SetMessageCallbacks) {
|
|||
Dart_ShutdownIsolate();
|
||||
}
|
||||
|
||||
TEST_CASE(DartAPI_SetStickyError) {
|
||||
const char* kScriptChars = "main() => throw 'HI';";
|
||||
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
|
||||
Dart_Handle retobj = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT(Dart_IsError(retobj));
|
||||
EXPECT(Dart_IsUnhandledExceptionError(retobj));
|
||||
EXPECT(!Dart_HasStickyError());
|
||||
EXPECT(Dart_GetStickyError() == Dart_Null());
|
||||
Dart_SetStickyError(retobj);
|
||||
EXPECT(Dart_HasStickyError());
|
||||
EXPECT(Dart_GetStickyError() != Dart_Null());
|
||||
Dart_SetStickyError(Dart_Null());
|
||||
EXPECT(!Dart_HasStickyError());
|
||||
EXPECT(Dart_GetStickyError() == Dart_Null());
|
||||
}
|
||||
|
||||
TEST_CASE(DartAPI_TypeGetNonParamtericTypes) {
|
||||
const char* kScriptChars =
|
||||
"class MyClass0 {\n"
|
||||
|
|
|
@ -1699,11 +1699,8 @@ static void ShutdownIsolate(uword parameter) {
|
|||
Dart::ShutdownIsolate(isolate);
|
||||
}
|
||||
|
||||
void Isolate::SetStickyError(RawError* sticky_error) {
|
||||
ASSERT(
|
||||
((sticky_error_ == Error::null()) || (sticky_error == Error::null())) &&
|
||||
(sticky_error != sticky_error_));
|
||||
sticky_error_ = sticky_error;
|
||||
void Isolate::ClearStickyError() {
|
||||
sticky_error_ = Error::null();
|
||||
}
|
||||
|
||||
void Isolate::Run() {
|
||||
|
@ -2329,13 +2326,6 @@ void Isolate::TrackDeoptimizedCode(const Code& code) {
|
|||
deoptimized_code.Add(code);
|
||||
}
|
||||
|
||||
RawError* Isolate::StealStickyError() {
|
||||
NoSafepointScope no_safepoint;
|
||||
RawError* return_value = sticky_error_;
|
||||
sticky_error_ = Error::null();
|
||||
return return_value;
|
||||
}
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
void Isolate::set_pending_service_extension_calls(
|
||||
const GrowableObjectArray& value) {
|
||||
|
@ -2837,6 +2827,11 @@ Thread* Isolate::ScheduleThread(bool is_mutator, bool bypass_safepoint) {
|
|||
os_thread->set_thread(thread);
|
||||
if (is_mutator) {
|
||||
scheduled_mutator_thread_ = thread;
|
||||
if (sticky_error() != Error::null()) {
|
||||
ASSERT(thread->sticky_error() == Error::null());
|
||||
thread->sticky_error_ = sticky_error_;
|
||||
sticky_error_ = Error::null();
|
||||
}
|
||||
}
|
||||
Thread::SetCurrent(thread);
|
||||
os_thread->EnableThreadInterrupts();
|
||||
|
|
|
@ -606,11 +606,8 @@ class Isolate : public BaseIsolate {
|
|||
void set_deoptimized_code_array(const GrowableObjectArray& value);
|
||||
void TrackDeoptimizedCode(const Code& code);
|
||||
|
||||
// Also sends a paused at exit event over the service protocol.
|
||||
void SetStickyError(RawError* sticky_error);
|
||||
|
||||
RawError* sticky_error() const { return sticky_error_; }
|
||||
DART_WARN_UNUSED_RESULT RawError* StealStickyError();
|
||||
void ClearStickyError();
|
||||
|
||||
void RetainKernelBlob(const ExternalTypedData& kernel_blob);
|
||||
|
||||
|
|
|
@ -866,12 +866,13 @@ void Object::Init(Isolate* isolate) {
|
|||
// needs to be created earlier as VM isolate snapshot reader references it
|
||||
// before Object::FinalizeVMIsolate.
|
||||
|
||||
// Some thread fields need to be reinitialized as null constants have not been
|
||||
// initialized until now.
|
||||
// Some thread and isolate fields need to be reinitialized as null constants
|
||||
// have not been initialized until now.
|
||||
Thread* thr = Thread::Current();
|
||||
ASSERT(thr != NULL);
|
||||
thr->ClearStickyError();
|
||||
thr->clear_pending_functions();
|
||||
isolate->ClearStickyError();
|
||||
|
||||
ASSERT(!null_object_->IsSmi());
|
||||
ASSERT(!null_array_->IsSmi());
|
||||
|
|
|
@ -3142,7 +3142,8 @@ static bool ReloadSources(Thread* thread, JSONStream* js) {
|
|||
"A library tag handler must be installed.");
|
||||
return true;
|
||||
}
|
||||
if ((isolate->sticky_error() != Error::null()) ||
|
||||
if ((thread->sticky_error() != Error::null()) ||
|
||||
(isolate->sticky_error() != Error::null()) ||
|
||||
(Thread::Current()->sticky_error() != Error::null())) {
|
||||
js->PrintError(kIsolateReloadBarred,
|
||||
"This isolate cannot reload sources anymore because there "
|
||||
|
|
|
@ -381,10 +381,6 @@ class RunServiceTask : public ThreadPool::Task {
|
|||
if (!error.IsNull() && !error.IsUnwindError()) {
|
||||
OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString());
|
||||
}
|
||||
error = I->sticky_error();
|
||||
if (!error.IsNull() && !error.IsUnwindError()) {
|
||||
OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString());
|
||||
}
|
||||
Dart::RunShutdownCallback();
|
||||
}
|
||||
ASSERT(ServiceIsolate::IsServiceIsolate(I));
|
||||
|
|
|
@ -140,49 +140,6 @@ static void HandleRootMessage(const Array& message) {
|
|||
Service::HandleRootMessage(message);
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(Service_IsolateStickyError) {
|
||||
const char* kScript = "main() => throw 'HI THERE STICKY';\n";
|
||||
|
||||
Isolate* isolate = thread->isolate();
|
||||
isolate->set_is_runnable(true);
|
||||
Dart_Handle result;
|
||||
{
|
||||
TransitionVMToNative transition(thread);
|
||||
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
|
||||
EXPECT_VALID(lib);
|
||||
result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT(Dart_IsUnhandledExceptionError(result));
|
||||
EXPECT(!Dart_HasStickyError());
|
||||
}
|
||||
EXPECT(Thread::Current()->sticky_error() == Error::null());
|
||||
|
||||
{
|
||||
JSONStream js;
|
||||
isolate->PrintJSON(&js, false);
|
||||
// No error property and no PauseExit state.
|
||||
EXPECT_NOTSUBSTRING("\"error\":", js.ToCString());
|
||||
EXPECT_NOTSUBSTRING("HI THERE STICKY", js.ToCString());
|
||||
EXPECT_NOTSUBSTRING("PauseExit", js.ToCString());
|
||||
}
|
||||
|
||||
{
|
||||
// Set the sticky error.
|
||||
TransitionVMToNative transition(thread);
|
||||
Dart_SetStickyError(result);
|
||||
Dart_SetPausedOnExit(true);
|
||||
EXPECT(Dart_HasStickyError());
|
||||
}
|
||||
|
||||
{
|
||||
JSONStream js;
|
||||
isolate->PrintJSON(&js, false);
|
||||
// Error and PauseExit set.
|
||||
EXPECT_SUBSTRING("\"error\":", js.ToCString());
|
||||
EXPECT_SUBSTRING("HI THERE STICKY", js.ToCString());
|
||||
EXPECT_SUBSTRING("PauseExit", js.ToCString());
|
||||
}
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(Service_IdZones) {
|
||||
Zone* zone = thread->zone();
|
||||
Isolate* isolate = thread->isolate();
|
||||
|
|
30
tests/lib_2/isolate/kill_regexp_test.dart
Normal file
30
tests/lib_2/isolate/kill_regexp_test.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=--intrinsify
|
||||
// VMOptions=--no_intrinsify
|
||||
|
||||
import "dart:isolate";
|
||||
import "dart:async";
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
isomain1(replyPort) {
|
||||
final regexp = new RegExp('[ab]c');
|
||||
while (true) {
|
||||
Expect.equals(4, regexp.allMatches("acbcacbc").length);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
ReceivePort reply = new ReceivePort();
|
||||
Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
|
||||
new Timer(new Duration(milliseconds: 50), () {
|
||||
print('killing isolate $i');
|
||||
isolate.kill(priority: Isolate.immediate);
|
||||
});
|
||||
});
|
||||
reply.close();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue