// Copyright (c) 2013, 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 #include "vm/isolate.h" #include "include/dart_api.h" #include "include/dart_native_api.h" #include "platform/assert.h" #include "platform/atomic.h" #include "platform/growable_array.h" #include "platform/text_buffer.h" #include "vm/canonical_tables.h" #include "vm/class_finalizer.h" #include "vm/code_observers.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_api_message.h" #include "vm/dart_api_state.h" #include "vm/dart_entry.h" #include "vm/debugger.h" #include "vm/deopt_instructions.h" #include "vm/dispatch_table.h" #include "vm/ffi_callback_metadata.h" #include "vm/flags.h" #include "vm/heap/heap.h" #include "vm/heap/pointer_block.h" #include "vm/heap/safepoint.h" #include "vm/heap/verifier.h" #include "vm/image_snapshot.h" #include "vm/isolate_reload.h" #include "vm/kernel_isolate.h" #include "vm/lockers.h" #include "vm/log.h" #include "vm/message_handler.h" #include "vm/message_snapshot.h" #include "vm/object.h" #include "vm/object_id_ring.h" #include "vm/object_store.h" #include "vm/os_thread.h" #include "vm/port.h" #include "vm/profiler.h" #include "vm/reusable_handles.h" #include "vm/reverse_pc_lookup_cache.h" #include "vm/service.h" #include "vm/service_event.h" #include "vm/service_isolate.h" #include "vm/simulator.h" #include "vm/stack_frame.h" #include "vm/stub_code.h" #include "vm/symbols.h" #include "vm/tags.h" #include "vm/thread.h" #include "vm/thread_interrupter.h" #include "vm/thread_registry.h" #include "vm/timeline.h" #include "vm/visitor.h" #if !defined(DART_PRECOMPILED_RUNTIME) #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/stub_code_compiler.h" #endif namespace dart { DECLARE_FLAG(bool, print_metrics); DECLARE_FLAG(bool, trace_service); DECLARE_FLAG(bool, trace_shutdown); DECLARE_FLAG(bool, warn_on_pause_with_no_debugger); DECLARE_FLAG(int, old_gen_growth_time_ratio); // Reload flags. DECLARE_FLAG(int, reload_every); #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) DECLARE_FLAG(bool, check_reloaded); DECLARE_FLAG(bool, reload_every_back_off); DECLARE_FLAG(bool, trace_reload); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) static void DeterministicModeHandler(bool value) { if (value) { FLAG_background_compilation = false; // Timing dependent. FLAG_concurrent_mark = false; // Timing dependent. FLAG_concurrent_sweep = false; // Timing dependent. FLAG_scavenger_tasks = 0; // Timing dependent. FLAG_old_gen_growth_time_ratio = 0; // Timing dependent. FLAG_random_seed = 0x44617274; // "Dart" } } DEFINE_FLAG_HANDLER(DeterministicModeHandler, deterministic, "Enable deterministic mode."); DEFINE_FLAG(bool, disable_thread_pool_limit, false, "Disables the limit of the thread pool (simulates custom embedder " "with custom message handler on unlimited number of threads)."); // Quick access to the locally defined thread() and isolate() methods. #define T (thread()) #define I (isolate()) #define IG (isolate_group()) #if defined(DEBUG) // Helper class to ensure that a live origin_id is never reused // and assigned to an isolate. class VerifyOriginId : public IsolateVisitor { public: explicit VerifyOriginId(Dart_Port id) : id_(id) {} void VisitIsolate(Isolate* isolate) { ASSERT(isolate->origin_id() != id_); } private: Dart_Port id_; DISALLOW_COPY_AND_ASSIGN(VerifyOriginId); }; #endif static std::unique_ptr SerializeMessage(Dart_Port dest_port, const Instance& obj) { return WriteMessage(/* same_group */ false, obj, dest_port, Message::kNormalPriority); } static std::unique_ptr SerializeMessage(Zone* zone, Dart_Port dest_port, Dart_CObject* obj) { return WriteApiMessage(zone, obj, dest_port, Message::kNormalPriority); } void IsolateGroupSource::add_loaded_blob( Zone* zone, const ExternalTypedData& external_typed_data) { Array& loaded_blobs = Array::Handle(); bool saved_external_typed_data = false; if (loaded_blobs_ != nullptr) { loaded_blobs = loaded_blobs_; // Walk the array, and (if stuff was removed) compact and reuse the space. // Note that the space has to be compacted as the ordering is important. WeakProperty& weak_property = WeakProperty::Handle(); WeakProperty& weak_property_tmp = WeakProperty::Handle(); ExternalTypedData& existing_entry = ExternalTypedData::Handle(zone); intptr_t next_entry_index = 0; for (intptr_t i = 0; i < loaded_blobs.Length(); i++) { weak_property ^= loaded_blobs.At(i); if (weak_property.key() != ExternalTypedData::null()) { if (i != next_entry_index) { existing_entry = ExternalTypedData::RawCast(weak_property.key()); weak_property_tmp ^= loaded_blobs.At(next_entry_index); weak_property_tmp.set_key(existing_entry); } next_entry_index++; } } if (next_entry_index < loaded_blobs.Length()) { // There's now space to re-use. weak_property ^= loaded_blobs.At(next_entry_index); weak_property.set_key(external_typed_data); next_entry_index++; saved_external_typed_data = true; } if (next_entry_index < loaded_blobs.Length()) { ExternalTypedData& nullExternalTypedData = ExternalTypedData::Handle(zone); while (next_entry_index < loaded_blobs.Length()) { // Null out any extra spaces. weak_property ^= loaded_blobs.At(next_entry_index); weak_property.set_key(nullExternalTypedData); next_entry_index++; } } } if (!saved_external_typed_data) { const WeakProperty& weak_property = WeakProperty::Handle(WeakProperty::New(Heap::kOld)); weak_property.set_key(external_typed_data); intptr_t length = loaded_blobs.IsNull() ? 0 : loaded_blobs.Length(); Array& new_array = Array::Handle(Array::Grow(loaded_blobs, length + 1, Heap::kOld)); new_array.SetAt(length, weak_property); loaded_blobs_ = new_array.ptr(); } num_blob_loads_++; } void IdleTimeHandler::InitializeWithHeap(Heap* heap) { MutexLocker ml(&mutex_); ASSERT(heap_ == nullptr && heap != nullptr); heap_ = heap; } bool IdleTimeHandler::ShouldCheckForIdle() { MutexLocker ml(&mutex_); return idle_start_time_ > 0 && FLAG_idle_timeout_micros != 0 && disabled_counter_ == 0; } void IdleTimeHandler::UpdateStartIdleTime() { MutexLocker ml(&mutex_); if (disabled_counter_ == 0) { idle_start_time_ = OS::GetCurrentMonotonicMicros(); } } bool IdleTimeHandler::ShouldNotifyIdle(int64_t* expiry) { const int64_t now = OS::GetCurrentMonotonicMicros(); MutexLocker ml(&mutex_); if (idle_start_time_ > 0 && disabled_counter_ == 0) { const int64_t expiry_time = idle_start_time_ + FLAG_idle_timeout_micros; if (expiry_time < now) { idle_start_time_ = 0; return true; } } *expiry = now + FLAG_idle_timeout_micros; return false; } void IdleTimeHandler::NotifyIdle(int64_t deadline) { { MutexLocker ml(&mutex_); disabled_counter_++; } if (heap_ != nullptr) { heap_->NotifyIdle(deadline); } { MutexLocker ml(&mutex_); disabled_counter_--; idle_start_time_ = 0; } } void IdleTimeHandler::NotifyIdleUsingDefaultDeadline() { const int64_t now = OS::GetCurrentMonotonicMicros(); NotifyIdle(now + FLAG_idle_duration_micros); } DisableIdleTimerScope::DisableIdleTimerScope(IdleTimeHandler* handler) : handler_(handler) { if (handler_ != nullptr) { MutexLocker ml(&handler_->mutex_); ++handler_->disabled_counter_; handler_->idle_start_time_ = 0; } } DisableIdleTimerScope::~DisableIdleTimerScope() { if (handler_ != nullptr) { MutexLocker ml(&handler_->mutex_); --handler_->disabled_counter_; ASSERT(handler_->disabled_counter_ >= 0); } } class FinalizeWeakPersistentHandlesVisitor : public HandleVisitor { public: explicit FinalizeWeakPersistentHandlesVisitor(IsolateGroup* isolate_group) : HandleVisitor(Thread::Current()), isolate_group_(isolate_group) {} void VisitHandle(uword addr) override { auto handle = reinterpret_cast(addr); handle->UpdateUnreachable(isolate_group_); } private: IsolateGroup* isolate_group_; DISALLOW_COPY_AND_ASSIGN(FinalizeWeakPersistentHandlesVisitor); }; void MutatorThreadPool::OnEnterIdleLocked(MutexLocker* ml, Worker* worker) { if (FLAG_idle_timeout_micros == 0) return; // If the isolate has not started running application code yet, we ignore the // idle time. if (!isolate_group_->initial_spawn_successful()) return; int64_t idle_expiry = 0; // Obtain the idle time we should wait. if (isolate_group_->idle_time_handler()->ShouldNotifyIdle(&idle_expiry)) { MutexUnlocker mls(ml); NotifyIdle(); return; } // Avoid shutdown having to wait for the timeout to expire. if (ShuttingDownLocked()) return; // Wait for the recommended idle timeout. // We can be woken up because of a), b) or c) const auto result = worker->Sleep(idle_expiry - OS::GetCurrentMonotonicMicros()); // a) If there are new tasks we have to run them. if (TasksWaitingToRunLocked()) return; // b) If the thread pool is shutting down we're done. if (ShuttingDownLocked()) return; // c) We timed out and should run the idle notifier. if (result == Monitor::kTimedOut && isolate_group_->idle_time_handler()->ShouldNotifyIdle(&idle_expiry)) { MutexUnlocker mls(ml); NotifyIdle(); return; } // There must've been another thread doing active work in the meantime. // If that thread becomes idle and is the last idle thread it will run this // code again. } void MutatorThreadPool::NotifyIdle() { EnterIsolateGroupScope isolate_group_scope(isolate_group_); isolate_group_->idle_time_handler()->NotifyIdleUsingDefaultDeadline(); } IsolateGroup::IsolateGroup(std::shared_ptr source, void* embedder_data, ObjectStore* object_store, Dart_IsolateFlags api_flags, bool is_vm_isolate) : class_table_(nullptr), cached_class_table_table_(nullptr), object_store_(object_store), class_table_allocator_(), is_vm_isolate_(is_vm_isolate), embedder_data_(embedder_data), thread_pool_(), isolates_lock_(new SafepointRwLock()), isolates_(), start_time_micros_(OS::GetCurrentMonotonicMicros()), is_system_isolate_group_(source->flags.is_system_isolate), random_(), #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) last_reload_timestamp_(OS::GetCurrentTimeMillis()), reload_every_n_stack_overflow_checks_(FLAG_reload_every), #endif source_(std::move(source)), api_state_(new ApiState()), thread_registry_(new ThreadRegistry()), safepoint_handler_(new SafepointHandler(this)), store_buffer_(new StoreBuffer()), heap_(nullptr), saved_unlinked_calls_(Array::null()), initial_field_table_(new FieldTable(/*isolate=*/nullptr)), shared_initial_field_table_(new FieldTable(/*isolate=*/nullptr, /*isolate_group=*/nullptr)), shared_field_table_(new FieldTable(/*isolate=*/nullptr, this)), #if !defined(DART_PRECOMPILED_RUNTIME) background_compiler_(new BackgroundCompiler(this)), #endif symbols_mutex_(), type_canonicalization_mutex_(), type_arguments_canonicalization_mutex_(), subtype_test_cache_mutex_(), megamorphic_table_mutex_(), type_feedback_mutex_(), patchable_call_mutex_(), constant_canonicalization_mutex_(), kernel_data_lib_cache_mutex_(), kernel_data_class_cache_mutex_(), kernel_constants_mutex_(), field_list_mutex_(), boxed_field_list_(GrowableObjectArray::null()), program_lock_(new SafepointRwLock(SafepointLevel::kGCAndDeopt)), active_mutators_monitor_(new Monitor()), max_active_mutators_(Scavenger::MaxMutatorThreadCount()) #if !defined(PRODUCT) , debugger_(new GroupDebugger(this)) #endif { FlagsCopyFrom(api_flags); if (!is_vm_isolate) { thread_pool_.reset( new MutatorThreadPool(this, FLAG_disable_thread_pool_limit ? 0 : Scavenger::MaxMutatorThreadCount())); } { WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_); // Keep isolate IDs less than 2^53 so web clients of the service // protocol can process it properly. // // See https://github.com/dart-lang/sdk/issues/53081. id_ = isolate_group_random_->NextJSInt(); } heap_walk_class_table_ = class_table_ = new ClassTable(&class_table_allocator_); cached_class_table_table_.store(class_table_->table()); memset(&native_assets_api_, 0, sizeof(NativeAssetsApi)); } IsolateGroup::IsolateGroup(std::shared_ptr source, void* embedder_data, Dart_IsolateFlags api_flags, bool is_vm_isolate) : IsolateGroup(source, embedder_data, new ObjectStore(), api_flags, is_vm_isolate) { if (object_store() != nullptr) { object_store()->InitStubs(); } } IsolateGroup::~IsolateGroup() { // Ensure we destroy the heap before the other members. heap_ = nullptr; ASSERT(old_marking_stack_ == nullptr); ASSERT(new_marking_stack_ == nullptr); ASSERT(deferred_marking_stack_ == nullptr); if (obfuscation_map_ != nullptr) { for (intptr_t i = 0; obfuscation_map_[i] != nullptr; i++) { delete[] obfuscation_map_[i]; } delete[] obfuscation_map_; } class_table_allocator_.Free(class_table_); if (heap_walk_class_table_ != class_table_) { class_table_allocator_.Free(heap_walk_class_table_); } #if !defined(PRODUCT) delete debugger_; debugger_ = nullptr; #endif } void IsolateGroup::RegisterIsolate(Isolate* isolate) { SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get()); ASSERT(isolates_lock_->IsCurrentThreadWriter()); isolates_.Append(isolate); isolate_count_++; } bool IsolateGroup::ContainsOnlyOneIsolate() { SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get()); // We do allow 0 here as well, because the background compiler might call // this method while the mutator thread is in shutdown procedure and // unregistered itself already. return isolate_count_ == 0 || isolate_count_ == 1; } void IsolateGroup::RunWithLockedGroup(std::function fun) { SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get()); fun(); } void IsolateGroup::UnregisterIsolate(Isolate* isolate) { SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get()); isolates_.Remove(isolate); } bool IsolateGroup::UnregisterIsolateDecrementCount() { SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get()); isolate_count_--; return isolate_count_ == 0; } void IsolateGroup::CreateHeap(bool is_vm_isolate, bool is_service_or_kernel_isolate) { Heap::Init(this, is_vm_isolate, is_vm_isolate ? 0 // New gen size 0; VM isolate should only allocate in old. : FLAG_new_gen_semi_max_size * MBInWords, (is_service_or_kernel_isolate ? kDefaultMaxOldGenHeapSize : FLAG_old_gen_heap_size) * MBInWords); #define ISOLATE_GROUP_METRIC_CONSTRUCTORS(type, variable, name, unit) \ metric_##variable##_.InitInstance(this, name, nullptr, Metric::unit); ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_CONSTRUCTORS) #undef ISOLATE_GROUP_METRIC_CONSTRUCTORS } void IsolateGroup::Shutdown() { char* name = nullptr; // We retrieve the flag value once to avoid the compiler complaining about the // possibly uninitialized value of name, as the compiler is unaware that when // the flag variable is non-const, it is set once during VM initialization and // never changed after, and that modification never runs concurrently with // this method. const bool trace_shutdown = FLAG_trace_shutdown; if (trace_shutdown) { name = Utils::StrDup(source()->name); OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutdown starting for group %s\n", Dart::UptimeMillis(), name); } // Ensure to join all threads before waiting for pending GC tasks (the thread // pool can trigger idle notification, which can start new GC tasks). // // (The vm-isolate doesn't have a thread pool.) if (!is_vm_isolate_) { ASSERT(thread_pool_ != nullptr); thread_pool_->Shutdown(); thread_pool_.reset(); } // Needs to happen before starting to destroy the heap so helper tasks like // the SampleBlockProcessor don't try to enter the group during this // tear-down. UnregisterIsolateGroup(this); // Wait for any pending GC tasks. if (heap_ != nullptr) { // Wait for any concurrent GC tasks to finish before shutting down. // TODO(rmacnak): Interrupt tasks for faster shutdown. PageSpace* old_space = heap_->old_space(); MonitorLocker ml(old_space->tasks_lock()); while (old_space->tasks() > 0) { ml.Wait(); } // Needs to happen before ~PageSpace so TLS and the thread registry are // still valid. old_space->AbandonMarkingForShutdown(); } // If the creation of the isolate group (or the first isolate within the // isolate group) failed, we do not invoke the cleanup callback (the // embedder is responsible for handling the creation error). if (initial_spawn_successful_ && !is_vm_isolate_) { auto group_shutdown_callback = Isolate::GroupCleanupCallback(); if (group_shutdown_callback != nullptr) { group_shutdown_callback(embedder_data()); } } delete this; // After this isolate group has died we might need to notify a pending // `Dart_Cleanup()` call. { if (trace_shutdown) { OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Notifying " "isolate group shutdown (%s)\n", Dart::UptimeMillis(), name); } MonitorLocker ml(Isolate::isolate_creation_monitor_); if (!Isolate::creation_enabled_ && !IsolateGroup::HasApplicationIsolateGroups()) { ml.Notify(); } if (trace_shutdown) { OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done Notifying " "isolate group shutdown (%s)\n", Dart::UptimeMillis(), name); } } if (trace_shutdown) { OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done shutdown for group %s\n", Dart::UptimeMillis(), name); free(name); } } void IsolateGroup::set_heap(std::unique_ptr heap) { idle_time_handler_.InitializeWithHeap(heap.get()); heap_ = std::move(heap); } void IsolateGroup::set_saved_unlinked_calls(const Array& saved_unlinked_calls) { saved_unlinked_calls_ = saved_unlinked_calls.ptr(); } void IsolateGroup::IncreaseMutatorCount(Isolate* mutator, bool is_nested_reenter) { ASSERT(mutator->group() == this); // If the mutator was temporarily blocked on a worker thread, we have to // unblock the worker thread again. if (is_nested_reenter) { ASSERT(mutator->mutator_thread() != nullptr); thread_pool()->MarkCurrentWorkerAsUnBlocked(); } // Prevent too many mutators from entering the isolate group to avoid // pathological behavior where many threads are fighting for obtaining TLABs. { // NOTE: This is performance critical code, we should avoid monitors and use // std::atomics in the fast case (where active_mutators < // max_active_mutators) and only use monitors in the uncommon case. MonitorLocker ml(active_mutators_monitor_.get()); ASSERT(active_mutators_ <= max_active_mutators_); while (active_mutators_ == max_active_mutators_) { waiting_mutators_++; ml.Wait(); waiting_mutators_--; } active_mutators_++; } } void IsolateGroup::DecreaseMutatorCount(Isolate* mutator, bool is_nested_exit) { ASSERT(mutator->group() == this); // If the mutator thread has an active stack and runs on our thread pool we // will mark the worker as blocked, thereby possibly spawning a new worker for // pending tasks (if there are any). if (is_nested_exit) { ASSERT(mutator->mutator_thread() != nullptr); thread_pool()->MarkCurrentWorkerAsBlocked(); } { // NOTE: This is performance critical code, we should avoid monitors and use // std::atomics in the fast case (where active_mutators < // max_active_mutators) and only use monitors in the uncommon case. MonitorLocker ml(active_mutators_monitor_.get()); ASSERT(active_mutators_ <= max_active_mutators_); active_mutators_--; if (waiting_mutators_ > 0) { ml.Notify(); } } } #ifndef PRODUCT void IsolateGroup::PrintJSON(JSONStream* stream, bool ref) { JSONObject jsobj(stream); PrintToJSONObject(&jsobj, ref); } void IsolateGroup::PrintToJSONObject(JSONObject* jsobj, bool ref) { jsobj->AddProperty("type", (ref ? "@IsolateGroup" : "IsolateGroup")); jsobj->AddServiceId(ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, id()); jsobj->AddProperty("name", source()->script_uri); jsobj->AddPropertyF("number", "%" Pu64 "", id()); jsobj->AddProperty("isSystemIsolateGroup", is_system_isolate_group()); if (ref) { return; } { JSONArray isolate_array(jsobj, "isolates"); for (auto it = isolates_.Begin(); it != isolates_.End(); ++it) { Isolate* isolate = *it; isolate_array.AddValue(isolate, /*ref=*/true); } } } void IsolateGroup::PrintMemoryUsageJSON(JSONStream* stream) { int64_t used = heap()->TotalUsedInWords(); int64_t capacity = heap()->TotalCapacityInWords(); int64_t external_used = heap()->TotalExternalInWords(); JSONObject jsobj(stream); // This is the same "MemoryUsage" that the isolate-specific "getMemoryUsage" // rpc method returns. jsobj.AddProperty("type", "MemoryUsage"); jsobj.AddProperty64("heapUsage", used * kWordSize); jsobj.AddProperty64("heapCapacity", capacity * kWordSize); jsobj.AddProperty64("externalUsage", external_used * kWordSize); } #endif void IsolateGroup::ForEach(std::function action) { ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_); for (auto isolate_group : *isolate_groups_) { action(isolate_group); } } void IsolateGroup::RunWithIsolateGroup( uint64_t id, std::function action, std::function not_found) { ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_); for (auto isolate_group : *isolate_groups_) { if (isolate_group->id() == id) { action(isolate_group); return; } } not_found(); } void IsolateGroup::RegisterIsolateGroup(IsolateGroup* isolate_group) { WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_); isolate_groups_->Append(isolate_group); } void IsolateGroup::UnregisterIsolateGroup(IsolateGroup* isolate_group) { WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_); isolate_groups_->Remove(isolate_group); } bool IsolateGroup::HasApplicationIsolateGroups() { ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_); for (auto group : *isolate_groups_) { if (!IsolateGroup::IsSystemIsolateGroup(group)) { return true; } } return false; } bool IsolateGroup::HasOnlyVMIsolateGroup() { ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_); for (auto group : *isolate_groups_) { if (!group->is_vm_isolate()) { return false; } } return true; } void IsolateGroup::Init() { ASSERT(isolate_groups_rwlock_ == nullptr); isolate_groups_rwlock_ = new RwLock(); ASSERT(isolate_groups_ == nullptr); isolate_groups_ = new IntrusiveDList(); isolate_group_random_ = new Random(); } void IsolateGroup::Cleanup() { delete isolate_group_random_; isolate_group_random_ = nullptr; delete isolate_groups_rwlock_; isolate_groups_rwlock_ = nullptr; ASSERT(isolate_groups_->IsEmpty()); delete isolate_groups_; isolate_groups_ = nullptr; } bool IsolateVisitor::IsSystemIsolate(Isolate* isolate) const { return Isolate::IsSystemIsolate(isolate); } Bequest::~Bequest() { if (handle_ == nullptr) { return; } IsolateGroup* isolate_group = IsolateGroup::Current(); CHECK_ISOLATE_GROUP(isolate_group); NoSafepointScope no_safepoint_scope; ApiState* state = isolate_group->api_state(); ASSERT(state != nullptr); state->FreePersistentHandle(handle_); } void IsolateGroup::RegisterClass(const Class& cls) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) if (IsReloading()) { program_reload_context()->RegisterClass(cls); return; } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) if (cls.IsTopLevel()) { class_table()->RegisterTopLevel(cls); } else { class_table()->Register(cls); } } #if defined(DEBUG) void IsolateGroup::ValidateClassTable() { class_table()->Validate(); } #endif // DEBUG void IsolateGroup::RegisterSharedStaticField(const Field& field, const Object& initial_value) { const bool need_to_grow_backing_store = shared_initial_field_table()->Register(field); const intptr_t field_id = field.field_id(); shared_initial_field_table()->SetAt(field_id, initial_value.ptr()); if (need_to_grow_backing_store) { // We have to stop other isolates from accessing shared isolate group // field state, since we'll have to grow the backing store. GcSafepointOperationScope scope(Thread::Current()); const bool need_to_grow_other_backing_store = shared_field_table()->Register(field, field_id); ASSERT(need_to_grow_other_backing_store); } else { const bool need_to_grow_other_backing_store = shared_field_table()->Register(field, field_id); ASSERT(!need_to_grow_other_backing_store); } shared_field_table()->SetAt(field_id, initial_value.ptr()); } void IsolateGroup::RegisterStaticField(const Field& field, const Object& initial_value) { ASSERT(program_lock()->IsCurrentThreadWriter()); ASSERT(field.is_static()); if (field.is_shared()) { RegisterSharedStaticField(field, initial_value); return; } const bool need_to_grow_backing_store = initial_field_table()->Register(field); const intptr_t field_id = field.field_id(); initial_field_table()->SetAt(field_id, initial_value.ptr()); SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get()); if (need_to_grow_backing_store) { // We have to stop other isolates from accessing their field state, since // we'll have to grow the backing store. GcSafepointOperationScope scope(Thread::Current()); for (auto isolate : isolates_) { auto field_table = isolate->field_table(); if (field_table->IsReadyToUse()) { field_table->Register(field, field_id); field_table->SetAt(field_id, initial_value.ptr()); } } } else { for (auto isolate : isolates_) { auto field_table = isolate->field_table(); if (field_table->IsReadyToUse()) { field_table->Register(field, field_id); field_table->SetAt(field_id, initial_value.ptr()); } } } } void IsolateGroup::FreeStaticField(const Field& field) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) // This can only be called during hot-reload. ASSERT(program_reload_context() != nullptr); #endif const intptr_t field_id = field.field_id(); if (field.is_shared()) { shared_field_table()->Free(field_id); } else { initial_field_table()->Free(field_id); ForEachIsolate([&](Isolate* isolate) { auto field_table = isolate->field_table(); // The isolate might've just been created and is now participating in // the reload request inside `IsolateGroup::RegisterIsolate()`. // At that point it doesn't have the field table setup yet. if (field_table->IsReadyToUse()) { field_table->Free(field_id); } }); } } Isolate* IsolateGroup::EnterTemporaryIsolate() { Dart_IsolateFlags flags; Isolate::FlagsInitialize(&flags); Isolate* const isolate = Isolate::InitIsolate("temp", this, flags); ASSERT(isolate != nullptr); ASSERT(Isolate::Current() == isolate); return isolate; } void IsolateGroup::ExitTemporaryIsolate() { Thread* thread = Thread::Current(); ASSERT(thread != nullptr); thread->set_execution_state(Thread::kThreadInVM); Dart::ShutdownIsolate(thread); } void IsolateGroup::RehashConstants(Become* become) { // Even though no individual constant contains a cycle, there can be "cycles" // between the canonical tables if some const instances of A have fields that // are const instance of B and vice versa. So set all the old tables to the // side and clear all the tables attached to the classes before rehashing // instead of resetting and rehash one class at a time. Thread* thread = Thread::Current(); StackZone stack_zone(thread); Zone* zone = stack_zone.GetZone(); intptr_t num_cids = class_table()->NumCids(); Array** old_constant_tables = zone->Alloc(num_cids); for (intptr_t i = 0; i < num_cids; i++) { old_constant_tables[i] = nullptr; } Class& cls = Class::Handle(zone); for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) { if (!class_table()->IsValidIndex(cid) || !class_table()->HasValidClassAt(cid)) { continue; } if ((cid == kTypeArgumentsCid) || IsStringClassId(cid)) { // TypeArguments and Symbols have special tables for canonical objects // that aren't based on address. continue; } if ((cid == kMintCid) || (cid == kDoubleCid)) { // Constants stored as a plain list or in a hashset with a stable // hashcode, which only depends on the actual value of the constant. continue; } cls = class_table()->At(cid); if (cls.constants() == Array::null()) continue; old_constant_tables[cid] = &Array::Handle(zone, cls.constants()); cls.set_constants(Object::null_array()); } // Clear invalid hashes. heap()->ResetCanonicalHashTable(); Instance& constant = Instance::Handle(zone); Field& field = Field::Handle(zone); String& name = String::Handle(zone); Array& new_values = Array::Handle(zone); Instance& old_value = Instance::Handle(zone); Instance& new_value = Instance::Handle(zone); Instance& deleted = Instance::Handle(zone); if (become != nullptr) { for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) { Array* old_constants = old_constant_tables[cid]; if (old_constants == nullptr) continue; cls = class_table()->At(cid); CanonicalInstancesSet set(zone, old_constants->ptr()); CanonicalInstancesSet::Iterator it(&set); while (it.MoveNext()) { constant ^= set.GetKey(it.Current()); ASSERT(!constant.IsNull()); ASSERT(!constant.InVMIsolateHeap()); constant.ClearCanonical(); } set.Release(); } } for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) { Array* old_constants = old_constant_tables[cid]; if (old_constants == nullptr) continue; cls = class_table()->At(cid); CanonicalInstancesSet set(zone, old_constants->ptr()); CanonicalInstancesSet::Iterator it(&set); if (cls.is_enum_class() && (become != nullptr)) { field = cls.LookupStaticField(Symbols::_DeletedEnumSentinel()); deleted ^= field.StaticConstFieldValue(); if (deleted.IsNull()) { deleted = Instance::New(cls, Heap::kOld); field = object_store()->enum_name_field(); name = cls.ScrubbedName(); name = Symbols::FromConcat(thread, Symbols::_DeletedEnumPrefix(), name); deleted.SetField(field, name); field = object_store()->enum_index_field(); new_value = Smi::New(-1); deleted.SetField(field, new_value); field = cls.LookupStaticField(Symbols::_DeletedEnumSentinel()); // The static const field contains `Object::null()` instead of // `Object::sentinel()` - so it's not considered an initializing store. field.SetStaticConstFieldValue(deleted, /*assert_initializing_store*/ false); } field = cls.LookupField(Symbols::Values()); new_values ^= field.StaticConstFieldValue(); field = object_store()->enum_name_field(); while (it.MoveNext()) { old_value ^= set.GetKey(it.Current()); ASSERT(old_value.GetClassId() == cid); bool found = false; for (intptr_t j = 0; j < new_values.Length(); j++) { new_value ^= new_values.At(j); ASSERT(new_value.GetClassId() == cid); if (old_value.GetField(field) == new_value.GetField(field)) { found = true; break; } } if (!found) { new_value = deleted.ptr(); } if (old_value.ptr() != new_value.ptr()) { become->Add(old_value, new_value, "enum forwarding"); } if (new_value.IsCanonical()) { cls.InsertCanonicalConstant(zone, new_value); } } } else { while (it.MoveNext()) { old_value ^= set.GetKey(it.Current()); ASSERT(!old_value.IsNull()); if (become == nullptr) { ASSERT(old_value.IsCanonical()); cls.InsertCanonicalConstant(zone, old_value); } else { new_value = old_value.Canonicalize(thread); if (old_value.ptr() != new_value.ptr()) { become->Add(old_value, new_value, "constant forwarding"); } } } } set.Release(); } // Save memory. heap()->ResetCanonicalHashTable(); } void Isolate::SendInternalLibMessage(LibMsgId msg_id, uint64_t capability) { const bool ok = SendInternalLibMessage(main_port(), msg_id, capability); if (!ok) UNREACHABLE(); } bool Isolate::SendInternalLibMessage(Dart_Port main_port, LibMsgId msg_id, uint64_t capability) { Dart_CObject array_entry_msg_kind; array_entry_msg_kind.type = Dart_CObject_kInt64; array_entry_msg_kind.value.as_int64 = Message::kIsolateLibOOBMsg; Dart_CObject array_entry_msg_id; array_entry_msg_id.type = Dart_CObject_kInt64; array_entry_msg_id.value.as_int64 = msg_id; Dart_CObject array_entry_capability; array_entry_capability.type = Dart_CObject_kCapability; array_entry_capability.value.as_capability.id = capability; Dart_CObject* array_entries[3] = { &array_entry_msg_kind, &array_entry_msg_id, &array_entry_capability, }; Dart_CObject message; message.type = Dart_CObject_kArray; message.value.as_array.values = array_entries; message.value.as_array.length = ARRAY_SIZE(array_entries); AllocOnlyStackZone zone; std::unique_ptr msg = WriteApiMessage( zone.GetZone(), &message, main_port, Message::kOOBPriority); if (msg == nullptr) UNREACHABLE(); return PortMap::PostMessage(std::move(msg)); } void IsolateGroup::set_object_store(ObjectStore* object_store) { object_store_.reset(object_store); } class IsolateMessageHandler : public MessageHandler { public: explicit IsolateMessageHandler(Isolate* isolate); ~IsolateMessageHandler(); const char* name() const override; void MessageNotify(Message::Priority priority) override; MessageStatus HandleMessage(std::unique_ptr message) override; #ifndef PRODUCT void NotifyPauseOnStart() override; void NotifyPauseOnExit() override; #endif // !PRODUCT #if defined(DEBUG) // Check that it is safe to access this handler. void CheckAccess() const override; #endif Isolate* isolate() const override { return isolate_; } IsolateGroup* isolate_group() const { return isolate_->group(); } bool KeepAliveLocked() override { // Otherwise we only stay alive as long as there's active receive ports, or // there are FFI callbacks keeping the isolate alive. return isolate_->HasLivePorts() || isolate_->HasOpenNativeCallables(); } private: // A result of false indicates that the isolate should terminate the // processing of further events. ErrorPtr HandleLibMessage(const Array& message); MessageStatus ProcessUnhandledException(const Error& result); Isolate* isolate_; }; IsolateMessageHandler::IsolateMessageHandler(Isolate* isolate) : isolate_(isolate) {} IsolateMessageHandler::~IsolateMessageHandler() {} const char* IsolateMessageHandler::name() const { return isolate_->name(); } // Isolate library OOB messages are fixed sized arrays which have the // following format: // [ OOB dispatch, Isolate library dispatch, ] ErrorPtr IsolateMessageHandler::HandleLibMessage(const Array& message) { if (message.Length() < 2) return Error::null(); Zone* zone = T->zone(); const Object& type = Object::Handle(zone, message.At(1)); if (!type.IsSmi()) return Error::null(); const intptr_t msg_type = Smi::Cast(type).Value(); switch (msg_type) { case Isolate::kPauseMsg: { // [ OOB, kPauseMsg, pause capability, resume capability ] if (message.Length() != 4) return Error::null(); Object& obj = Object::Handle(zone, message.At(2)); if (!I->VerifyPauseCapability(obj)) return Error::null(); obj = message.At(3); if (!obj.IsCapability()) return Error::null(); if (I->AddResumeCapability(Capability::Cast(obj))) { increment_paused(); } break; } case Isolate::kResumeMsg: { // [ OOB, kResumeMsg, pause capability, resume capability ] if (message.Length() != 4) return Error::null(); Object& obj = Object::Handle(zone, message.At(2)); if (!I->VerifyPauseCapability(obj)) return Error::null(); obj = message.At(3); if (!obj.IsCapability()) return Error::null(); if (I->RemoveResumeCapability(Capability::Cast(obj))) { decrement_paused(); } break; } case Isolate::kPingMsg: { // [ OOB, kPingMsg, responsePort, priority, response ] if (message.Length() != 5) return Error::null(); const Object& obj2 = Object::Handle(zone, message.At(2)); if (!obj2.IsSendPort()) return Error::null(); const SendPort& send_port = SendPort::Cast(obj2); const Object& obj3 = Object::Handle(zone, message.At(3)); if (!obj3.IsSmi()) return Error::null(); const intptr_t priority = Smi::Cast(obj3).Value(); const Object& obj4 = Object::Handle(zone, message.At(4)); if (!obj4.IsInstance() && !obj4.IsNull()) return Error::null(); const Instance& response = obj4.IsNull() ? Instance::null_instance() : Instance::Cast(obj4); if (priority == Isolate::kImmediateAction) { PortMap::PostMessage(SerializeMessage(send_port.Id(), response)); } else { ASSERT((priority == Isolate::kBeforeNextEventAction) || (priority == Isolate::kAsEventAction)); // Update the message so that it will be handled immediately when it // is picked up from the message queue the next time. message.SetAt( 0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg))); message.SetAt(3, Smi::Handle(zone, Smi::New(Isolate::kImmediateAction))); this->PostMessage( SerializeMessage(Message::kIllegalPort, message), priority == Isolate::kBeforeNextEventAction /* at_head */); } break; } case Isolate::kKillMsg: case Isolate::kInternalKillMsg: { // [ OOB, kKillMsg, terminate capability, priority ] if (message.Length() != 4) return Error::null(); Object& obj = Object::Handle(zone, message.At(3)); if (!obj.IsSmi()) return Error::null(); const intptr_t priority = Smi::Cast(obj).Value(); if (priority == Isolate::kImmediateAction) { Thread::Current()->StartUnwindError(); obj = message.At(2); if (I->VerifyTerminateCapability(obj)) { // We will kill the current isolate by returning an UnwindError. if (msg_type == Isolate::kKillMsg) { const String& msg = String::Handle( String::New("isolate terminated by Isolate.kill")); const UnwindError& error = UnwindError::Handle(UnwindError::New(msg)); error.set_is_user_initiated(true); return error.ptr(); } else if (msg_type == Isolate::kInternalKillMsg) { const String& msg = String::Handle(String::New("isolate terminated by vm")); return UnwindError::New(msg); } else { UNREACHABLE(); } } else { return Error::null(); } } else { ASSERT((priority == Isolate::kBeforeNextEventAction) || (priority == Isolate::kAsEventAction)); // Update the message so that it will be handled immediately when it // is picked up from the message queue the next time. message.SetAt( 0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg))); message.SetAt(3, Smi::Handle(zone, Smi::New(Isolate::kImmediateAction))); this->PostMessage( SerializeMessage(Message::kIllegalPort, message), priority == Isolate::kBeforeNextEventAction /* at_head */); } break; } case Isolate::kInterruptMsg: { // [ OOB, kInterruptMsg, pause capability ] if (message.Length() != 3) return Error::null(); Object& obj = Object::Handle(zone, message.At(2)); if (!I->VerifyPauseCapability(obj)) return Error::null(); #if !defined(PRODUCT) // If we are already paused, don't pause again. if (I->debugger()->PauseEvent() == nullptr) { return I->debugger()->PauseInterrupted(); } #endif break; } case Isolate::kDrainServiceExtensionsMsg: { #ifndef PRODUCT Object& obj = Object::Handle(zone, message.At(2)); if (!obj.IsSmi()) return Error::null(); const intptr_t priority = Smi::Cast(obj).Value(); if (priority == Isolate::kImmediateAction) { return I->InvokePendingServiceExtensionCalls(); } else { ASSERT((priority == Isolate::kBeforeNextEventAction) || (priority == Isolate::kAsEventAction)); // Update the message so that it will be handled immediately when it // is picked up from the message queue the next time. message.SetAt( 0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg))); message.SetAt(2, Smi::Handle(zone, Smi::New(Isolate::kImmediateAction))); this->PostMessage( SerializeMessage(Message::kIllegalPort, message), priority == Isolate::kBeforeNextEventAction /* at_head */); } #else UNREACHABLE(); #endif // !PRODUCT break; } case Isolate::kAddExitMsg: case Isolate::kDelExitMsg: case Isolate::kAddErrorMsg: case Isolate::kDelErrorMsg: { // [ OOB, msg, listener port ] if (message.Length() < 3) return Error::null(); const Object& obj = Object::Handle(zone, message.At(2)); if (!obj.IsSendPort()) return Error::null(); const SendPort& listener = SendPort::Cast(obj); switch (msg_type) { case Isolate::kAddExitMsg: { if (message.Length() != 4) return Error::null(); // [ OOB, msg, listener port, response object ] const Object& response = Object::Handle(zone, message.At(3)); if (!response.IsInstance() && !response.IsNull()) { return Error::null(); } I->AddExitListener(listener, response.IsNull() ? Instance::null_instance() : Instance::Cast(response)); break; } case Isolate::kDelExitMsg: if (message.Length() != 3) return Error::null(); I->RemoveExitListener(listener); break; case Isolate::kAddErrorMsg: if (message.Length() != 3) return Error::null(); I->AddErrorListener(listener); break; case Isolate::kDelErrorMsg: if (message.Length() != 3) return Error::null(); I->RemoveErrorListener(listener); break; default: UNREACHABLE(); } break; } case Isolate::kErrorFatalMsg: { // [ OOB, kErrorFatalMsg, terminate capability, val ] if (message.Length() != 4) return Error::null(); // Check that the terminate capability has been passed correctly. Object& obj = Object::Handle(zone, message.At(2)); if (!I->VerifyTerminateCapability(obj)) return Error::null(); // Get the value to be set. obj = message.At(3); if (!obj.IsBool()) return Error::null(); I->SetErrorsFatal(Bool::Cast(obj).value()); break; } case Isolate::kCheckForReload: { // [ OOB, kCheckForReload, ignored ] #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) { ReloadParticipationScope allow_reload(T); T->CheckForSafepoint(); } #else UNREACHABLE(); #endif break; } #if defined(DEBUG) // Malformed OOB messages are silently ignored in release builds. default: FATAL("Unknown OOB message type: %" Pd "\n", msg_type); break; #endif // defined(DEBUG) } return Error::null(); } void IsolateMessageHandler::MessageNotify(Message::Priority priority) { if (priority >= Message::kOOBPriority) { // Handle out of band messages even if the mutator thread is busy. I->ScheduleInterrupts(Thread::kMessageInterrupt); } Dart_MessageNotifyCallback callback = I->message_notify_callback(); if (callback != nullptr) { // Allow the embedder to handle message notification. (*callback)(Api::CastIsolate(I)); } } bool Isolate::HasPendingMessages() { return message_handler_->HasMessages() || message_handler_->HasOOBMessages(); } MessageHandler::MessageStatus IsolateMessageHandler::HandleMessage( std::unique_ptr message) { #ifdef DEBUG CheckAccess(); #endif Thread* thread = Thread::Current(); StackZone stack_zone(thread); Zone* zone = stack_zone.GetZone(); HandleScope handle_scope(thread); #if defined(SUPPORT_TIMELINE) TimelineBeginEndScope tbes( thread, Timeline::GetIsolateStream(), message->IsOOB() ? "HandleOOBMessage" : "HandleMessage"); tbes.SetNumArguments(1); tbes.CopyArgument(0, "isolateName", I->name()); #endif // Parse the message. Object& msg_obj = Object::Handle(zone, ReadMessage(thread, message.get())); if (msg_obj.IsError()) { // An error occurred while reading the message. return ProcessUnhandledException(Error::Cast(msg_obj)); } if (!msg_obj.IsNull() && !msg_obj.IsInstance()) { // TODO(turnidge): We need to decide what an isolate does with // malformed messages. If they (eventually) come from a remote // machine, then it might make sense to drop the message entirely. // In the case that the message originated locally, which is // always true for now, then this should never occur. UNREACHABLE(); } Instance& msg = Instance::Handle(zone); msg ^= msg_obj.ptr(); // Can't use Instance::Cast because may be null. MessageStatus status = kOK; if (message->IsOOB()) { // OOB messages are expected to be fixed length arrays where the first // element is a Smi describing the OOB destination. Messages that do not // confirm to this layout are silently ignored. if (msg.IsArray()) { const Array& oob_msg = Array::Cast(msg); if (oob_msg.Length() > 0) { const Object& oob_tag = Object::Handle(zone, oob_msg.At(0)); if (oob_tag.IsSmi()) { switch (Smi::Cast(oob_tag).Value()) { case Message::kServiceOOBMsg: { #ifndef PRODUCT const Error& error = Error::Handle(Service::HandleIsolateMessage(I, oob_msg)); if (!error.IsNull()) { status = ProcessUnhandledException(error); } #else UNREACHABLE(); #endif break; } case Message::kIsolateLibOOBMsg: { const Error& error = Error::Handle(HandleLibMessage(oob_msg)); if (!error.IsNull()) { status = ProcessUnhandledException(error); } break; } #if defined(DEBUG) // Malformed OOB messages are silently ignored in release builds. default: { UNREACHABLE(); break; } #endif // defined(DEBUG) } } } } } else if (message->IsFinalizerInvocationRequest()) { const Object& msg_handler = Object::Handle( zone, DartLibraryCalls::HandleFinalizerMessage(FinalizerBase::Cast(msg))); if (msg_handler.IsError()) { status = ProcessUnhandledException(Error::Cast(msg_handler)); } else { // The handler closure which was used to successfully handle the message. } } else if (message->dest_port() == Message::kIllegalPort) { // Check whether this is a delayed OOB message which needed handling as // part of the regular message dispatch. All other messages are dropped on // the floor. if (msg.IsArray()) { const Array& msg_arr = Array::Cast(msg); if (msg_arr.Length() > 0) { const Object& oob_tag = Object::Handle(zone, msg_arr.At(0)); if (oob_tag.IsSmi() && (Smi::Cast(oob_tag).Value() == Message::kDelayedIsolateLibOOBMsg)) { const Error& error = Error::Handle(HandleLibMessage(msg_arr)); if (!error.IsNull()) { status = ProcessUnhandledException(error); } } } } } else { const Object& msg_handler = Object::Handle( zone, DartLibraryCalls::HandleMessage(message->dest_port(), msg)); if (msg_handler.IsError()) { status = ProcessUnhandledException(Error::Cast(msg_handler)); } else if (msg_handler.IsNull()) { // If the port has been closed then the message will be dropped at this // point. Make sure to post to the delivery failure port in that case. } else { // The handler closure which was used to successfully handle the message. } } return status; } #ifndef PRODUCT void IsolateMessageHandler::NotifyPauseOnStart() { if (Isolate::IsSystemIsolate(I)) { return; } if (Service::debug_stream.enabled() || FLAG_warn_on_pause_with_no_debugger) { StartIsolateScope start_isolate(I); StackZone zone(T); HandleScope handle_scope(T); ServiceEvent pause_event(I, ServiceEvent::kPauseStart); Service::HandleEvent(&pause_event); } else if (FLAG_trace_service) { OS::PrintErr("vm-service: Dropping event of type PauseStart (%s)\n", I->name()); } } void IsolateMessageHandler::NotifyPauseOnExit() { if (Isolate::IsSystemIsolate(I)) { return; } if (Service::debug_stream.enabled() || FLAG_warn_on_pause_with_no_debugger) { StartIsolateScope start_isolate(I); StackZone zone(T); HandleScope handle_scope(T); ServiceEvent pause_event(I, ServiceEvent::kPauseExit); Service::HandleEvent(&pause_event); } else if (FLAG_trace_service) { OS::PrintErr("vm-service: Dropping event of type PauseExit (%s)\n", I->name()); } } #endif // !PRODUCT #if defined(DEBUG) void IsolateMessageHandler::CheckAccess() const { ASSERT(isolate() == Isolate::Current()); } #endif static MessageHandler::MessageStatus StoreError(Thread* thread, const Error& error) { thread->set_sticky_error(error); if (error.IsUnwindError()) { const UnwindError& unwind = UnwindError::Cast(error); if (!unwind.is_user_initiated()) { return MessageHandler::kShutdown; } } return MessageHandler::kError; } MessageHandler::MessageStatus IsolateMessageHandler::ProcessUnhandledException( const Error& result) { if (FLAG_trace_isolates) { OS::PrintErr( "[!] Unhandled exception in %s:\n" " exception: %s\n", T->isolate()->name(), result.ToErrorCString()); } NoReloadScope no_reload(T); // Generate the error and stacktrace strings for the error message. const char* exception_cstr = nullptr; const char* stacktrace_cstr = nullptr; if (result.IsUnhandledException()) { Zone* zone = T->zone(); const UnhandledException& uhe = UnhandledException::Cast(result); const Instance& exception = Instance::Handle(zone, uhe.exception()); if (exception.ptr() == IG->object_store()->out_of_memory()) { exception_cstr = "Out of Memory"; // Cf. OutOfMemoryError.toString(). } else if (exception.ptr() == IG->object_store()->stack_overflow()) { exception_cstr = "Stack Overflow"; // Cf. StackOverflowError.toString(). } else { const Object& exception_str = Object::Handle(zone, DartLibraryCalls::ToString(exception)); if (!exception_str.IsString()) { exception_cstr = exception.ToCString(); } else { exception_cstr = exception_str.ToCString(); } } const Instance& stacktrace = Instance::Handle(zone, uhe.stacktrace()); stacktrace_cstr = stacktrace.ToCString(); } else { exception_cstr = result.ToErrorCString(); } if (result.IsUnwindError()) { // When unwinding we don't notify error listeners and we ignore // whether errors are fatal for the current isolate. return StoreError(T, result); } else { bool has_listener = I->NotifyErrorListeners(exception_cstr, stacktrace_cstr); if (I->ErrorsFatal()) { if (has_listener) { T->ClearStickyError(); } else { T->set_sticky_error(result); } #if !defined(PRODUCT) // Notify the debugger about specific unhandled exceptions which are // withheld when being thrown. Do this after setting the sticky error // so the isolate has an error set when paused with the unhandled // exception. if (result.IsUnhandledException()) { const UnhandledException& error = UnhandledException::Cast(result); InstancePtr exception = error.exception(); if ((exception == IG->object_store()->out_of_memory()) || (exception == IG->object_store()->stack_overflow())) { // We didn't notify the debugger when the stack was full. Do it now. I->debugger()->PauseException(Instance::Handle(exception)); } } #endif // !defined(PRODUCT) return kError; } } return kOK; } void IsolateGroup::FlagsInitialize(Dart_IsolateFlags* api_flags) { api_flags->version = DART_FLAGS_CURRENT_VERSION; #define INIT_FROM_FLAG(when, name, bitname, isolate_flag, flag) \ api_flags->isolate_flag = flag; BOOL_ISOLATE_GROUP_FLAG_LIST(INIT_FROM_FLAG) #undef INIT_FROM_FLAG api_flags->is_service_isolate = false; api_flags->is_kernel_isolate = false; api_flags->null_safety = true; } void IsolateGroup::FlagsCopyTo(Dart_IsolateFlags* api_flags) { api_flags->version = DART_FLAGS_CURRENT_VERSION; #define INIT_FROM_FIELD(when, name, bitname, isolate_flag, flag) \ api_flags->isolate_flag = name(); BOOL_ISOLATE_GROUP_FLAG_LIST(INIT_FROM_FIELD) #undef INIT_FROM_FIELD api_flags->is_service_isolate = false; api_flags->is_kernel_isolate = false; api_flags->null_safety = true; } void IsolateGroup::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) { #if defined(DART_PRECOMPILER) #define FLAG_FOR_PRECOMPILER(action) action #else #define FLAG_FOR_PRECOMPILER(action) #endif #if !defined(PRODUCT) #define FLAG_FOR_NONPRODUCT(action) action #else #define FLAG_FOR_NONPRODUCT(action) #endif #define FLAG_FOR_PRODUCT(action) action #define SET_FROM_FLAG(when, name, bitname, isolate_flag, flag) \ FLAG_FOR_##when(isolate_group_flags_ = bitname##Bit::update( \ api_flags.isolate_flag, isolate_group_flags_)); BOOL_ISOLATE_GROUP_FLAG_LIST(SET_FROM_FLAG) #undef FLAG_FOR_NONPRODUCT #undef FLAG_FOR_PRECOMPILER #undef FLAG_FOR_PRODUCT #undef SET_FROM_FLAG } void Isolate::FlagsInitialize(Dart_IsolateFlags* api_flags) { IsolateGroup::FlagsInitialize(api_flags); api_flags->version = DART_FLAGS_CURRENT_VERSION; #define INIT_FROM_FLAG(when, name, bitname, isolate_flag, flag) \ api_flags->isolate_flag = flag; BOOL_ISOLATE_FLAG_LIST(INIT_FROM_FLAG) #undef INIT_FROM_FLAG api_flags->is_service_isolate = false; api_flags->is_kernel_isolate = false; api_flags->null_safety = true; } void Isolate::FlagsCopyTo(Dart_IsolateFlags* api_flags) const { group()->FlagsCopyTo(api_flags); api_flags->version = DART_FLAGS_CURRENT_VERSION; #define INIT_FROM_FIELD(when, name, bitname, isolate_flag, flag) \ api_flags->isolate_flag = name(); BOOL_ISOLATE_FLAG_LIST(INIT_FROM_FIELD) #undef INIT_FROM_FIELD api_flags->is_service_isolate = false; api_flags->is_kernel_isolate = false; api_flags->null_safety = true; } void Isolate::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) { #if defined(DART_PRECOMPILER) #define FLAG_FOR_PRECOMPILER(action) action #else #define FLAG_FOR_PRECOMPILER(action) #endif #if !defined(PRODUCT) #define FLAG_FOR_NONPRODUCT(action) action #else #define FLAG_FOR_NONPRODUCT(action) #endif #define FLAG_FOR_PRODUCT(action) action #define SET_FROM_FLAG(when, name, bitname, isolate_flag, flag) \ FLAG_FOR_##when(isolate_flags_ = bitname##Bit::update( \ api_flags.isolate_flag, isolate_flags_)); BOOL_ISOLATE_FLAG_LIST(SET_FROM_FLAG) #undef FLAG_FOR_NONPRODUCT #undef FLAG_FOR_PRECOMPILER #undef FLAG_FOR_PRODUCT #undef SET_FROM_FLAG } #if defined(DEBUG) // static void BaseIsolate::AssertCurrent(BaseIsolate* isolate) { ASSERT(isolate == Isolate::Current()); } #endif // defined(DEBUG) #if defined(DEBUG) #define REUSABLE_HANDLE_SCOPE_INIT(object) \ reusable_##object##_handle_scope_active_(false), #else #define REUSABLE_HANDLE_SCOPE_INIT(object) #endif // defined(DEBUG) #define REUSABLE_HANDLE_INITIALIZERS(object) object##_handle_(nullptr), class LibraryPrefixMapTraits { public: static bool ReportStats() { return false; } static const char* Name() { return "LibraryPrefixMapTraits"; } static bool IsMatch(const Object& a, const Object& b) { if (!a.IsLibraryPrefix() || !b.IsLibraryPrefix()) { return false; } return a.ptr() == b.ptr(); } static uword Hash(const Object& obj) { auto& prefix = LibraryPrefix::Cast(obj); return String::Hash(prefix.name()); } }; // TODO(srdjan): Some Isolate monitors can be shared. Replace their usage with // that shared monitor. Isolate::Isolate(IsolateGroup* isolate_group, const Dart_IsolateFlags& api_flags) : BaseIsolate(), current_tag_(UserTag::null()), default_tag_(UserTag::null()), field_table_(new FieldTable(/*isolate=*/this)), finalizers_(GrowableObjectArray::null()), isolate_group_(isolate_group), isolate_object_store_(new IsolateObjectStore()), isolate_flags_(0), #if !defined(PRODUCT) last_resume_timestamp_(OS::GetCurrentTimeMillis()), vm_tag_counters_(), pending_service_extension_calls_(GrowableObjectArray::null()), registered_service_extension_handlers_(GrowableObjectArray::null()), service_id_zones_(nullptr), #define ISOLATE_METRIC_CONSTRUCTORS(type, variable, name, unit) \ metric_##variable##_(), ISOLATE_METRIC_LIST(ISOLATE_METRIC_CONSTRUCTORS) #undef ISOLATE_METRIC_CONSTRUCTORS #endif // !defined(PRODUCT) start_time_micros_(OS::GetCurrentMonotonicMicros()), message_notify_callback_(nullptr), on_shutdown_callback_(Isolate::ShutdownCallback()), on_cleanup_callback_(Isolate::CleanupCallback()), random_(), mutex_(), tag_table_(GrowableObjectArray::null()), sticky_error_(Error::null()), spawn_count_monitor_(), handler_info_cache_(), catch_entry_moves_cache_(), wake_pause_event_handler_count_(0), loaded_prefixes_set_storage_(nullptr) { FlagsCopyFrom(api_flags); SetErrorsFatal(true); // TODO(asiva): A Thread is not available here, need to figure out // how the vm_tag (kEmbedderTagId) can be set, these tags need to // move to the OSThread structure. set_user_tag(UserTags::kDefaultUserTag); } #undef REUSABLE_HANDLE_SCOPE_INIT #undef REUSABLE_HANDLE_INITIALIZERS Isolate::~Isolate() { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) // TODO(32796): Re-enable assertion. // RELEASE_ASSERT(program_reload_context_ == nullptr); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) #if !defined(PRODUCT) delete debugger_; debugger_ = nullptr; if (service_id_zones_ != nullptr) { for (intptr_t i = 0; i < service_id_zones_->length(); ++i) { delete service_id_zones_->At(i); } delete service_id_zones_; service_id_zones_ = nullptr; } delete pause_loop_monitor_; pause_loop_monitor_ = nullptr; #endif // !defined(PRODUCT) free(name_); delete field_table_; #if defined(USING_SIMULATOR) delete simulator_; #endif delete message_handler_; message_handler_ = nullptr; // Fail fast if we send messages to a dead isolate. ASSERT(deopt_context_ == nullptr); // No deopt in progress when isolate deleted. ASSERT(spawn_count_ == 0); // The [Thread] object should've been released on the last // `Thread::ExitIsolate()` call. ASSERT(scheduled_mutator_thread_ == nullptr); ASSERT(mutator_thread_ == nullptr); } void Isolate::InitVM() { create_group_callback_ = nullptr; initialize_callback_ = nullptr; shutdown_callback_ = nullptr; cleanup_callback_ = nullptr; cleanup_group_callback_ = nullptr; register_kernel_blob_callback_ = nullptr; unregister_kernel_blob_callback_ = nullptr; if (isolate_creation_monitor_ == nullptr) { isolate_creation_monitor_ = new Monitor(); } ASSERT(isolate_creation_monitor_ != nullptr); EnableIsolateCreation(); } Isolate* Isolate::InitIsolate(const char* name_prefix, IsolateGroup* isolate_group, const Dart_IsolateFlags& api_flags, bool is_vm_isolate) { Isolate* result = new Isolate(isolate_group, api_flags); result->set_is_vm_isolate(is_vm_isolate); result->BuildName(name_prefix); if (!is_vm_isolate) { // vm isolate object store is initialized later, after null instance // is created (in Dart::Init). // Non-vm isolates need to have isolate object store initialized is that // exit_listeners have to be null-initialized as they will be used if // we fail to create isolate below, have to do low level shutdown. ASSERT(result->group()->object_store() != nullptr); result->isolate_object_store()->Init(); } ASSERT(result != nullptr); #if !defined(PRODUCT) // Initialize metrics. #define ISOLATE_METRIC_INIT(type, variable, name, unit) \ result->metric_##variable##_.InitInstance(result, name, nullptr, \ Metric::unit); ISOLATE_METRIC_LIST(ISOLATE_METRIC_INIT); #undef ISOLATE_METRIC_INIT #endif // !defined(PRODUCT) // First we ensure we enter the isolate. This will ensure we're participating // in any safepointing requests from this point on. Other threads requesting a // safepoint operation will therefore wait until we've stopped. // // Though the [result] isolate is still in a state where no memory has been // allocated, which means it's safe to GC the isolate group until here. Thread::EnterIsolate(result); // Setup the isolate message handler. result->message_handler_ = new IsolateMessageHandler(result); result->set_main_port(PortMap::CreatePort(result->message_handler())); #if defined(DEBUG) // Verify that we are never reusing a live origin id. VerifyOriginId id_verifier(result->main_port()); Isolate::VisitIsolates(&id_verifier); #endif result->set_origin_id(result->main_port()); // Keep capability IDs less than 2^53 so web clients of the service // protocol can process it properly. // // See https://github.com/dart-lang/sdk/issues/53081. result->set_pause_capability(result->random()->NextJSInt()); result->set_terminate_capability(result->random()->NextJSInt()); #if !defined(PRODUCT) result->debugger_ = new Debugger(result); #endif // !defined(PRODUCT) // Now we register the isolate in the group. From this point on any GC would // traverse the isolate roots (before this point, the roots are only pointing // to vm-isolate objects, e.g. null) isolate_group->RegisterIsolate(result); if (api_flags.is_service_isolate) { ASSERT(!ServiceIsolate::Exists()); ServiceIsolate::SetServiceIsolate(result); #if !defined(DART_PRECOMPILED_RUNTIME) } else if (api_flags.is_kernel_isolate) { ASSERT(!KernelIsolate::Exists()); KernelIsolate::SetKernelIsolate(result); #endif // !defined(DART_PRECOMPILED_RUNTIME) } if (FLAG_trace_isolates) { if (name_prefix == nullptr || strcmp(name_prefix, "vm-isolate") != 0) { OS::PrintErr( "[+] Starting isolate:\n" "\tisolate: %s\n", result->name()); } } // Add to isolate list. Shutdown and delete the isolate on failure. if (!TryMarkIsolateReady(result)) { result->LowLevelShutdown(); Isolate::LowLevelCleanup(result); return nullptr; } return result; } Thread* Isolate::mutator_thread() const { ASSERT(thread_registry() != nullptr); return mutator_thread_; } ObjectPtr IsolateGroup::CallTagHandler(Dart_LibraryTag tag, const Object& arg1, const Object& arg2) { Thread* thread = Thread::Current(); Api::Scope api_scope(thread); Dart_Handle api_arg1 = Api::NewHandle(thread, arg1.ptr()); Dart_Handle api_arg2 = Api::NewHandle(thread, arg2.ptr()); Dart_Handle api_result; { TransitionVMToNative transition(thread); ASSERT(HasTagHandler()); api_result = library_tag_handler()(tag, api_arg1, api_arg2); } return Api::UnwrapHandle(api_result); } ObjectPtr Isolate::CallDeferredLoadHandler(intptr_t id) { Thread* thread = Thread::Current(); Api::Scope api_scope(thread); Dart_Handle api_result; { TransitionVMToNative transition(thread); RELEASE_ASSERT(HasDeferredLoadHandler()); api_result = group()->deferred_load_handler()(id); } return Api::UnwrapHandle(api_result); } void IsolateGroup::SetupImagePage(const uint8_t* image_buffer, bool is_executable) { Image image(image_buffer); heap()->SetupImagePage(image.object_start(), image.object_size(), is_executable); } void IsolateGroup::ScheduleInterrupts(uword interrupt_bits) { SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get()); for (Isolate* isolate : isolates_) { isolate->ScheduleInterrupts(interrupt_bits); } } void Isolate::ScheduleInterrupts(uword interrupt_bits) { // We take the threads lock here to ensure that the mutator thread does not // exit the isolate while we are trying to schedule interrupts on it. MonitorLocker ml(group()->thread_registry()->threads_lock()); Thread* mthread = mutator_thread(); if (mthread != nullptr) { mthread->ScheduleInterrupts(interrupt_bits); } } void Isolate::set_name(const char* name) { free(name_); name_ = Utils::StrDup(name); } int64_t IsolateGroup::UptimeMicros() const { return OS::GetCurrentMonotonicMicros() - start_time_micros_; } int64_t Isolate::UptimeMicros() const { return OS::GetCurrentMonotonicMicros() - start_time_micros_; } Dart_Port Isolate::origin_id() { MutexLocker ml(&origin_id_mutex_); return origin_id_; } void Isolate::set_origin_id(Dart_Port id) { MutexLocker ml(&origin_id_mutex_); ASSERT((id == main_port_ && origin_id_ == 0) || (origin_id_ == main_port_)); origin_id_ = id; } void Isolate::set_finalizers(const GrowableObjectArray& value) { finalizers_ = value.ptr(); } bool Isolate::IsPaused() const { #if defined(PRODUCT) return false; #else return (debugger_ != nullptr) && (debugger_->PauseEvent() != nullptr); #endif // !defined(PRODUCT) } ErrorPtr Isolate::PausePostRequest() { #if !defined(PRODUCT) if (debugger_ == nullptr) { return Error::null(); } ASSERT(!IsPaused()); const Error& error = Error::Handle(debugger_->PausePostRequest()); if (!error.IsNull()) { if (Thread::Current()->top_exit_frame_info() == 0) { return error.ptr(); } else { Exceptions::PropagateError(error); UNREACHABLE(); } } #endif return Error::null(); } void Isolate::BuildName(const char* name_prefix) { ASSERT(name_ == nullptr); if (name_prefix == nullptr) { name_ = OS::SCreate(nullptr, "isolate-%" Pd64 "", main_port()); } else { name_ = Utils::StrDup(name_prefix); } } #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) bool IsolateGroup::CanReload() { // We only call this method on the mutator thread. Normally the caller is // inside of the "reloadSources" service OOB message handler. Though // we also use it in the slow path of StackOverflowCheck in the artificial // --hot-reload-test-mode like flags. // // During reload itself we don't process OOB messages and don't execute Dart // code, so the caller should implicitly have a guarantee we're not reloading // already. RELEASE_ASSERT(!Thread::Current()->OwnsReloadSafepoint()); // We only allow reload to take place from the point on where the first // isolate within an isolate group has setup it's root library. From that // point on it's safe to perform hot-reload. auto thread = Thread::Current(); if (object_store()->root_library() == Library::null()) { return false; } // We only care about the current thread's [NoReloadScope]. If we're inside // one we cannot reload right now. Though if another isolate's mutator // thread is inside such a scope, the multi-isolate reload will simply wait // until it's out of that scope again. if (thread->no_reload_scope_depth_ != 0) { return false; } return !IsolateGroup::IsSystemIsolateGroup(this) && OSThread::Current()->HasStackHeadroom(64 * KB); } bool IsolateGroup::ReloadSources(JSONStream* js, bool force_reload, const char* root_script_url, const char* packages_url, bool dont_delete_reload_context) { ASSERT(!IsReloading()); // Ensure all isolates inside the isolate group are paused at a place where we // can safely do a reload. RELOAD_OPERATION_SCOPE(Thread::Current()); auto class_table = IsolateGroup::Current()->class_table(); std::shared_ptr group_reload_context( new IsolateGroupReloadContext(this, class_table, js)); group_reload_context_ = group_reload_context; SetHasAttemptedReload(true); program_reload_context_ = new ProgramReloadContext(group_reload_context_, this); const bool success = group_reload_context_->Reload(force_reload, root_script_url, packages_url, /*kernel_buffer=*/nullptr, /*kernel_buffer_size=*/0); if (!dont_delete_reload_context) { DeleteReloadContext(); } return success; } bool IsolateGroup::ReloadKernel(JSONStream* js, bool force_reload, const uint8_t* kernel_buffer, intptr_t kernel_buffer_size, bool dont_delete_reload_context) { ASSERT(!IsReloading()); // Ensure all isolates inside the isolate group are paused at a place where we // can safely do a reload. RELOAD_OPERATION_SCOPE(Thread::Current()); auto class_table = IsolateGroup::Current()->class_table(); std::shared_ptr group_reload_context( new IsolateGroupReloadContext(this, class_table, js)); group_reload_context_ = group_reload_context; SetHasAttemptedReload(true); program_reload_context_ = new ProgramReloadContext(group_reload_context_, this); const bool success = group_reload_context_->Reload( force_reload, /*root_script_url=*/nullptr, /*packages_url=*/nullptr, kernel_buffer, kernel_buffer_size); if (!dont_delete_reload_context) { DeleteReloadContext(); } return success; } void IsolateGroup::DeleteReloadContext() { GcSafepointOperationScope safepoint_scope(Thread::Current()); group_reload_context_.reset(); delete program_reload_context_; program_reload_context_ = nullptr; } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) const char* Isolate::MakeRunnable() { MutexLocker ml(&mutex_); // Check if we are in a valid state to make the isolate runnable. if (is_runnable() == true) { return "Isolate is already runnable"; } if (group()->object_store()->root_library() == Library::null()) { return "The embedder has to ensure there is a root library (e.g. by " "calling Dart_LoadScriptFromKernel )."; } MakeRunnableLocked(); return nullptr; } void Isolate::MakeRunnableLocked() { ASSERT(mutex_.IsOwnedByCurrentThread()); ASSERT(!is_runnable()); ASSERT(group()->object_store()->root_library() != Library::null()); // Set the isolate as runnable and if we are being spawned schedule // isolate on thread pool for execution. set_is_runnable(true); #ifndef PRODUCT if (!Isolate::IsSystemIsolate(this)) { if (FLAG_pause_isolates_on_unhandled_exceptions) { debugger()->SetExceptionPauseInfo(kPauseOnUnhandledExceptions); } } #endif // !PRODUCT #if defined(SUPPORT_TIMELINE) TimelineStream* stream = Timeline::GetIsolateStream(); ASSERT(stream != nullptr); TimelineEvent* event = stream->StartEvent(); if (event != nullptr) { event->Instant("Runnable"); event->Complete(); } #endif #ifndef PRODUCT if (!Isolate::IsSystemIsolate(this) && Service::isolate_stream.enabled()) { ServiceEvent runnableEvent(this, ServiceEvent::kIsolateRunnable); Service::HandleEvent(&runnableEvent, /* enter_safepoint */ false); } GetRunnableLatencyMetric()->set_value(UptimeMicros()); #endif // !PRODUCT } bool Isolate::VerifyPauseCapability(const Object& capability) const { return !capability.IsNull() && capability.IsCapability() && (pause_capability() == Capability::Cast(capability).Id()); } bool Isolate::VerifyTerminateCapability(const Object& capability) const { return !capability.IsNull() && capability.IsCapability() && (terminate_capability() == Capability::Cast(capability).Id()); } bool Isolate::AddResumeCapability(const Capability& capability) { // Ensure a limit for the number of resume capabilities remembered. const intptr_t kMaxResumeCapabilities = compiler::target::kSmiMax / (6 * kWordSize); const GrowableObjectArray& caps = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->resume_capabilities()); Capability& current = Capability::Handle(current_zone()); intptr_t insertion_index = -1; for (intptr_t i = 0; i < caps.Length(); i++) { current ^= caps.At(i); if (current.IsNull()) { if (insertion_index < 0) { insertion_index = i; } } else if (current.Id() == capability.Id()) { return false; } } if (insertion_index < 0) { if (caps.Length() >= kMaxResumeCapabilities) { // Cannot grow the array of resume capabilities beyond its max. Additional // pause requests are ignored. In practice will never happen as we will // run out of memory beforehand. return false; } caps.Add(capability); } else { caps.SetAt(insertion_index, capability); } return true; } bool Isolate::RemoveResumeCapability(const Capability& capability) { const GrowableObjectArray& caps = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->resume_capabilities()); Capability& current = Capability::Handle(current_zone()); for (intptr_t i = 0; i < caps.Length(); i++) { current ^= caps.At(i); if (!current.IsNull() && (current.Id() == capability.Id())) { // Remove the matching capability from the list. current = Capability::null(); caps.SetAt(i, current); return true; } } return false; } // TODO(iposva): Remove duplicated code and start using some hash based // structure instead of these linear lookups. void Isolate::AddExitListener(const SendPort& listener, const Instance& response) { // Ensure a limit for the number of listeners remembered. const intptr_t kMaxListeners = compiler::target::kSmiMax / (12 * kWordSize); const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->exit_listeners()); SendPort& current = SendPort::Handle(current_zone()); intptr_t insertion_index = -1; for (intptr_t i = 0; i < listeners.Length(); i += 2) { current ^= listeners.At(i); if (current.IsNull()) { if (insertion_index < 0) { insertion_index = i; } } else if (current.Id() == listener.Id()) { listeners.SetAt(i + 1, response); return; } } if (insertion_index < 0) { if (listeners.Length() >= kMaxListeners) { // Cannot grow the array of listeners beyond its max. Additional // listeners are ignored. In practice will never happen as we will // run out of memory beforehand. return; } listeners.Add(listener); listeners.Add(response); } else { listeners.SetAt(insertion_index, listener); listeners.SetAt(insertion_index + 1, response); } } void Isolate::RemoveExitListener(const SendPort& listener) { const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->exit_listeners()); SendPort& current = SendPort::Handle(current_zone()); for (intptr_t i = 0; i < listeners.Length(); i += 2) { current ^= listeners.At(i); if (!current.IsNull() && (current.Id() == listener.Id())) { // Remove the matching listener from the list. current = SendPort::null(); listeners.SetAt(i, current); listeners.SetAt(i + 1, Object::null_instance()); return; } } } void Isolate::NotifyExitListeners() { const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->exit_listeners()); if (listeners.IsNull()) return; SendPort& listener = SendPort::Handle(current_zone()); Instance& response = Instance::Handle(current_zone()); for (intptr_t i = 0; i < listeners.Length(); i += 2) { listener ^= listeners.At(i); if (!listener.IsNull()) { Dart_Port port_id = listener.Id(); response ^= listeners.At(i + 1); PortMap::PostMessage(SerializeMessage(port_id, response)); } } } void Isolate::AddErrorListener(const SendPort& listener) { // Ensure a limit for the number of listeners remembered. const intptr_t kMaxListeners = compiler::target::kSmiMax / (6 * kWordSize); const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->error_listeners()); SendPort& current = SendPort::Handle(current_zone()); intptr_t insertion_index = -1; for (intptr_t i = 0; i < listeners.Length(); i++) { current ^= listeners.At(i); if (current.IsNull()) { if (insertion_index < 0) { insertion_index = i; } } else if (current.Id() == listener.Id()) { return; } } if (insertion_index < 0) { if (listeners.Length() >= kMaxListeners) { // Cannot grow the array of listeners beyond its max. Additional // listeners are ignored. In practice will never happen as we will // run out of memory beforehand. return; } listeners.Add(listener); } else { listeners.SetAt(insertion_index, listener); } } void Isolate::RemoveErrorListener(const SendPort& listener) { const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->error_listeners()); SendPort& current = SendPort::Handle(current_zone()); for (intptr_t i = 0; i < listeners.Length(); i++) { current ^= listeners.At(i); if (!current.IsNull() && (current.Id() == listener.Id())) { // Remove the matching listener from the list. current = SendPort::null(); listeners.SetAt(i, current); return; } } } bool Isolate::NotifyErrorListeners(const char* message, const char* stacktrace) { const GrowableObjectArray& listeners = GrowableObjectArray::Handle( current_zone(), isolate_object_store()->error_listeners()); if (listeners.IsNull()) return false; Dart_CObject arr; Dart_CObject* arr_values[2]; arr.type = Dart_CObject_kArray; arr.value.as_array.length = 2; arr.value.as_array.values = arr_values; Dart_CObject msg; msg.type = Dart_CObject_kString; msg.value.as_string = const_cast(message); arr_values[0] = &msg; Dart_CObject stack; if (stacktrace == nullptr) { stack.type = Dart_CObject_kNull; } else { stack.type = Dart_CObject_kString; stack.value.as_string = const_cast(stacktrace); } arr_values[1] = &stack; SendPort& listener = SendPort::Handle(current_zone()); bool was_somebody_notified = false; for (intptr_t i = 0; i < listeners.Length(); i++) { listener ^= listeners.At(i); if (!listener.IsNull()) { Dart_Port port_id = listener.Id(); PortMap::PostMessage(SerializeMessage(current_zone(), port_id, &arr)); was_somebody_notified = true; } } return was_somebody_notified; } static void ShutdownIsolate(uword parameter) { Dart_EnterIsolate(reinterpret_cast(parameter)); Dart_ShutdownIsolate(); } void Isolate::SetStickyError(ErrorPtr sticky_error) { ASSERT( ((sticky_error_ == Error::null()) || (sticky_error == Error::null())) && (sticky_error != sticky_error_)); sticky_error_ = sticky_error; } void Isolate::Run() { message_handler()->Run(group()->thread_pool(), nullptr, ShutdownIsolate, reinterpret_cast(this)); } MessageHandler* Isolate::message_handler() const { return message_handler_; } void Isolate::RunAndCleanupFinalizersOnShutdown() { if (finalizers_ == GrowableObjectArray::null()) return; // Ensure we have a zone and handle scope so that we can call VM functions, // but we no longer allocate new heap objects. Thread* thread = Thread::Current(); StackZone stack_zone(thread); HandleScope handle_scope(thread); NoSafepointScope no_safepoint_scope; // Set live finalizers isolate to null, before deleting the message handler. const auto& finalizers = GrowableObjectArray::Handle(stack_zone.GetZone(), finalizers_); if (!finalizers.IsNull()) { const intptr_t num_finalizers = finalizers.Length(); auto& weak_reference = WeakReference::Handle(stack_zone.GetZone()); auto& finalizer = FinalizerBase::Handle(stack_zone.GetZone()); auto& current_entry = FinalizerEntry::Handle(stack_zone.GetZone()); auto& all_entries = Set::Handle(stack_zone.GetZone()); for (int i = 0; i < num_finalizers; i++) { weak_reference ^= finalizers.At(i); finalizer ^= weak_reference.target(); if (!finalizer.IsNull()) { if (finalizer.isolate() == this) { if (FLAG_trace_finalizers) { THR_Print("Isolate %p Setting finalizer %p isolate to null\n", this, finalizer.ptr()->untag()); } // Finalizer was not sent to another isolate with send and exit. finalizer.set_isolate(nullptr); } else { // TODO(http://dartbug.com/47777): Send and exit support. UNREACHABLE(); } if (finalizer.IsNativeFinalizer()) { // Immediately call native callback. const auto& native_finalizer = NativeFinalizer::Cast(finalizer); all_entries = finalizer.all_entries(); Set::Iterator iterator(all_entries); while (iterator.MoveNext()) { current_entry ^= iterator.CurrentKey(); native_finalizer.RunCallback(current_entry, "Isolate shutdown"); } } } } } } void Isolate::LowLevelShutdown() { // Ensure we have a zone and handle scope so that we can call VM functions, // but we no longer allocate new heap objects. Thread* thread = Thread::Current(); StackZone stack_zone(thread); HandleScope handle_scope(thread); NoSafepointScope no_safepoint_scope; // Notify exit listeners that this isolate is shutting down. if (group()->object_store() != nullptr) { const Error& error = Error::Handle(thread->sticky_error()); if (error.IsNull() || !error.IsUnwindError() || UnwindError::Cast(error).is_user_initiated()) { NotifyExitListeners(); } } // Close all the ports owned by this isolate. PortMap::ClosePorts(message_handler()); // Fail fast if anybody tries to post any more messages to this isolate. delete message_handler_; message_handler_ = nullptr; // Clean up any synchronous FFI callbacks registered with this isolate. Skip // if this isolate never registered any. if (ffi_callback_list_head_ != nullptr) { FfiCallbackMetadata::Instance()->DeleteAllCallbacks( &ffi_callback_list_head_); } #if !defined(PRODUCT) if (FLAG_dump_megamorphic_stats) { MegamorphicCacheTable::PrintSizes(thread); } if (FLAG_dump_symbol_stats) { Symbols::DumpStats(group()); } if (FLAG_trace_isolates) { group()->heap()->PrintSizes(); OS::PrintErr( "[-] Stopping isolate:\n" "\tisolate: %s\n", name()); } if (FLAG_print_metrics) { LogBlock lb; OS::PrintErr("Printing metrics for %s\n", name()); #define ISOLATE_GROUP_METRIC_PRINT(type, variable, name, unit) \ OS::PrintErr("%s\n", isolate_group_->Get##variable##Metric()->ToString()); ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_PRINT) #undef ISOLATE_GROUP_METRIC_PRINT #define ISOLATE_METRIC_PRINT(type, variable, name, unit) \ OS::PrintErr("%s\n", metric_##variable##_.ToString()); ISOLATE_METRIC_LIST(ISOLATE_METRIC_PRINT) #undef ISOLATE_METRIC_PRINT OS::PrintErr("\n"); } #endif // !defined(PRODUCT) } #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) void IsolateGroup::MaybeIncreaseReloadEveryNStackOverflowChecks() { if (FLAG_reload_every_back_off) { if (reload_every_n_stack_overflow_checks_ < 5000) { reload_every_n_stack_overflow_checks_ += 99; } else { const auto old_value = reload_every_n_stack_overflow_checks_; reload_every_n_stack_overflow_checks_ = old_value * old_value; } // Cap the value. if (reload_every_n_stack_overflow_checks_ > 1000000) { reload_every_n_stack_overflow_checks_ = 1000000; } } } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) void Isolate::set_forward_table_new(WeakTable* table) { std::unique_ptr value(table); forward_table_new_ = std::move(value); } void Isolate::set_forward_table_old(WeakTable* table) { std::unique_ptr value(table); forward_table_old_ = std::move(value); } void Isolate::Shutdown() { Thread* thread = Thread::Current(); ASSERT(this == thread->isolate()); // Don't allow anymore dart code to execution on this isolate. thread->ClearStackLimit(); { StackZone zone(thread); ServiceIsolate::SendIsolateShutdownMessage(); #if !defined(PRODUCT) HandleScope handle_scope(thread); debugger()->Shutdown(); Profiler::IsolateShutdown(thread); #endif } #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) if (FLAG_check_reloaded && is_runnable() && !Isolate::IsSystemIsolate(this)) { if (!group()->HasAttemptedReload()) { FATAL( "Isolate did not reload before exiting and " "--check-reloaded is enabled.\n"); } } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) // Then, proceed with low-level teardown. Isolate::UnMarkIsolateReady(this); // Ensure native finalizers are run before isolate has shutdown message is // sent. This way users can rely on the exit message that an isolate will not // run any Dart code anymore _and_ will not run any native finalizers anymore. RunAndCleanupFinalizersOnShutdown(); // Post message before LowLevelShutdown that sends onExit message. // This ensures that exit message comes last. if (bequest_ != nullptr) { auto beneficiary = bequest_->beneficiary(); auto handle = bequest_->TakeHandle(); PortMap::PostMessage( Message::New(beneficiary, handle, Message::kNormalPriority)); bequest_.reset(); } LowLevelShutdown(); // Now we can unregister from the thread, invoke cleanup callback, delete the // isolate (and possibly the isolate group). Isolate::LowLevelCleanup(this); } void Isolate::LowLevelCleanup(Isolate* isolate) { #if !defined(DART_PRECOMPILED_RUNTIME) if (isolate->is_kernel_isolate()) { KernelIsolate::SetKernelIsolate(nullptr); } #endif if (isolate->is_service_isolate()) { ServiceIsolate::SetServiceIsolate(nullptr); } // Cache these two fields, since they are no longer available after the // `delete isolate` further down. IsolateGroup* isolate_group = isolate->isolate_group_; Dart_IsolateCleanupCallback cleanup = isolate->on_cleanup_callback(); auto callback_data = isolate->init_callback_data_; // From this point on the isolate is no longer visited by GC (which is ok, // since we're just going to delete it anyway). isolate_group->UnregisterIsolate(isolate); // From this point on the isolate doesn't participate in safepointing // requests anymore. ASSERT(!Thread::Current()->HasActiveState()); Thread::ExitIsolate(/*isolate_shutdown=*/true); // Now it's safe to delete the isolate. delete isolate; // Run isolate specific cleanup function for all non "vm-isolate's. const bool is_vm_isolate = Dart::vm_isolate() == isolate; if (!is_vm_isolate) { if (cleanup != nullptr) { cleanup(isolate_group->embedder_data(), callback_data); } } const bool shutdown_group = isolate_group->UnregisterIsolateDecrementCount(); if (shutdown_group) { KernelIsolate::NotifyAboutIsolateGroupShutdown(isolate_group); if (!is_vm_isolate) { Thread::EnterIsolateGroupAsHelper(isolate_group, Thread::kUnknownTask, /*bypass_safepoint=*/false); #if !defined(DART_PRECOMPILED_RUNTIME) BackgroundCompiler::Stop(isolate_group); #endif // !defined(DART_PRECOMPILED_RUNTIME) // Finalize any weak persistent handles with a non-null referent with // isolate group still being available. FinalizeWeakPersistentHandlesVisitor visitor(isolate_group); isolate_group->api_state()->VisitWeakHandlesUnlocked(&visitor); Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false); } // The "vm-isolate" does not have a thread pool. ASSERT(is_vm_isolate == (isolate_group->thread_pool() == nullptr)); if (is_vm_isolate || !isolate_group->thread_pool()->CurrentThreadIsWorker()) { isolate_group->Shutdown(); } else { class ShutdownGroupTask : public ThreadPool::Task { public: explicit ShutdownGroupTask(IsolateGroup* isolate_group) : isolate_group_(isolate_group) {} virtual void Run() { isolate_group_->Shutdown(); } private: IsolateGroup* isolate_group_; }; // The current thread is running on the isolate group's thread pool. // So we cannot safely delete the isolate group (and it's pool). // Instead we will destroy the isolate group on the VM-global pool. if (FLAG_trace_shutdown) { OS::PrintErr("[+%" Pd64 "ms] : Scheduling shutdown on VM pool %s\n", Dart::UptimeMillis(), isolate_group->source()->name); } Dart::thread_pool()->Run(isolate_group); } } else { // TODO(dartbug.com/36097): An isolate just died. A significant amount of // memory might have become unreachable. We should evaluate how to best // inform the GC about this situation. } } Dart_InitializeIsolateCallback Isolate::initialize_callback_ = nullptr; Dart_IsolateGroupCreateCallback Isolate::create_group_callback_ = nullptr; Dart_IsolateShutdownCallback Isolate::shutdown_callback_ = nullptr; Dart_IsolateCleanupCallback Isolate::cleanup_callback_ = nullptr; Dart_IsolateGroupCleanupCallback Isolate::cleanup_group_callback_ = nullptr; Dart_RegisterKernelBlobCallback Isolate::register_kernel_blob_callback_ = nullptr; Dart_UnregisterKernelBlobCallback Isolate::unregister_kernel_blob_callback_ = nullptr; Random* IsolateGroup::isolate_group_random_ = nullptr; Monitor* Isolate::isolate_creation_monitor_ = nullptr; bool Isolate::creation_enabled_ = false; RwLock* IsolateGroup::isolate_groups_rwlock_ = nullptr; IntrusiveDList* IsolateGroup::isolate_groups_ = nullptr; void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { ASSERT(visitor != nullptr); // Visit objects in the field table. // N.B.: The heap snapshot writer requires visiting the field table first, so // that the pointer visitation order aligns with order of field name metadata. if (!visitor->trace_values_through_fields()) { field_table()->VisitObjectPointers(visitor); } // Visit objects in the isolate object store. if (isolate_object_store() != nullptr) { isolate_object_store()->VisitObjectPointers(visitor); } visitor->clear_gc_root_type(); // Visit the objects directly referenced from the isolate structure. visitor->VisitPointer(reinterpret_cast(¤t_tag_)); visitor->VisitPointer(reinterpret_cast(&default_tag_)); visitor->VisitPointer(reinterpret_cast(&tag_table_)); visitor->VisitPointer(reinterpret_cast(&sticky_error_)); visitor->VisitPointer(reinterpret_cast(&finalizers_)); #if !defined(PRODUCT) visitor->VisitPointer( reinterpret_cast(&pending_service_extension_calls_)); visitor->VisitPointer( reinterpret_cast(®istered_service_extension_handlers_)); #endif // !defined(PRODUCT) #if !defined(PRODUCT) // Visit objects in the debugger. if (debugger() != nullptr) { debugger()->VisitObjectPointers(visitor); } if (is_service_isolate()) { ServiceIsolate::VisitObjectPointers(visitor); } #endif // !defined(PRODUCT) #if !defined(DART_PRECOMPILED_RUNTIME) // Visit objects that are being used for deoptimization. if (deopt_context() != nullptr) { deopt_context()->VisitObjectPointers(visitor); } #endif // !defined(DART_PRECOMPILED_RUNTIME) visitor->VisitPointer( reinterpret_cast(&loaded_prefixes_set_storage_)); if (pointers_to_verify_at_exit_.length() != 0) { visitor->VisitPointers(&pointers_to_verify_at_exit_[0], pointers_to_verify_at_exit_.length()); } } void Isolate::VisitStackPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { if (mutator_thread_ != nullptr) { mutator_thread_->VisitObjectPointers(visitor, validate_frames); } } void IsolateGroup::ReleaseStoreBuffers() { thread_registry()->ReleaseStoreBuffers(); } void IsolateGroup::FlushMarkingStacks() { thread_registry()->FlushMarkingStacks(); } void Isolate::RememberLiveTemporaries() { if (mutator_thread_ != nullptr) { mutator_thread_->RememberLiveTemporaries(); } } void Isolate::DeferredMarkLiveTemporaries() { if (mutator_thread_ != nullptr) { mutator_thread_->DeferredMarkLiveTemporaries(); } } void Isolate::init_loaded_prefixes_set_storage() { ASSERT(loaded_prefixes_set_storage_ == nullptr); loaded_prefixes_set_storage_ = HashTables::New >(4); } bool Isolate::IsPrefixLoaded(const LibraryPrefix& prefix) const { UnorderedHashSet loaded_prefixes_set( loaded_prefixes_set_storage_); bool result = loaded_prefixes_set.GetOrNull(prefix) != Object::null(); loaded_prefixes_set.Release(); return result; } void Isolate::SetPrefixIsLoaded(const LibraryPrefix& prefix) { UnorderedHashSet loaded_prefixes_set( loaded_prefixes_set_storage_); loaded_prefixes_set.InsertOrGet(prefix); loaded_prefixes_set_storage_ = loaded_prefixes_set.Release().ptr(); } void IsolateGroup::EnableIncrementalBarrier( MarkingStack* old_marking_stack, MarkingStack* new_marking_stack, MarkingStack* deferred_marking_stack) { ASSERT(old_marking_stack_ == nullptr); old_marking_stack_ = old_marking_stack; ASSERT(new_marking_stack_ == nullptr); new_marking_stack_ = new_marking_stack; ASSERT(deferred_marking_stack_ == nullptr); deferred_marking_stack_ = deferred_marking_stack; thread_registry()->AcquireMarkingStacks(); ASSERT(Thread::Current()->is_marking()); } void IsolateGroup::DisableIncrementalBarrier() { thread_registry()->ReleaseMarkingStacks(); ASSERT(old_marking_stack_ != nullptr); old_marking_stack_ = nullptr; ASSERT(new_marking_stack_ != nullptr); new_marking_stack_ = nullptr; ASSERT(deferred_marking_stack_ != nullptr); deferred_marking_stack_ = nullptr; } void IsolateGroup::ForEachIsolate( std::function function, bool at_safepoint) { auto thread = Thread::Current(); if (at_safepoint) { ASSERT(thread->OwnsSafepoint() || (thread->task_kind() == Thread::kMutatorTask) || (thread->task_kind() == Thread::kMarkerTask) || (thread->task_kind() == Thread::kCompactorTask) || (thread->task_kind() == Thread::kScavengerTask) || (thread->task_kind() == Thread::kIncrementalCompactorTask)); for (Isolate* isolate : isolates_) { function(isolate); } return; } if (thread != nullptr && thread->OwnsSafepoint()) { for (Isolate* isolate : isolates_) { function(isolate); } return; } SafepointReadRwLocker ml(thread, isolates_lock_.get()); for (Isolate* isolate : isolates_) { function(isolate); } } Isolate* IsolateGroup::FirstIsolate() const { SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get()); return FirstIsolateLocked(); } Isolate* IsolateGroup::FirstIsolateLocked() const { return isolates_.IsEmpty() ? nullptr : isolates_.First(); } void IsolateGroup::RunWithStoppedMutatorsCallable( Callable* single_current_mutator, Callable* otherwise, bool use_force_growth_in_otherwise) { auto thread = Thread::Current(); StoppedMutatorsScope stopped_mutators_scope(thread); if (thread->OwnsSafepoint()) { RELEASE_ASSERT(thread->OwnsSafepoint()); single_current_mutator->Call(); return; } { SafepointReadRwLocker ml(thread, isolates_lock_.get()); if (thread->IsDartMutatorThread() && ContainsOnlyOneIsolate()) { single_current_mutator->Call(); return; } } // We use the more strict safepoint operation scope here (which ensures that // all other threads, including auxiliary threads are at a safepoint), even // though we only need to ensure that the mutator threads are stopped. if (use_force_growth_in_otherwise) { ForceGrowthSafepointOperationScope safepoint_scope( thread, SafepointLevel::kGCAndDeopt); otherwise->Call(); } else { DeoptSafepointOperationScope safepoint_scope(thread); otherwise->Call(); } } void IsolateGroup::VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { VisitSharedPointers(visitor); for (Isolate* isolate : isolates_) { isolate->VisitObjectPointers(visitor, validate_frames); } VisitStackPointers(visitor, validate_frames); } void IsolateGroup::VisitSharedPointers(ObjectPointerVisitor* visitor) { // Visit objects in the class table. class_table()->VisitObjectPointers(visitor); if (heap_walk_class_table() != class_table()) { heap_walk_class_table()->VisitObjectPointers(visitor); } api_state()->VisitObjectPointersUnlocked(visitor); // Visit objects in the object store. if (object_store() != nullptr) { object_store()->VisitObjectPointers(visitor); } visitor->VisitPointer(reinterpret_cast(&saved_unlinked_calls_)); initial_field_table()->VisitObjectPointers(visitor); shared_initial_field_table()->VisitObjectPointers(visitor); shared_field_table()->VisitObjectPointers(visitor); // Visit the boxed_field_list_. // 'boxed_field_list_' access via mutator and background compilation threads // is guarded with a monitor. This means that we can visit it only // when at safepoint or the field_list_mutex_ lock has been taken. visitor->VisitPointer(reinterpret_cast(&boxed_field_list_)); NOT_IN_PRECOMPILED(background_compiler()->VisitPointers(visitor)); #if !defined(PRODUCT) if (debugger() != nullptr) { debugger()->VisitObjectPointers(visitor); } #endif #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) // Visit objects that are being used for isolate reload. if (program_reload_context() != nullptr) { program_reload_context()->VisitObjectPointers(visitor); program_reload_context()->group_reload_context()->VisitObjectPointers( visitor); } #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) if (source()->loaded_blobs_ != nullptr) { visitor->VisitPointer( reinterpret_cast(&(source()->loaded_blobs_))); } if (become() != nullptr) { become()->VisitObjectPointers(visitor); } } void IsolateGroup::VisitStackPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames) { visitor->set_gc_root_type("stack"); // Visit objects in all threads (e.g. Dart stack, handles in zones), except // for the mutator threads themselves. thread_registry()->VisitObjectPointers(this, visitor, validate_frames); for (Isolate* isolate : isolates_) { // Visit mutator thread, even if the isolate isn't entered/scheduled // (there might be live API handles to visit). isolate->VisitStackPointers(visitor, validate_frames); } visitor->clear_gc_root_type(); } void IsolateGroup::VisitPointersInAllServiceIdZones( ObjectPointerVisitor& visitor) { #if !defined(PRODUCT) for (Isolate* isolate : isolates_) { for (intptr_t i = 0; i < isolate->NumServiceIdZones(); ++i) { isolate->GetServiceIdZone(i)->VisitPointers(visitor); } } #endif // !defined(PRODUCT) } void IsolateGroup::VisitWeakPersistentHandles(HandleVisitor* visitor) { api_state()->VisitWeakHandlesUnlocked(visitor); } void IsolateGroup::DeferredMarkLiveTemporaries() { ForEachIsolate( [&](Isolate* isolate) { isolate->DeferredMarkLiveTemporaries(); }, /*at_safepoint=*/true); } void IsolateGroup::RememberLiveTemporaries() { ForEachIsolate([&](Isolate* isolate) { isolate->RememberLiveTemporaries(); }, /*at_safepoint=*/true); } #if !defined(PRODUCT) RingServiceIdZone& Isolate::AddServiceIdZone( ObjectIdRing::BackingBufferKind backing_buffer_kind, ObjectIdRing::IdPolicy id_assignment_policy, int32_t capacity) { EnsureDefaultServiceIdZone(); switch (backing_buffer_kind) { case ObjectIdRing::BackingBufferKind::kRing: service_id_zones_->Add(new RingServiceIdZone( service_id_zones_->length(), id_assignment_policy, capacity)); return *service_id_zones_->Last(); default: UNREACHABLE(); } } void Isolate::DeleteServiceIdZone(int32_t id) { ASSERT(service_id_zones_ != nullptr); ASSERT(id < service_id_zones_->length()); delete service_id_zones_->At(id); (*service_id_zones_)[id] = nullptr; } RingServiceIdZone& Isolate::EnsureDefaultServiceIdZone() { if (service_id_zones_ == nullptr) { service_id_zones_ = new MallocGrowableArray(); } if (service_id_zones_->is_empty()) { service_id_zones_->Add( new RingServiceIdZone(0, ObjectIdRing::IdPolicy::kAllocateId, RingServiceIdZone::kDefaultCapacity)); } return *service_id_zones_->At(0); } RingServiceIdZone* Isolate::GetServiceIdZone(intptr_t zone_id) const { if (service_id_zones_ == nullptr || service_id_zones_->length() <= zone_id) { return nullptr; } return service_id_zones_->At(zone_id); } intptr_t Isolate::NumServiceIdZones() const { if (service_id_zones_ == nullptr) { return 0; } return service_id_zones_->length(); } static const char* ExceptionPauseInfoToServiceEnum(Dart_ExceptionPauseInfo pi) { switch (pi) { case kPauseOnAllExceptions: return "All"; case kNoPauseOnExceptions: return "None"; case kPauseOnUnhandledExceptions: return "Unhandled"; default: UNIMPLEMENTED(); return nullptr; } } static ServiceEvent IsolatePauseEvent(Isolate* isolate) { if (!isolate->is_runnable()) { // Isolate is not yet runnable. ASSERT((isolate->debugger() == nullptr) || (isolate->debugger()->PauseEvent() == nullptr)); return ServiceEvent(isolate, ServiceEvent::kNone); } else if (isolate->message_handler()->should_pause_on_start()) { if (isolate->message_handler()->is_paused_on_start()) { ASSERT((isolate->debugger() == nullptr) || (isolate->debugger()->PauseEvent() == nullptr)); return ServiceEvent(isolate, ServiceEvent::kPauseStart); } else { // Isolate is runnable but not paused on start. // Some service clients get confused if they see: // NotRunnable -> Runnable -> PausedAtStart // Treat Runnable+ShouldPauseOnStart as NotRunnable so they see: // NonRunnable -> PausedAtStart // The should_pause_on_start flag is set to false after resume. ASSERT((isolate->debugger() == nullptr) || (isolate->debugger()->PauseEvent() == nullptr)); return ServiceEvent(isolate, ServiceEvent::kNone); } } else if (isolate->message_handler()->is_paused_on_exit() && ((isolate->debugger() == nullptr) || (isolate->debugger()->PauseEvent() == nullptr))) { return ServiceEvent(isolate, ServiceEvent::kPauseExit); } else if ((isolate->debugger() != nullptr) && (isolate->debugger()->PauseEvent() != nullptr) && !isolate->ResumeRequest()) { return *(isolate->debugger()->PauseEvent()); } else { ServiceEvent pause_event(isolate, ServiceEvent::kResume); #if !defined(DART_PRECOMPILED_RUNTIME) if (isolate->debugger() != nullptr) { // TODO(turnidge): Don't compute a full stack trace. DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); if (stack->Length() > 0) { pause_event.set_top_frame(stack->FrameAt(0)); } } #endif return pause_event; } } void Isolate::PrintJSON(JSONStream* stream, bool ref) { JSONObject jsobj(stream); jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate")); jsobj.AddServiceId(ISOLATE_SERVICE_ID_FORMAT_STRING, static_cast(main_port())); jsobj.AddProperty("name", name()); jsobj.AddPropertyF("number", "%" Pd64 "", static_cast(main_port())); jsobj.AddProperty("isSystemIsolate", is_system_isolate()); jsobj.AddPropertyF("isolateGroupId", ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, group()->id()); if (ref) { return; } jsobj.AddPropertyF("_originNumber", "%" Pd64 "", static_cast(origin_id())); int64_t uptime_millis = UptimeMicros() / kMicrosecondsPerMillisecond; int64_t start_time = OS::GetCurrentTimeMillis() - uptime_millis; jsobj.AddPropertyTimeMillis("startTime", start_time); { JSONObject jsheap(&jsobj, "_heaps"); group()->heap()->PrintToJSONObject(Heap::kNew, &jsheap); group()->heap()->PrintToJSONObject(Heap::kOld, &jsheap); } { // Stringification macros // See https://gcc.gnu.org/onlinedocs/gcc-4.8.5/cpp/Stringification.html #define TO_STRING(s) STR(s) #define STR(s) #s #define ADD_ISOLATE_FLAGS(when, name, bitname, isolate_flag_name, flag_name) \ { \ JSONObject jsflag(&jsflags); \ jsflag.AddProperty("name", TO_STRING(name)); \ jsflag.AddProperty("valueAsString", name() ? "true" : "false"); \ } JSONArray jsflags(&jsobj, "isolateFlags"); BOOL_ISOLATE_FLAG_LIST(ADD_ISOLATE_FLAGS) #undef ADD_ISOLATE_FLAGS #undef TO_STRING #undef STR } jsobj.AddProperty("runnable", is_runnable()); jsobj.AddProperty("livePorts", open_ports_keepalive_); jsobj.AddProperty("pauseOnExit", message_handler()->should_pause_on_exit()); #if !defined(DART_PRECOMPILED_RUNTIME) jsobj.AddProperty("_isReloading", group()->IsReloading()); #endif // !defined(DART_PRECOMPILED_RUNTIME) ServiceEvent pause_event = IsolatePauseEvent(this); jsobj.AddProperty("pauseEvent", &pause_event); const Library& lib = Library::Handle(group()->object_store()->root_library()); if (!lib.IsNull()) { jsobj.AddProperty("rootLib", lib); } if (FLAG_profiler) { JSONObject tagCounters(&jsobj, "_tagCounters"); vm_tag_counters()->PrintToJSONObject(&tagCounters); } if (Thread::Current()->sticky_error() != Object::null()) { Error& error = Error::Handle(Thread::Current()->sticky_error()); ASSERT(!error.IsNull()); jsobj.AddProperty("error", error, false); } else if (sticky_error() != Object::null()) { Error& error = Error::Handle(sticky_error()); ASSERT(!error.IsNull()); jsobj.AddProperty("error", error, false); } { const GrowableObjectArray& libs = GrowableObjectArray::Handle(group()->object_store()->libraries()); intptr_t num_libs = libs.Length(); Library& lib = Library::Handle(); JSONArray lib_array(&jsobj, "libraries"); for (intptr_t i = 0; i < num_libs; i++) { lib ^= libs.At(i); ASSERT(!lib.IsNull()); lib_array.AddValue(lib); } } { JSONArray breakpoints(&jsobj, "breakpoints"); if (debugger() != nullptr) { debugger()->PrintBreakpointsToJSONArray(&breakpoints); } } Dart_ExceptionPauseInfo pause_info = (debugger() != nullptr) ? debugger()->GetExceptionPauseInfo() : kNoPauseOnExceptions; jsobj.AddProperty("exceptionPauseMode", ExceptionPauseInfoToServiceEnum(pause_info)); if (debugger() != nullptr) { JSONObject settings(&jsobj, "_debuggerSettings"); debugger()->PrintSettingsToJSONObject(&settings); } { GrowableObjectArray& handlers = GrowableObjectArray::Handle(registered_service_extension_handlers()); if (!handlers.IsNull()) { JSONArray extensions(&jsobj, "extensionRPCs"); String& handler_name = String::Handle(); for (intptr_t i = 0; i < handlers.Length(); i += kRegisteredEntrySize) { handler_name ^= handlers.At(i + kRegisteredNameIndex); extensions.AddValue(handler_name.ToCString()); } } } { JSONObject isolate_group(&jsobj, "isolate_group"); group()->PrintToJSONObject(&isolate_group, /*ref=*/true); } } void Isolate::PrintMemoryUsageJSON(JSONStream* stream) { group()->heap()->PrintMemoryUsageJSON(stream); } void Isolate::PrintPauseEventJSON(JSONStream* stream) { IsolatePauseEvent(this).PrintJSON(stream); } #endif // !defined(PRODUCT) void Isolate::set_tag_table(const GrowableObjectArray& value) { tag_table_ = value.ptr(); } void Isolate::set_current_tag(const UserTag& tag) { uword user_tag = tag.tag(); ASSERT(user_tag < kUwordMax); set_user_tag(user_tag); current_tag_ = tag.ptr(); } void Isolate::set_default_tag(const UserTag& tag) { default_tag_ = tag.ptr(); } ErrorPtr Isolate::StealStickyError() { NoSafepointScope no_safepoint; ErrorPtr return_value = sticky_error_; sticky_error_ = Error::null(); return return_value; } #if !defined(PRODUCT) void Isolate::set_pending_service_extension_calls( const GrowableObjectArray& value) { pending_service_extension_calls_ = value.ptr(); } void Isolate::set_registered_service_extension_handlers( const GrowableObjectArray& value) { registered_service_extension_handlers_ = value.ptr(); } ErrorPtr Isolate::InvokePendingServiceExtensionCalls() { GrowableObjectArray& calls = GrowableObjectArray::Handle(GetAndClearPendingServiceExtensionCalls()); if (calls.IsNull()) { return Error::null(); } // Grab run function. const Library& developer_lib = Library::Handle(Library::DeveloperLibrary()); ASSERT(!developer_lib.IsNull()); const Function& run_extension = Function::Handle( developer_lib.LookupFunctionAllowPrivate(Symbols::_runExtension())); ASSERT(!run_extension.IsNull()); const Array& arguments = Array::Handle(Array::New(kPendingEntrySize + 1, Heap::kNew)); Object& result = Object::Handle(); String& method_name = String::Handle(); Instance& closure = Instance::Handle(); Array& parameter_keys = Array::Handle(); Array& parameter_values = Array::Handle(); Instance& reply_port = Instance::Handle(); Instance& id = Instance::Handle(); for (intptr_t i = 0; i < calls.Length(); i += kPendingEntrySize) { // Grab arguments for call. closure ^= calls.At(i + kPendingHandlerIndex); ASSERT(!closure.IsNull()); arguments.SetAt(kPendingHandlerIndex, closure); method_name ^= calls.At(i + kPendingMethodNameIndex); ASSERT(!method_name.IsNull()); arguments.SetAt(kPendingMethodNameIndex, method_name); parameter_keys ^= calls.At(i + kPendingKeysIndex); ASSERT(!parameter_keys.IsNull()); arguments.SetAt(kPendingKeysIndex, parameter_keys); parameter_values ^= calls.At(i + kPendingValuesIndex); ASSERT(!parameter_values.IsNull()); arguments.SetAt(kPendingValuesIndex, parameter_values); reply_port ^= calls.At(i + kPendingReplyPortIndex); ASSERT(!reply_port.IsNull()); arguments.SetAt(kPendingReplyPortIndex, reply_port); id ^= calls.At(i + kPendingIdIndex); arguments.SetAt(kPendingIdIndex, id); arguments.SetAt(kPendingEntrySize, Bool::Get(FLAG_trace_service)); if (FLAG_trace_service) { OS::PrintErr("[+%" Pd64 "ms] Isolate %s invoking _runExtension for %s\n", Dart::UptimeMillis(), name(), method_name.ToCString()); } result = DartEntry::InvokeFunction(run_extension, arguments); if (FLAG_trace_service) { OS::PrintErr("[+%" Pd64 "ms] Isolate %s _runExtension complete for %s\n", Dart::UptimeMillis(), name(), method_name.ToCString()); } // Propagate the error. if (result.IsError()) { // Remaining service extension calls are dropped. if (!result.IsUnwindError()) { // Send error back over the protocol. Service::PostError(method_name, parameter_keys, parameter_values, reply_port, id, Error::Cast(result)); } return Error::Cast(result).ptr(); } // Drain the microtask queue. result = DartLibraryCalls::DrainMicrotaskQueue(); // Propagate the error. if (result.IsError()) { // Remaining service extension calls are dropped. return Error::Cast(result).ptr(); } } return Error::null(); } GrowableObjectArrayPtr Isolate::GetAndClearPendingServiceExtensionCalls() { GrowableObjectArrayPtr r = pending_service_extension_calls_; pending_service_extension_calls_ = GrowableObjectArray::null(); return r; } void Isolate::AppendServiceExtensionCall(const Instance& closure, const String& method_name, const Array& parameter_keys, const Array& parameter_values, const Instance& reply_port, const Instance& id) { if (FLAG_trace_service) { OS::PrintErr("[+%" Pd64 "ms] Isolate %s ENQUEUING request for extension %s\n", Dart::UptimeMillis(), name(), method_name.ToCString()); } GrowableObjectArray& calls = GrowableObjectArray::Handle(pending_service_extension_calls()); bool schedule_drain = false; if (calls.IsNull()) { calls = GrowableObjectArray::New(); ASSERT(!calls.IsNull()); set_pending_service_extension_calls(calls); schedule_drain = true; } ASSERT(kPendingHandlerIndex == 0); calls.Add(closure); ASSERT(kPendingMethodNameIndex == 1); calls.Add(method_name); ASSERT(kPendingKeysIndex == 2); calls.Add(parameter_keys); ASSERT(kPendingValuesIndex == 3); calls.Add(parameter_values); ASSERT(kPendingReplyPortIndex == 4); calls.Add(reply_port); ASSERT(kPendingIdIndex == 5); calls.Add(id); if (schedule_drain) { const Array& msg = Array::Handle(Array::New(3)); Object& element = Object::Handle(); element = Smi::New(Message::kIsolateLibOOBMsg); msg.SetAt(0, element); element = Smi::New(Isolate::kDrainServiceExtensionsMsg); msg.SetAt(1, element); element = Smi::New(Isolate::kBeforeNextEventAction); msg.SetAt(2, element); std::unique_ptr message = WriteMessage( /* same_group */ false, msg, main_port(), Message::kOOBPriority); bool posted = PortMap::PostMessage(std::move(message)); ASSERT(posted); } } // This function is written in C++ and not Dart because we must do this // operation atomically in the face of random OOB messages. Do not port // to Dart code unless you can ensure that the operations will can be // done atomically. void Isolate::RegisterServiceExtensionHandler(const String& name, const Instance& closure) { // Don't allow for service extensions to be registered for internal isolates. if (Isolate::IsVMInternalIsolate(this)) { return; } GrowableObjectArray& handlers = GrowableObjectArray::Handle(registered_service_extension_handlers()); if (handlers.IsNull()) { handlers = GrowableObjectArray::New(Heap::kOld); set_registered_service_extension_handlers(handlers); } #if defined(DEBUG) { // Sanity check. const Instance& existing_handler = Instance::Handle(LookupServiceExtensionHandler(name)); ASSERT(existing_handler.IsNull()); } #endif ASSERT(kRegisteredNameIndex == 0); handlers.Add(name, Heap::kOld); ASSERT(kRegisteredHandlerIndex == 1); handlers.Add(closure, Heap::kOld); { // Fire off an event. ServiceEvent event(this, ServiceEvent::kServiceExtensionAdded); event.set_extension_rpc(&name); Service::HandleEvent(&event); } } // This function is written in C++ and not Dart because we must do this // operation atomically in the face of random OOB messages. Do not port // to Dart code unless you can ensure that the operations will can be // done atomically. InstancePtr Isolate::LookupServiceExtensionHandler(const String& name) { const GrowableObjectArray& handlers = GrowableObjectArray::Handle(registered_service_extension_handlers()); if (handlers.IsNull()) { return Instance::null(); } String& handler_name = String::Handle(); for (intptr_t i = 0; i < handlers.Length(); i += kRegisteredEntrySize) { handler_name ^= handlers.At(i + kRegisteredNameIndex); ASSERT(!handler_name.IsNull()); if (handler_name.Equals(name)) { return Instance::RawCast(handlers.At(i + kRegisteredHandlerIndex)); } } return Instance::null(); } void Isolate::WakePauseEventHandler(Dart_Isolate isolate) { Isolate* iso = reinterpret_cast(isolate); MonitorLocker ml(iso->pause_loop_monitor_); ml.Notify(); Dart_MessageNotifyCallback current_notify_callback = iso->message_notify_callback(); // It is possible that WakePauseEventHandler was replaced by original callback // while waiting for pause_loop_monitor_. In that case PauseEventHandler // is no longer running and the original callback needs to be invoked instead // of incrementing wake_pause_event_handler_count_. if (current_notify_callback != Isolate::WakePauseEventHandler) { if (current_notify_callback != nullptr) { current_notify_callback(isolate); } } else { ++iso->wake_pause_event_handler_count_; } } void Isolate::PauseEventHandler() { // We are stealing a pause event (like a breakpoint) from the // embedder. We don't know what kind of thread we are on -- it // could be from our thread pool or it could be a thread from the // embedder. Sit on the current thread handling service events // until we are told to resume. if (pause_loop_monitor_ == nullptr) { pause_loop_monitor_ = new Monitor(); } Dart_EnterScope(); MonitorLocker ml(pause_loop_monitor_, false); Dart_MessageNotifyCallback saved_notify_callback = message_notify_callback(); ASSERT(wake_pause_event_handler_count_ == 0); set_message_notify_callback(Isolate::WakePauseEventHandler); #if !defined(DART_PRECOMPILED_RUNTIME) const bool had_program_reload_context = group()->program_reload_context() != nullptr; const int64_t start_time_micros = !had_program_reload_context ? 0 : group() ->program_reload_context() ->group_reload_context() ->start_time_micros(); #endif // !defined(DART_PRECOMPILED_RUNTIME) bool resume = false; while (true) { // Handle all available vm service messages, up to a resume // request. while (!resume && Dart_HasServiceMessages()) { ml.Exit(); resume = Dart_HandleServiceMessages(); ml.Enter(); } if (resume) { break; } #if !defined(DART_PRECOMPILED_RUNTIME) if (had_program_reload_context && (group()->program_reload_context() == nullptr)) { if (FLAG_trace_reload) { const int64_t reload_time_micros = OS::GetCurrentMonotonicMicros() - start_time_micros; double reload_millis = MicrosecondsToMilliseconds(reload_time_micros); OS::PrintErr("Reloading has finished! (%.2f ms)\n", reload_millis); } break; } #endif // !defined(DART_PRECOMPILED_RUNTIME) // Wait for more service messages. Monitor::WaitResult res = ml.Wait(); ASSERT(res == Monitor::kNotified); } // If any non-service messages came in, we need to notify the registered // message notify callback to check for unhandled messages. Otherwise, events // may be left unhandled until the next event comes in. See // https://github.com/dart-lang/sdk/issues/37312. if (saved_notify_callback != nullptr) { while (wake_pause_event_handler_count_ > 0) { saved_notify_callback(Api::CastIsolate(this)); --wake_pause_event_handler_count_; } } else { wake_pause_event_handler_count_ = 0; } set_message_notify_callback(saved_notify_callback); Dart_ExitScope(); } #endif // !defined(PRODUCT) void Isolate::VisitIsolates(IsolateVisitor* visitor) { if (visitor == nullptr) { return; } IsolateGroup::ForEach([&](IsolateGroup* group) { group->ForEachIsolate( [&](Isolate* isolate) { visitor->VisitIsolate(isolate); }); }); } intptr_t Isolate::IsolateListLength() { intptr_t count = 0; IsolateGroup::ForEach([&](IsolateGroup* group) { group->ForEachIsolate([&](Isolate* isolate) { count++; }); }); return count; } Isolate* Isolate::LookupIsolateByPort(Dart_Port port) { Isolate* match = nullptr; IsolateGroup::ForEach([&](IsolateGroup* group) { group->ForEachIsolate([&](Isolate* isolate) { if (isolate->main_port() == port) { match = isolate; } }); }); return match; } std::unique_ptr Isolate::LookupIsolateNameByPort(Dart_Port port) { MonitorLocker ml(isolate_creation_monitor_); std::unique_ptr result; IsolateGroup::ForEach([&](IsolateGroup* group) { group->ForEachIsolate([&](Isolate* isolate) { if (isolate->main_port() == port) { const size_t len = strlen(isolate->name()) + 1; result = std::unique_ptr(new char[len]); strncpy(result.get(), isolate->name(), len); } }); }); return result; } bool Isolate::TryMarkIsolateReady(Isolate* isolate) { MonitorLocker ml(isolate_creation_monitor_); if (!creation_enabled_) { return false; } isolate->accepts_messages_ = true; return true; } void Isolate::UnMarkIsolateReady(Isolate* isolate) { MonitorLocker ml(isolate_creation_monitor_); isolate->accepts_messages_ = false; } void Isolate::DisableIsolateCreation() { MonitorLocker ml(isolate_creation_monitor_); creation_enabled_ = false; } void Isolate::EnableIsolateCreation() { MonitorLocker ml(isolate_creation_monitor_); creation_enabled_ = true; } bool Isolate::IsolateCreationEnabled() { MonitorLocker ml(isolate_creation_monitor_); return creation_enabled_; } bool IsolateGroup::IsSystemIsolateGroup(const IsolateGroup* group) { return group->source()->flags.is_system_isolate; } bool Isolate::IsVMInternalIsolate(const Isolate* isolate) { return isolate->is_kernel_isolate() || isolate->is_service_isolate() || isolate->is_vm_isolate(); } void Isolate::KillLocked(LibMsgId msg_id) { Dart_CObject kill_msg; Dart_CObject* list_values[4]; kill_msg.type = Dart_CObject_kArray; kill_msg.value.as_array.length = 4; kill_msg.value.as_array.values = list_values; Dart_CObject oob; oob.type = Dart_CObject_kInt32; oob.value.as_int32 = Message::kIsolateLibOOBMsg; list_values[0] = &oob; Dart_CObject msg_type; msg_type.type = Dart_CObject_kInt32; msg_type.value.as_int32 = msg_id; list_values[1] = &msg_type; Dart_CObject cap; cap.type = Dart_CObject_kCapability; cap.value.as_capability.id = terminate_capability(); list_values[2] = ∩ Dart_CObject imm; imm.type = Dart_CObject_kInt32; imm.value.as_int32 = Isolate::kImmediateAction; list_values[3] = &imm; { AllocOnlyStackZone zone; std::unique_ptr message = WriteApiMessage( zone.GetZone(), &kill_msg, main_port(), Message::kOOBPriority); ASSERT(message != nullptr); // Post the message at the given port. bool success = PortMap::PostMessage(std::move(message)); ASSERT(success); } } class IsolateKillerVisitor : public IsolateVisitor { public: IsolateKillerVisitor(Isolate::LibMsgId msg_id, bool kill_system_isolates = false) : target_(nullptr), msg_id_(msg_id), kill_system_isolates_(kill_system_isolates) {} IsolateKillerVisitor(Isolate* isolate, Isolate::LibMsgId msg_id) : target_(isolate), msg_id_(msg_id), kill_system_isolates_(false) { ASSERT(isolate != Dart::vm_isolate()); } virtual ~IsolateKillerVisitor() {} void VisitIsolate(Isolate* isolate) { MonitorLocker ml(Isolate::isolate_creation_monitor_); ASSERT(isolate != nullptr); if (ShouldKill(isolate)) { if (isolate->AcceptsMessagesLocked()) { isolate->KillLocked(msg_id_); } } } private: bool ShouldKill(Isolate* isolate) { if (kill_system_isolates_) { ASSERT(target_ == nullptr); // Don't kill the service isolate or vm isolate. return IsSystemIsolate(isolate) && !Isolate::IsVMInternalIsolate(isolate); } // If a target_ is specified, then only kill the target_. // Otherwise, don't kill the service isolate or vm isolate. return (((target_ != nullptr) && (isolate == target_)) || ((target_ == nullptr) && !IsSystemIsolate(isolate))); } Isolate* target_; Isolate::LibMsgId msg_id_; bool kill_system_isolates_; }; void Isolate::KillAllIsolates(LibMsgId msg_id) { IsolateKillerVisitor visitor(msg_id); VisitIsolates(&visitor); } void Isolate::KillAllSystemIsolates(LibMsgId msg_id) { IsolateKillerVisitor visitor(msg_id, /*kill_system_isolates=*/true); VisitIsolates(&visitor); } void Isolate::KillIfExists(Isolate* isolate, LibMsgId msg_id) { IsolateKillerVisitor visitor(isolate, msg_id); VisitIsolates(&visitor); } void Isolate::IncrementSpawnCount() { MonitorLocker ml(&spawn_count_monitor_); spawn_count_++; } void Isolate::DecrementSpawnCount() { MonitorLocker ml(&spawn_count_monitor_); ASSERT(spawn_count_ > 0); spawn_count_--; ml.Notify(); } void Isolate::WaitForOutstandingSpawns() { Thread* thread = Thread::Current(); ASSERT(thread != nullptr); MonitorLocker ml(&spawn_count_monitor_); while (spawn_count_ > 0) { ml.WaitWithSafepointCheck(thread); } } FfiCallbackMetadata::Trampoline Isolate::CreateAsyncFfiCallback( Zone* zone, const Function& send_function, Dart_Port send_port) { return FfiCallbackMetadata::Instance()->CreateAsyncFfiCallback( this, zone, send_function, send_port, &ffi_callback_list_head_); } FfiCallbackMetadata::Trampoline Isolate::CreateIsolateLocalFfiCallback( Zone* zone, const Function& trampoline, const Closure& target, bool keep_isolate_alive) { if (keep_isolate_alive) { UpdateNativeCallableKeepIsolateAliveCounter(1); } return FfiCallbackMetadata::Instance()->CreateIsolateLocalFfiCallback( this, zone, trampoline, target, &ffi_callback_list_head_); } bool Isolate::HasLivePorts() { ASSERT(0 <= open_ports_ && 0 <= open_ports_keepalive_ && open_ports_keepalive_ <= open_ports_); return open_ports_keepalive_ > 0; } ReceivePortPtr Isolate::CreateReceivePort(const String& debug_name) { Dart_Port port_id = PortMap::CreatePort(message_handler()); ++open_ports_; ++open_ports_keepalive_; return ReceivePort::New(port_id, debug_name); } void Isolate::SetReceivePortKeepAliveState(const ReceivePort& receive_port, bool keep_isolate_alive) { // Changing keep-isolate-alive state of a closed port is a NOP. if (!receive_port.is_open()) return; ASSERT(0 < open_ports_); // If the state doesn't change it's a NOP. if (receive_port.keep_isolate_alive() == keep_isolate_alive) return; if (keep_isolate_alive) { ASSERT(open_ports_keepalive_ < open_ports_); ++open_ports_keepalive_; } else { ASSERT(0 < open_ports_keepalive_); --open_ports_keepalive_; } receive_port.set_keep_isolate_alive(keep_isolate_alive); } void Isolate::CloseReceivePort(const ReceivePort& receive_port) { // Closing an already closed port is a NOP. if (!receive_port.is_open()) return; ASSERT(open_ports_ > 0); const bool ok = PortMap::ClosePort(receive_port.Id()); RELEASE_ASSERT(ok); if (receive_port.keep_isolate_alive()) { --open_ports_keepalive_; receive_port.set_keep_isolate_alive(false); } --open_ports_; receive_port.set_is_open(false); } void Isolate::DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback) { FfiCallbackMetadata::Instance()->DeleteCallback(callback, &ffi_callback_list_head_); } void Isolate::UpdateNativeCallableKeepIsolateAliveCounter(intptr_t delta) { ffi_callback_keep_alive_counter_ += delta; ASSERT(ffi_callback_keep_alive_counter_ >= 0); } bool Isolate::HasOpenNativeCallables() { ASSERT(ffi_callback_keep_alive_counter_ >= 0); return ffi_callback_keep_alive_counter_ > 0; } #if !defined(PRODUCT) void IsolateGroup::CloneClassTableForReload() { RELEASE_ASSERT(class_table_ == heap_walk_class_table_); class_table_ = class_table_->Clone(); set_cached_class_table_table(nullptr); } void IsolateGroup::RestoreOriginalClassTable() { RELEASE_ASSERT(class_table_ != heap_walk_class_table_); class_table_allocator_.Free(class_table_); class_table_ = heap_walk_class_table_; set_cached_class_table_table(class_table_->table()); } void IsolateGroup::DropOriginalClassTable() { RELEASE_ASSERT(class_table_ != heap_walk_class_table_); class_table_allocator_.Free(heap_walk_class_table_); heap_walk_class_table_ = class_table_; set_cached_class_table_table(class_table_->table()); } #endif } // namespace dart