// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/port.h" #include #include "include/dart_api.h" #include "platform/utils.h" #include "vm/dart_api_impl.h" #include "vm/dart_api_message.h" #include "vm/dart_entry.h" #include "vm/isolate.h" #include "vm/lockers.h" #include "vm/message_handler.h" #include "vm/message_snapshot.h" #include "vm/os_thread.h" namespace dart { Mutex* PortMap::mutex_ = nullptr; PortSet* PortMap::ports_ = nullptr; Random* PortMap::prng_ = nullptr; Dart_Port PortMap::AllocatePort() { Dart_Port result; ASSERT(mutex_->IsOwnedByCurrentThread()); // Keep getting new values while we have an illegal port number or the port // number is already in use. do { // Ensure port ids are never valid object pointers so that reinterpreting // an object pointer as a port id never produces a used port id. const Dart_Port kMask2 = 0x3; // Ensure port ids are representable in JavaScript for the benefit of // vm-service clients such as Observatory. result = prng_->NextJSInt() | kMask2; // The two special marker ports are used for the hashset implementation and // cannot be used as actual ports. if (result == PortSet::kFreePort || result == PortSet::kDeletedPort) { continue; } ASSERT(!static_cast(static_cast(result))->IsWellFormed()); } while (ports_->Contains(result)); ASSERT(result != 0); ASSERT(!ports_->Contains(result)); return result; } Dart_Port PortMap::CreatePort(PortHandler* handler) { ASSERT(handler != nullptr); PortMap::Locker ml; if (ports_ == nullptr) { return ILLEGAL_PORT; } const Dart_Port port = AllocatePort(); if (auto ports = handler->ports(ml)) { ports->Insert(PortHandler::PortSetEntry{port}); } ports_->Insert(Entry{port, handler}); if (FLAG_trace_isolates) { OS::PrintErr( "[+] Opening port: \n" "\thandler: %s\n" "\tport: %" Pd64 "\n", handler->name(), port); } return port; } bool PortMap::ClosePort(Dart_Port port, PortHandler** port_handler) { if (port_handler != nullptr) *port_handler = nullptr; PortHandler* handler = nullptr; { PortMap::Locker ml; if (ports_ == nullptr) { return false; } auto it = ports_->TryLookup(port); if (it == ports_->end()) { return false; } Entry entry = *it; handler = entry.handler; ASSERT(handler != nullptr); #if defined(DEBUG) handler->CheckAccess(); #endif it.Delete(); ports_->Rebalance(); if (auto ports = handler->ports(ml)) { auto isolate_it = ports->TryLookup(port); ASSERT(isolate_it != ports->end()); isolate_it.Delete(); ports->Rebalance(); } } handler->OnPortClosed(port); if (port_handler != nullptr) *port_handler = handler; return true; } void PortMap::ClosePorts(MessageHandler* handler) { { PortMap::Locker ml; if (ports_ == nullptr) { return; } auto ports = handler->ports(ml); ASSERT(ports != nullptr); for (auto isolate_it = ports->begin(); isolate_it != ports->end(); ++isolate_it) { auto it = ports_->TryLookup((*isolate_it).port); ASSERT(it != ports_->end()); Entry entry = *it; ASSERT(entry.port == (*isolate_it).port); ASSERT(entry.handler == handler); it.Delete(); isolate_it.Delete(); } ASSERT(ports->IsEmpty()); ports_->Rebalance(); } handler->OnAllPortsClosed(); } bool PortMap::PostMessage(std::unique_ptr message, bool before_events) { Locker ml; if (ports_ == nullptr) { return false; } auto it = ports_->TryLookup(message->dest_port()); if (it == ports_->end()) { // Ownership of external data remains with the poster. message->DropFinalizers(); return false; } auto handler = (*it).handler; ASSERT(handler != nullptr); handler->PostMessage(std::move(message), before_events); return true; } #if defined(TESTING) bool PortMap::PortExists(Dart_Port id) { Locker ml; if (ports_ == nullptr) { return false; } auto it = ports_->TryLookup(id); return it != ports_->end(); } Isolate* PortMap::GetIsolate(Dart_Port id) { Locker ml; return GetIsolateLocked(ml, id); } #endif // defined(TESTING) Isolate* PortMap::GetIsolateLocked(const Locker& ml, Dart_Port id) { if (ports_ == nullptr) { return nullptr; } auto it = ports_->TryLookup(id); if (it == ports_->end()) { // Port does not exist. return nullptr; } auto handler = (*it).handler; return handler->isolate(); } Dart_Port PortMap::GetOriginId(Dart_Port id) { Locker ml; if (ports_ == nullptr) { return ILLEGAL_PORT; } auto it = ports_->TryLookup(id); if (it == ports_->end()) { // Port does not exist. return ILLEGAL_PORT; } auto handler = (*it).handler; Isolate* isolate = handler->isolate(); if (isolate == nullptr) { // Message handler is a native port instead of an isolate. return ILLEGAL_PORT; } return isolate->group()->id(); } bool PortMap::IsOwned(Dart_Port id) { Locker ml; Isolate* isolate = GetIsolateLocked(ml, id); if (isolate == nullptr) { // Either the port is invalid, or the isolate has already shut down. return false; } return isolate->GetOwnerThread(&ml) != OSThread::kInvalidThreadId; } bool PortMap::IsOwnedByCurrentThread(Dart_Port id, bool require_permanent_pin) { Locker ml; Isolate* isolate = GetIsolateLocked(ml, id); if (isolate == nullptr) { // Either the port is invalid, or the isolate has already shut down. return false; } return isolate->GetOwnerThread(&ml) == OSThread::GetCurrentThreadId() && (!require_permanent_pin || isolate->is_permanently_pinned()); } IsolateAcquireResult PortMap::AcquireIsolateByControlPort(Dart_Port target_port, Isolate** p_isolate) { ASSERT(p_isolate != nullptr); Locker ml; // isolates are not exiting while we hold this lock if (ports_ == nullptr) { return IsolateAcquireResult::ISOLATE_NOT_AVAILABLE; } auto it = ports_->TryLookup(target_port); if (it == ports_->end()) { return IsolateAcquireResult::ISOLATE_NOT_AVAILABLE; } auto target_handler = (*it).handler; ASSERT(target_handler != nullptr); auto target_isolate = target_handler->isolate(); if (!target_handler->isolate()->is_acquirable()) { return IsolateAcquireResult::HAS_MESSAGE_LOOP_OR_UNAVAILABLE; } if (!target_isolate->TryAcquireOwnership()) { return target_isolate->is_permanently_pinned() ? IsolateAcquireResult::PINNED_TO_ANOTHER_THREAD : IsolateAcquireResult::BUSY; } *p_isolate = target_isolate; return IsolateAcquireResult::SUCCESS; } #if defined(TESTING) bool PortMap::HasPorts(MessageHandler* handler) { Locker ml; if (ports_ == nullptr) { return false; } // The MessageHandler::ports_ is only accessed by [PortMap], it is guarded // by the [PortMap::mutex_] we already hold. return !handler->ports_.IsEmpty(); } #endif bool PortMap::IsReceiverInThisIsolateGroupOrClosed(Dart_Port receiver, IsolateGroup* group) { Locker ml; if (ports_ == nullptr) { // Port was closed. return true; } auto it = ports_->TryLookup(receiver); if (it == ports_->end()) { // Port was closed. return true; } auto isolate = (*it).handler->isolate(); if (isolate == nullptr) { // Port belongs to a native port instead of an isolate. return false; } return isolate->group() == group; } void PortMap::Init() { if (mutex_ == nullptr) { mutex_ = new Mutex(); } ASSERT(mutex_ != nullptr); if (prng_ == nullptr) { prng_ = new Random(); } if (ports_ == nullptr) { ports_ = new PortSet(); } } void PortMap::Shutdown() { // Tell all handlers which are running their own thread pools to shutdown. for (auto& entry : *ports_) { entry.handler->Shutdown(); } } void PortMap::Cleanup() { ASSERT(ports_ != nullptr); ASSERT(prng_ != nullptr); for (auto it = ports_->begin(); it != ports_->end(); ++it) { const auto& entry = *it; ASSERT(entry.handler != nullptr); delete entry.handler; it.Delete(); } ports_->Rebalance(); // Grab the mutex and delete the port set. Locker ml; delete prng_; prng_ = nullptr; delete ports_; ports_ = nullptr; } void PortMap::PrintPortsForMessageHandler(MessageHandler* handler, JSONStream* stream) { #ifndef PRODUCT JSONObject jsobj(stream); jsobj.AddProperty("type", "_Ports"); Object& msg_handler = Object::Handle(); { JSONArray ports(&jsobj, "ports"); SafepointMutexLocker ml(mutex_); if (ports_ == nullptr) { return; } for (auto& entry : *ports_) { if (entry.handler == handler) { JSONObject port(&ports); port.AddProperty("type", "_Port"); port.AddPropertyF("name", "Isolate Port (%" Pd64 ")", entry.port); msg_handler = DartLibraryCalls::LookupHandler(entry.port); port.AddProperty("handler", msg_handler); } } } #endif } PortHandler::~PortHandler() {} #if defined(DEBUG) void PortHandler::CheckAccess() const { // By default there is no checking. } #endif } // namespace dart