// 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/isolate.h" #include "vm/message_handler.h" #include "vm/thread.h" namespace dart { DECLARE_FLAG(bool, trace_isolates); Mutex* PortMap::mutex_ = NULL; PortMap::Entry* PortMap::map_ = NULL; MessageHandler* PortMap::deleted_entry_ = reinterpret_cast(1); intptr_t PortMap::capacity_ = 0; intptr_t PortMap::used_ = 0; intptr_t PortMap::deleted_ = 0; Dart_Port PortMap::next_port_ = 7111; intptr_t PortMap::FindPort(Dart_Port 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; } Dart_Port PortMap::AllocatePort() { Dart_Port result = next_port_; do { // TODO(iposva): Use an approved hashing function to have less predictable // port ids, or make them not accessible from Dart code or both. next_port_++; } while (FindPort(next_port_) >= 0); ASSERT(result != 0); return result; } void PortMap::SetLive(Dart_Port port) { MutexLocker ml(mutex_); intptr_t index = FindPort(port); ASSERT(index >= 0); map_[index].live = true; map_[index].handler->increment_live_ports(); } 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.live = false; // 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(); 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].live) { handler->decrement_live_ports(); } used_--; deleted_++; MaintainInvariants(); } handler->ClosePort(port); if (!handler->HasLivePorts() && handler->OwnedByPortMap()) { delete handler; } 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].live) { 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->GetIsolate(); } void PortMap::InitOnce() { mutex_ = new Mutex(); static const intptr_t kInitialCapacity = 8; // TODO(iposva): Verify whether we want to keep exponentially growing. ASSERT(Utils::IsPowerOfTwo(kInitialCapacity)); map_ = new Entry[kInitialCapacity]; memset(map_, 0, kInitialCapacity * sizeof(Entry)); capacity_ = kInitialCapacity; used_ = 0; deleted_ = 0; } } // namespace dart