d3fde891bf
Here's how the deadlock can happen:
* The vm-service background compiler asks all threads to go to a
safepoint before it finalizes the code. Then it finalizes the
code and notifies code observers. A code observer posts a
message to the vm-service. Posting this message requires holding the
monitor of the receiving isolate's message handler (see
MessageHandler::PostMessage).
* The vm-service mutator holds it's message handlers monitor and
enters the isolate (see MessageHandler::HandleMessages). The
StartIsolateScope schedules the mutator thread, which will in return
see that BG compiler wants to safepoint, so it waits for BG compiler
to finish.
At this point the mutator holds the MessageHandler's monitor and waits
for BG compiler to finish safepoint. The BG compiler tries to post a
message to the message handler (and the monitor is locked).
-> This CL ensures the BG compiler only arranges a safepoint before it
starts creating the instructions object. It fills it in and exits the
safepoint again. Only then do we notify code observers.
Furthermore this CL ensures we transition out of VM to native state
before calling the Dart_PostCMessage() function.
Change-Id: I5320440d54ca303a1ccc7556600a42d37cd6e1de
Reviewed-on: https://dart-review.googlesource.com/c/93421
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
115 lines
4.7 KiB
C++
115 lines
4.7 KiB
C++
// Copyright (c) 2014, 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.
|
|
|
|
#include "vm/globals.h"
|
|
#if defined(TARGET_ARCH_ARM64)
|
|
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/native_entry_test.h"
|
|
#include "vm/object.h"
|
|
#include "vm/runtime_entry.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
#define __ assembler->
|
|
|
|
namespace dart {
|
|
|
|
static Function* CreateFunction(const char* name) {
|
|
const String& class_name =
|
|
String::Handle(Symbols::New(Thread::Current(), "ownerClass"));
|
|
const Script& script = Script::Handle();
|
|
const Library& lib = Library::Handle(Library::New(class_name));
|
|
const Class& owner_class = Class::Handle(
|
|
Class::New(lib, class_name, script, TokenPosition::kNoSource));
|
|
const String& function_name =
|
|
String::ZoneHandle(Symbols::New(Thread::Current(), name));
|
|
Function& function = Function::ZoneHandle(Function::New(
|
|
function_name, RawFunction::kRegularFunction, true, false, false, false,
|
|
false, owner_class, TokenPosition::kNoSource));
|
|
return &function;
|
|
}
|
|
|
|
// Test calls to stub code which calls into the runtime.
|
|
static void GenerateCallToCallRuntimeStub(Assembler* assembler, int length) {
|
|
const int argc = 2;
|
|
const Smi& smi_length = Smi::ZoneHandle(Smi::New(length));
|
|
__ EnterDartFrame(0);
|
|
__ PushObject(Object::null_object()); // Push Null obj for return value.
|
|
__ PushObject(smi_length); // Push argument 1: length.
|
|
__ PushObject(Object::null_object()); // Push argument 2: type arguments.
|
|
ASSERT(kAllocateArrayRuntimeEntry.argument_count() == argc);
|
|
__ CallRuntime(kAllocateArrayRuntimeEntry, argc);
|
|
__ add(SP, SP, Operand(argc * kWordSize));
|
|
__ Pop(R0); // Pop return value from return slot.
|
|
__ LeaveDartFrame();
|
|
__ ret();
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(CallRuntimeStubCode) {
|
|
extern const Function& RegisterFakeFunction(const char* name,
|
|
const Code& code);
|
|
const int length = 10;
|
|
const char* kName = "Test_CallRuntimeStubCode";
|
|
ObjectPoolBuilder object_pool_builder;
|
|
Assembler assembler(&object_pool_builder);
|
|
GenerateCallToCallRuntimeStub(&assembler, length);
|
|
const Code& code = Code::Handle(Code::FinalizeCodeAndNotify(
|
|
*CreateFunction("Test_CallRuntimeStubCode"), nullptr, &assembler,
|
|
Code::PoolAttachment::kAttachPool));
|
|
const Function& function = RegisterFakeFunction(kName, code);
|
|
Array& result = Array::Handle();
|
|
result ^= DartEntry::InvokeFunction(function, Object::empty_array());
|
|
EXPECT_EQ(length, result.Length());
|
|
}
|
|
|
|
// Test calls to stub code which calls into a leaf runtime entry.
|
|
static void GenerateCallToCallLeafRuntimeStub(Assembler* assembler,
|
|
const char* str_value,
|
|
intptr_t lhs_index_value,
|
|
intptr_t rhs_index_value,
|
|
intptr_t length_value) {
|
|
const String& str = String::ZoneHandle(String::New(str_value, Heap::kOld));
|
|
const Smi& lhs_index = Smi::ZoneHandle(Smi::New(lhs_index_value));
|
|
const Smi& rhs_index = Smi::ZoneHandle(Smi::New(rhs_index_value));
|
|
const Smi& length = Smi::ZoneHandle(Smi::New(length_value));
|
|
__ EnterDartFrame(0);
|
|
__ ReserveAlignedFrameSpace(0);
|
|
__ LoadObject(R0, str);
|
|
__ LoadObject(R1, lhs_index);
|
|
__ LoadObject(R2, rhs_index);
|
|
__ LoadObject(R3, length);
|
|
__ CallRuntime(kCaseInsensitiveCompareUC16RuntimeEntry, 4);
|
|
__ LeaveDartFrame();
|
|
__ ret(); // Return value is in R0.
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(CallLeafRuntimeStubCode) {
|
|
extern const Function& RegisterFakeFunction(const char* name,
|
|
const Code& code);
|
|
const char* str_value = "abAB";
|
|
intptr_t lhs_index_value = 0;
|
|
intptr_t rhs_index_value = 2;
|
|
intptr_t length_value = 2;
|
|
const char* kName = "Test_CallLeafRuntimeStubCode";
|
|
ObjectPoolBuilder object_pool_builder;
|
|
Assembler assembler(&object_pool_builder);
|
|
GenerateCallToCallLeafRuntimeStub(&assembler, str_value, lhs_index_value,
|
|
rhs_index_value, length_value);
|
|
const Code& code = Code::Handle(Code::FinalizeCodeAndNotify(
|
|
*CreateFunction("Test_CallLeafRuntimeStubCode"), nullptr, &assembler,
|
|
Code::PoolAttachment::kAttachPool));
|
|
const Function& function = RegisterFakeFunction(kName, code);
|
|
Instance& result = Instance::Handle();
|
|
result ^= DartEntry::InvokeFunction(function, Object::empty_array());
|
|
EXPECT_EQ(Bool::True().raw(), result.raw());
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // defined TARGET_ARCH_ARM64
|