8f1a38f07b
Introduce `is_acquirable` isolate property that tracks whether isolate can be entered, avoid a `pool_` check that does not work correctly as isolate being shutdown. BUG=https://github.com/dart-lang/sdk/issues/63515 BUG=https://github.com/dart-lang/sdk/issues/63514 TEST=ci Change-Id: I84577ad082d87ead8cdb9f591fabdf918e2f4bd0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509221 Reviewed-by: Slava Egorov <vegorov@google.com>
366 lines
9.4 KiB
C++
366 lines
9.4 KiB
C++
// 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 <utility>
|
|
|
|
#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::Entry>* 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<Entry>::kFreePort ||
|
|
result == PortSet<Entry>::kDeletedPort) {
|
|
continue;
|
|
}
|
|
|
|
ASSERT(!static_cast<ObjectPtr>(static_cast<uword>(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> 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<Entry>();
|
|
}
|
|
}
|
|
|
|
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
|