| // 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 "platform/utils.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/dart_entry.h" |
| #include "vm/isolate.h" |
| #include "vm/lockers.h" |
| #include "vm/message_handler.h" |
| #include "vm/os_thread.h" |
| |
| namespace dart { |
| |
| Mutex* PortMap::mutex_ = NULL; |
| PortMap::Entry* PortMap::map_ = NULL; |
| MessageHandler* PortMap::deleted_entry_ = reinterpret_cast<MessageHandler*>(1); |
| intptr_t PortMap::capacity_ = 0; |
| intptr_t PortMap::used_ = 0; |
| intptr_t PortMap::deleted_ = 0; |
| Random* PortMap::prng_ = NULL; |
| |
| intptr_t PortMap::FindPort(Dart_Port port) { |
| // ILLEGAL_PORT (0) is used as a sentinel value in Entry.port. The loop below |
| // could return the index to a deleted port when we are searching for |
| // port id ILLEGAL_PORT. Return -1 immediately to indicate the port |
| // does not exist. |
| if (port == ILLEGAL_PORT) { |
| return -1; |
| } |
| ASSERT(port != ILLEGAL_PORT); |
| intptr_t index = port % capacity_; |
| intptr_t start_index = index; |
| Entry entry = map_[index]; |
| while (entry.handler != NULL) { |
| if (entry.port == port) { |
| return index; |
| } |
| index = (index + 1) % capacity_; |
| // Prevent endless loops. |
| ASSERT(index != start_index); |
| entry = map_[index]; |
| } |
| return -1; |
| } |
| |
| void PortMap::Rehash(intptr_t new_capacity) { |
| Entry* new_ports = new Entry[new_capacity]; |
| memset(new_ports, 0, new_capacity * sizeof(Entry)); |
| |
| for (intptr_t i = 0; i < capacity_; i++) { |
| Entry entry = map_[i]; |
| // Skip free and deleted entries. |
| if (entry.port != 0) { |
| intptr_t new_index = entry.port % new_capacity; |
| while (new_ports[new_index].port != 0) { |
| new_index = (new_index + 1) % new_capacity; |
| } |
| new_ports[new_index] = entry; |
| } |
| } |
| delete[] map_; |
| map_ = new_ports; |
| capacity_ = new_capacity; |
| deleted_ = 0; |
| } |
| |
| const char* PortMap::PortStateString(PortState kind) { |
| switch (kind) { |
| case kNewPort: |
| return "new"; |
| case kLivePort: |
| return "live"; |
| case kControlPort: |
| return "control"; |
| default: |
| UNREACHABLE(); |
| return "UNKNOWN"; |
| } |
| } |
| |
| Dart_Port PortMap::AllocatePort() { |
| const Dart_Port kMASK = 0x3fffffff; |
| Dart_Port result = prng_->NextUInt32() & kMASK; |
| |
| // Keep getting new values while we have an illegal port number or the port |
| // number is already in use. |
| while ((result == 0) || (FindPort(result) >= 0)) { |
| result = prng_->NextUInt32() & kMASK; |
| } |
| |
| ASSERT(result != 0); |
| ASSERT(FindPort(result) < 0); |
| return result; |
| } |
| |
| void PortMap::SetPortState(Dart_Port port, PortState state) { |
| MutexLocker ml(mutex_); |
| intptr_t index = FindPort(port); |
| ASSERT(index >= 0); |
| PortState old_state = map_[index].state; |
| ASSERT(old_state == kNewPort); |
| map_[index].state = state; |
| if (state == kLivePort) { |
| map_[index].handler->increment_live_ports(); |
| } |
| if (FLAG_trace_isolates) { |
| OS::PrintErr( |
| "[^] Port (%s) -> (%s): \n" |
| "\thandler: %s\n" |
| "\tport: %" Pd64 "\n", |
| PortStateString(old_state), PortStateString(state), |
| map_[index].handler->name(), port); |
| } |
| } |
| |
| void PortMap::MaintainInvariants() { |
| intptr_t empty = capacity_ - used_ - deleted_; |
| if (used_ > ((capacity_ / 4) * 3)) { |
| // Grow the port map. |
| Rehash(capacity_ * 2); |
| } else if (empty < deleted_) { |
| // Rehash without growing the table to flush the deleted slots out of the |
| // map. |
| Rehash(capacity_); |
| } |
| } |
| |
| Dart_Port PortMap::CreatePort(MessageHandler* handler) { |
| ASSERT(handler != NULL); |
| MutexLocker ml(mutex_); |
| #if defined(DEBUG) |
| handler->CheckAccess(); |
| #endif |
| |
| Entry entry; |
| entry.port = AllocatePort(); |
| entry.handler = handler; |
| entry.state = kNewPort; |
| |
| // Search for the first unused slot. Make use of the knowledge that here is |
| // currently no port with this id in the port map. |
| ASSERT(FindPort(entry.port) < 0); |
| intptr_t index = entry.port % capacity_; |
| Entry cur = map_[index]; |
| // Stop the search at the first found unused (free or deleted) slot. |
| while (cur.port != 0) { |
| index = (index + 1) % capacity_; |
| cur = map_[index]; |
| } |
| |
| // Insert the newly created port at the index. |
| ASSERT(index >= 0); |
| ASSERT(index < capacity_); |
| ASSERT(map_[index].port == 0); |
| ASSERT((map_[index].handler == NULL) || |
| (map_[index].handler == deleted_entry_)); |
| if (map_[index].handler == deleted_entry_) { |
| // Consuming a deleted entry. |
| deleted_--; |
| } |
| map_[index] = entry; |
| |
| // Increment number of used slots and grow if necessary. |
| used_++; |
| MaintainInvariants(); |
| |
| if (FLAG_trace_isolates) { |
| OS::PrintErr( |
| "[+] Opening port: \n" |
| "\thandler: %s\n" |
| "\tport: %" Pd64 "\n", |
| handler->name(), entry.port); |
| } |
| |
| return entry.port; |
| } |
| |
| bool PortMap::ClosePort(Dart_Port port) { |
| MessageHandler* handler = NULL; |
| { |
| MutexLocker ml(mutex_); |
| intptr_t index = FindPort(port); |
| if (index < 0) { |
| return false; |
| } |
| ASSERT(index < capacity_); |
| ASSERT(map_[index].port != 0); |
| ASSERT(map_[index].handler != deleted_entry_); |
| ASSERT(map_[index].handler != NULL); |
| |
| handler = map_[index].handler; |
| #if defined(DEBUG) |
| handler->CheckAccess(); |
| #endif |
| // Before releasing the lock mark the slot in the map as deleted. This makes |
| // it possible to release the port map lock before flushing all of its |
| // pending messages below. |
| map_[index].port = 0; |
| map_[index].handler = deleted_entry_; |
| if (map_[index].state == kLivePort) { |
| handler->decrement_live_ports(); |
| } |
| |
| used_--; |
| deleted_++; |
| MaintainInvariants(); |
| } |
| handler->ClosePort(port); |
| if (!handler->HasLivePorts() && handler->OwnedByPortMap()) { |
| // Delete handler as soon as it isn't busy with a task. |
| handler->RequestDeletion(); |
| } |
| return true; |
| } |
| |
| void PortMap::ClosePorts(MessageHandler* handler) { |
| { |
| MutexLocker ml(mutex_); |
| for (intptr_t i = 0; i < capacity_; i++) { |
| if (map_[i].handler == handler) { |
| // Mark the slot as deleted. |
| map_[i].port = 0; |
| map_[i].handler = deleted_entry_; |
| if (map_[i].state == kLivePort) { |
| handler->decrement_live_ports(); |
| } |
| used_--; |
| deleted_++; |
| } |
| } |
| MaintainInvariants(); |
| } |
| handler->CloseAllPorts(); |
| } |
| |
| bool PortMap::PostMessage(Message* message) { |
| MutexLocker ml(mutex_); |
| intptr_t index = FindPort(message->dest_port()); |
| if (index < 0) { |
| delete message; |
| return false; |
| } |
| ASSERT(index >= 0); |
| ASSERT(index < capacity_); |
| MessageHandler* handler = map_[index].handler; |
| ASSERT(map_[index].port != 0); |
| ASSERT((handler != NULL) && (handler != deleted_entry_)); |
| handler->PostMessage(message); |
| return true; |
| } |
| |
| bool PortMap::IsLocalPort(Dart_Port id) { |
| MutexLocker ml(mutex_); |
| intptr_t index = FindPort(id); |
| if (index < 0) { |
| // Port does not exist. |
| return false; |
| } |
| |
| MessageHandler* handler = map_[index].handler; |
| return handler->IsCurrentIsolate(); |
| } |
| |
| Isolate* PortMap::GetIsolate(Dart_Port id) { |
| MutexLocker ml(mutex_); |
| intptr_t index = FindPort(id); |
| if (index < 0) { |
| // Port does not exist. |
| return NULL; |
| } |
| |
| MessageHandler* handler = map_[index].handler; |
| return handler->isolate(); |
| } |
| |
| void PortMap::Init() { |
| if (mutex_ == NULL) { |
| mutex_ = new Mutex(); |
| } |
| ASSERT(mutex_ != NULL); |
| prng_ = new Random(); |
| |
| static const intptr_t kInitialCapacity = 8; |
| // TODO(iposva): Verify whether we want to keep exponentially growing. |
| ASSERT(Utils::IsPowerOfTwo(kInitialCapacity)); |
| if (map_ == NULL) { |
| // TODO(bkonyi): don't keep map_ after Dart_Cleanup. |
| map_ = new Entry[kInitialCapacity]; |
| capacity_ = kInitialCapacity; |
| } |
| memset(map_, 0, capacity_ * sizeof(Entry)); |
| used_ = 0; |
| deleted_ = 0; |
| } |
| |
| void PortMap::Cleanup() { |
| ASSERT(map_ != NULL); |
| ASSERT(prng_ != NULL); |
| for (intptr_t i = 0; i < capacity_; ++i) { |
| auto handler = map_[i].handler; |
| if (handler != NULL && handler != deleted_entry_) { |
| ClosePorts(handler); |
| delete handler; |
| } |
| } |
| delete prng_; |
| prng_ = NULL; |
| // TODO(bkonyi): find out why deleting map_ sometimes causes crashes. |
| // delete[] map_; |
| // map_ = NULL; |
| } |
| |
| void PortMap::PrintPortsForMessageHandler(MessageHandler* handler, |
| JSONStream* stream) { |
| #ifndef PRODUCT |
| if (!FLAG_support_service) { |
| return; |
| } |
| JSONObject jsobj(stream); |
| jsobj.AddProperty("type", "_Ports"); |
| Object& msg_handler = Object::Handle(); |
| { |
| JSONArray ports(&jsobj, "ports"); |
| SafepointMutexLocker ml(mutex_); |
| for (intptr_t i = 0; i < capacity_; i++) { |
| if (map_[i].handler == handler) { |
| if (map_[i].state == kLivePort) { |
| JSONObject port(&ports); |
| port.AddProperty("type", "_Port"); |
| port.AddPropertyF("name", "Isolate Port (%" Pd64 ")", map_[i].port); |
| msg_handler = DartLibraryCalls::LookupHandler(map_[i].port); |
| port.AddProperty("handler", msg_handler); |
| } |
| } |
| } |
| } |
| #endif |
| } |
| |
| void PortMap::DebugDumpForMessageHandler(MessageHandler* handler) { |
| SafepointMutexLocker ml(mutex_); |
| Object& msg_handler = Object::Handle(); |
| for (intptr_t i = 0; i < capacity_; i++) { |
| if (map_[i].handler == handler) { |
| if (map_[i].state == kLivePort) { |
| OS::PrintErr("Live Port = %" Pd64 "\n", map_[i].port); |
| msg_handler = DartLibraryCalls::LookupHandler(map_[i].port); |
| OS::PrintErr("Handler = %s\n", msg_handler.ToCString()); |
| } |
| } |
| } |
| } |
| |
| } // namespace dart |