| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/port.h" | 5 #include "vm/port.h" |
| 6 | 6 |
| 7 #include "vm/isolate.h" |
| 8 #include "vm/thread.h" |
| 9 #include "vm/utils.h" |
| 10 |
| 7 namespace dart { | 11 namespace dart { |
| 8 | 12 |
| 9 MessageQueue::~MessageQueue() { | |
| 10 // Ensure that all pending messages have been released. | |
| 11 ASSERT(head_ == NULL); | |
| 12 } | |
| 13 | |
| 14 | |
| 15 void MessageQueue::Enqueue(PortMessage* msg) { | |
| 16 // Make sure messages are not reused. | |
| 17 ASSERT(msg->next_ == NULL); | |
| 18 if (head_ == NULL) { | |
| 19 // Only element in the queue. | |
| 20 head_ = msg; | |
| 21 tail_ = msg; | |
| 22 } else { | |
| 23 ASSERT(tail_ != NULL); | |
| 24 // Append at the tail. | |
| 25 tail_->next_ = msg; | |
| 26 tail_ = msg; | |
| 27 } | |
| 28 } | |
| 29 | |
| 30 | |
| 31 PortMessage* MessageQueue::Dequeue() { | |
| 32 PortMessage* result = head_; | |
| 33 if (result != NULL) { | |
| 34 head_ = result->next_; | |
| 35 // The following update to tail_ is not strictly needed. | |
| 36 if (head_ == NULL) { | |
| 37 tail_ = NULL; | |
| 38 } | |
| 39 #if DEBUG | |
| 40 result->next_ = result; // Make sure to trigger ASSERT in Enqueue. | |
| 41 #endif // DEBUG | |
| 42 } | |
| 43 return result; | |
| 44 } | |
| 45 | |
| 46 | |
| 47 void MessageQueue::Flush(intptr_t port_id) { | |
| 48 PortMessage* cur = head_; | |
| 49 PortMessage* prev = NULL; | |
| 50 while (cur != NULL) { | |
| 51 PortMessage* next = cur->next_; | |
| 52 // If the message matches, then remove it from the queue and delete it. | |
| 53 if (cur->dest_id() == port_id) { | |
| 54 if (prev != NULL) { | |
| 55 prev->next_ = next; | |
| 56 } else { | |
| 57 head_ = next; | |
| 58 } | |
| 59 delete cur; | |
| 60 } else { | |
| 61 // Move prev forward. | |
| 62 prev = cur; | |
| 63 } | |
| 64 // Advance to the next message in the queue. | |
| 65 cur = next; | |
| 66 } | |
| 67 tail_ = prev; | |
| 68 } | |
| 69 | |
| 70 | |
| 71 void MessageQueue::FlushAll() { | |
| 72 PortMessage* cur = head_; | |
| 73 head_ = NULL; | |
| 74 tail_ = NULL; | |
| 75 while (cur != NULL) { | |
| 76 PortMessage* next = cur->next_; | |
| 77 delete next; | |
| 78 cur = next; | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 | 13 |
| 83 Mutex* PortMap::mutex_ = NULL; | 14 Mutex* PortMap::mutex_ = NULL; |
| 84 | 15 |
| 85 PortMap::Entry* PortMap::map_ = NULL; | 16 PortMap::Entry* PortMap::map_ = NULL; |
| 86 Isolate* PortMap::deleted_entry_ = reinterpret_cast<Isolate*>(1); | 17 Isolate* PortMap::deleted_entry_ = reinterpret_cast<Isolate*>(1); |
| 87 intptr_t PortMap::capacity_ = 0; | 18 intptr_t PortMap::capacity_ = 0; |
| 88 intptr_t PortMap::used_ = 0; | 19 intptr_t PortMap::used_ = 0; |
| 89 intptr_t PortMap::deleted_ = 0; | 20 intptr_t PortMap::deleted_ = 0; |
| 90 | 21 |
| 91 intptr_t PortMap::next_id_ = 7111; | 22 Dart_Port PortMap::next_port_ = 7111; |
| 92 | 23 |
| 93 | 24 |
| 94 intptr_t PortMap::FindId(intptr_t id) { | 25 intptr_t PortMap::FindPort(Dart_Port port) { |
| 95 intptr_t index = id % capacity_; | 26 intptr_t index = port % capacity_; |
| 96 intptr_t start_index = index; | 27 intptr_t start_index = index; |
| 97 Entry entry = map_[index]; | 28 Entry entry = map_[index]; |
| 98 while (entry.isolate != NULL) { | 29 while (entry.isolate != NULL) { |
| 99 if (entry.id == id) { | 30 if (entry.port == port) { |
| 100 return index; | 31 return index; |
| 101 } | 32 } |
| 102 index = (index + 1) % capacity_; | 33 index = (index + 1) % capacity_; |
| 103 // Prevent endless loops. | 34 // Prevent endless loops. |
| 104 ASSERT(index != start_index); | 35 ASSERT(index != start_index); |
| 105 entry = map_[index]; | 36 entry = map_[index]; |
| 106 } | 37 } |
| 107 return -1; | 38 return -1; |
| 108 } | 39 } |
| 109 | 40 |
| 110 | 41 |
| 111 void PortMap::Rehash(intptr_t new_capacity) { | 42 void PortMap::Rehash(intptr_t new_capacity) { |
| 112 Entry* new_ports = new Entry[new_capacity]; | 43 Entry* new_ports = new Entry[new_capacity]; |
| 113 memset(new_ports, 0, new_capacity * sizeof(Entry)); | 44 memset(new_ports, 0, new_capacity * sizeof(Entry)); |
| 114 | 45 |
| 115 for (intptr_t i = 0; i < capacity_; i++) { | 46 for (intptr_t i = 0; i < capacity_; i++) { |
| 116 Entry entry = map_[i]; | 47 Entry entry = map_[i]; |
| 117 // Skip free and deleted entries. | 48 // Skip free and deleted entries. |
| 118 if (entry.id != 0) { | 49 if (entry.port != 0) { |
| 119 intptr_t new_index = entry.id % new_capacity; | 50 intptr_t new_index = entry.port % new_capacity; |
| 120 while (new_ports[new_index].id != 0) { | 51 while (new_ports[new_index].port != 0) { |
| 121 new_index = (new_index + 1) % new_capacity; | 52 new_index = (new_index + 1) % new_capacity; |
| 122 } | 53 } |
| 123 new_ports[new_index] = entry; | 54 new_ports[new_index] = entry; |
| 124 } | 55 } |
| 125 } | 56 } |
| 126 delete map_; | 57 delete map_; |
| 127 map_ = new_ports; | 58 map_ = new_ports; |
| 128 capacity_ = new_capacity; | 59 capacity_ = new_capacity; |
| 129 deleted_ = 0; | 60 deleted_ = 0; |
| 130 } | 61 } |
| 131 | 62 |
| 132 | 63 |
| 133 intptr_t PortMap::AllocateId() { | 64 Dart_Port PortMap::AllocatePort() { |
| 134 intptr_t result = next_id_; | 65 Dart_Port result = next_port_; |
| 135 | 66 |
| 136 do { | 67 do { |
| 137 // TODO(iposva): Use an approved hashing function to have less predictable | 68 // TODO(iposva): Use an approved hashing function to have less predictable |
| 138 // port ids, or make them not accessible from Dart code or both. | 69 // port ids, or make them not accessible from Dart code or both. |
| 139 next_id_++; | 70 next_port_++; |
| 140 } while (FindId(next_id_) >= 0); | 71 } while (FindPort(next_port_) >= 0); |
| 141 | 72 |
| 142 ASSERT(result != 0); | 73 ASSERT(result != 0); |
| 143 return result; | 74 return result; |
| 144 } | 75 } |
| 145 | 76 |
| 146 | 77 |
| 147 void PortMap::MaintainInvariants() { | 78 void PortMap::MaintainInvariants() { |
| 148 intptr_t empty = capacity_ - used_ - deleted_; | 79 intptr_t empty = capacity_ - used_ - deleted_; |
| 149 if (used_ > ((capacity_ / 4) * 3)) { | 80 if (used_ > ((capacity_ / 4) * 3)) { |
| 150 // Grow the port map. | 81 // Grow the port map. |
| 151 Rehash(capacity_ * 2); | 82 Rehash(capacity_ * 2); |
| 152 } else if (empty < deleted_) { | 83 } else if (empty < deleted_) { |
| 153 // Rehash without growing the table to flush the deleted slots out of the | 84 // Rehash without growing the table to flush the deleted slots out of the |
| 154 // map. | 85 // map. |
| 155 Rehash(capacity_); | 86 Rehash(capacity_); |
| 156 } | 87 } |
| 157 } | 88 } |
| 158 | 89 |
| 159 | 90 |
| 160 intptr_t PortMap::CreatePort() { | 91 Dart_Port PortMap::CreatePort() { |
| 161 Isolate* isolate = Isolate::Current(); | 92 Isolate* isolate = Isolate::Current(); |
| 162 | 93 |
| 163 MutexLocker ml(mutex_); | 94 MutexLocker ml(mutex_); |
| 164 | 95 |
| 165 Entry entry; | 96 Entry entry; |
| 166 entry.id = AllocateId(); | 97 entry.port = AllocatePort(); |
| 167 entry.isolate = isolate; | 98 entry.isolate = isolate; |
| 168 | 99 |
| 169 // Search for the first unused slot. Make use of the knowledge that here is | 100 // Search for the first unused slot. Make use of the knowledge that here is |
| 170 // currently no port with this id in the port map. | 101 // currently no port with this id in the port map. |
| 171 ASSERT(FindId(entry.id) < 0); | 102 ASSERT(FindPort(entry.port) < 0); |
| 172 intptr_t index = entry.id % capacity_; | 103 intptr_t index = entry.port % capacity_; |
| 173 Entry cur = map_[index]; | 104 Entry cur = map_[index]; |
| 174 // Stop the search at the first found unused (free or deleted) slot. | 105 // Stop the search at the first found unused (free or deleted) slot. |
| 175 while (cur.id != 0) { | 106 while (cur.port != 0) { |
| 176 index = (index + 1) % capacity_; | 107 index = (index + 1) % capacity_; |
| 177 cur = map_[index]; | 108 cur = map_[index]; |
| 178 } | 109 } |
| 179 | 110 |
| 180 // Insert the newly created port at the index. | 111 // Insert the newly created port at the index. |
| 181 ASSERT(index >= 0); | 112 ASSERT(index >= 0); |
| 182 ASSERT(index < capacity_); | 113 ASSERT(index < capacity_); |
| 183 ASSERT(map_[index].id == 0); | 114 ASSERT(map_[index].port == 0); |
| 184 ASSERT((map_[index].isolate == NULL) || | 115 ASSERT((map_[index].isolate == NULL) || |
| 185 (map_[index].isolate == deleted_entry_)); | 116 (map_[index].isolate == deleted_entry_)); |
| 186 if (map_[index].isolate == deleted_entry_) { | 117 if (map_[index].isolate == deleted_entry_) { |
| 187 // Consuming a deleted entry. | 118 // Consuming a deleted entry. |
| 188 deleted_--; | 119 deleted_--; |
| 189 } | 120 } |
| 190 map_[index] = entry; | 121 map_[index] = entry; |
| 191 isolate->increment_active_ports(); | 122 isolate->increment_active_ports(); |
| 192 | 123 |
| 193 // Increment number of used slots and grow if necessary. | 124 // Increment number of used slots and grow if necessary. |
| 194 used_++; | 125 used_++; |
| 195 MaintainInvariants(); | 126 MaintainInvariants(); |
| 196 | 127 |
| 197 return entry.id; | 128 return entry.port; |
| 198 } | 129 } |
| 199 | 130 |
| 200 | 131 |
| 201 void PortMap::ClosePort(intptr_t id) { | 132 void PortMap::ClosePort(Dart_Port port) { |
| 202 Isolate* isolate = Isolate::Current(); | 133 Isolate* isolate = Isolate::Current(); |
| 203 { | 134 { |
| 204 MutexLocker ml(mutex_); | 135 MutexLocker ml(mutex_); |
| 205 intptr_t index = FindId(id); | 136 intptr_t index = FindPort(port); |
| 206 if (index < 0) { | 137 if (index < 0) { |
| 207 return; | 138 return; |
| 208 } | 139 } |
| 209 ASSERT(index < capacity_); | 140 ASSERT(index < capacity_); |
| 210 ASSERT(map_[index].id != 0); | 141 ASSERT(map_[index].port != 0); |
| 211 ASSERT(map_[index].isolate == isolate); | 142 ASSERT(map_[index].isolate == isolate); |
| 212 // Before releasing the lock mark the slot in the map as deleted. This makes | 143 // Before releasing the lock mark the slot in the map as deleted. This makes |
| 213 // it possible to release the port map lock before flushing all of its | 144 // it possible to release the port map lock before flushing all of its |
| 214 // pending messages below. | 145 // pending messages below. |
| 215 map_[index].id = 0; | 146 map_[index].port = 0; |
| 216 map_[index].isolate = deleted_entry_; | 147 map_[index].isolate = deleted_entry_; |
| 217 isolate->decrement_active_ports(); | 148 isolate->decrement_active_ports(); |
| 218 | 149 |
| 219 used_--; | 150 used_--; |
| 220 deleted_++; | 151 deleted_++; |
| 221 MaintainInvariants(); | 152 MaintainInvariants(); |
| 222 } | 153 } |
| 223 { | 154 |
| 224 // Remove the pending messages for this port. | 155 // Notify the embedder that this port is closed. |
| 225 MonitorLocker ml(isolate->monitor()); | 156 Dart_ClosePortCallback callback = isolate->close_port_callback(); |
| 226 isolate->message_queue()->Flush(id); | 157 ASSERT(callback); |
| 227 } | 158 ASSERT(port != kCloseAllPorts); |
| 159 (*callback)(isolate, port); |
| 228 } | 160 } |
| 229 | 161 |
| 230 | 162 |
| 231 void PortMap::ClosePorts() { | 163 void PortMap::ClosePorts() { |
| 232 Isolate* isolate = Isolate::Current(); | 164 Isolate* isolate = Isolate::Current(); |
| 233 { | 165 { |
| 234 MutexLocker ml(mutex_); | 166 MutexLocker ml(mutex_); |
| 235 for (intptr_t i = 0; i < capacity_; i++) { | 167 for (intptr_t i = 0; i < capacity_; i++) { |
| 236 if (map_[i].isolate == isolate) { | 168 if (map_[i].isolate == isolate) { |
| 237 // Mark the slot as deleted. | 169 // Mark the slot as deleted. |
| 238 map_[i].id = 0; | 170 map_[i].port = 0; |
| 239 map_[i].isolate = deleted_entry_; | 171 map_[i].isolate = deleted_entry_; |
| 240 isolate->decrement_active_ports(); | 172 isolate->decrement_active_ports(); |
| 241 | 173 |
| 242 used_--; | 174 used_--; |
| 243 deleted_++; | 175 deleted_++; |
| 244 } | 176 } |
| 245 } | 177 } |
| 246 MaintainInvariants(); | 178 MaintainInvariants(); |
| 247 } | 179 } |
| 248 isolate->message_queue()->FlushAll(); | 180 |
| 181 // Notify the embedder that all ports are closed. |
| 182 Dart_ClosePortCallback callback = isolate->close_port_callback(); |
| 183 ASSERT(callback); |
| 184 (*callback)(isolate, kCloseAllPorts); |
| 249 } | 185 } |
| 250 | 186 |
| 251 | 187 |
| 252 bool PortMap::IsActivePort(intptr_t id) { | 188 bool PortMap::IsActivePort(Dart_Port port) { |
| 253 MutexLocker ml(mutex_); | 189 MutexLocker ml(mutex_); |
| 254 return (FindId(id) >= 0); | 190 return (FindPort(port) >= 0); |
| 255 } | 191 } |
| 256 | 192 |
| 257 | 193 |
| 258 bool PortMap::PostMessage(PortMessage* msg) { | 194 bool PortMap::PostMessage(Dart_Port dest_port, |
| 259 intptr_t id = msg->dest_id(); | 195 Dart_Port reply_port, |
| 196 Dart_Message message) { |
| 260 mutex_->Lock(); | 197 mutex_->Lock(); |
| 261 intptr_t index = FindId(id); | 198 intptr_t index = FindPort(dest_port); |
| 262 if (index < 0) { | 199 if (index < 0) { |
| 200 free(message); |
| 263 mutex_->Unlock(); | 201 mutex_->Unlock(); |
| 264 return false; | 202 return false; |
| 265 } | 203 } |
| 266 ASSERT(index >= 0); | 204 ASSERT(index >= 0); |
| 267 ASSERT(index < capacity_); | 205 ASSERT(index < capacity_); |
| 268 Isolate* isolate = map_[index].isolate; | 206 Isolate* isolate = map_[index].isolate; |
| 269 ASSERT(map_[index].id != 0); | 207 ASSERT(map_[index].port != 0); |
| 270 ASSERT((isolate != NULL) && (isolate != deleted_entry_)); | 208 ASSERT((isolate != NULL) && (isolate != deleted_entry_)); |
| 271 Monitor* monitor = isolate->monitor(); | 209 |
| 272 monitor->Enter(); | 210 // Delegate message delivery to the embedder. |
| 273 isolate->message_queue()->Enqueue(msg); | 211 Dart_PostMessageCallback callback = isolate->post_message_callback(); |
| 274 monitor->Notify(); | 212 ASSERT(callback); |
| 275 monitor->Exit(); | 213 bool result = (*callback)(isolate, dest_port, reply_port, message); |
| 214 |
| 276 mutex_->Unlock(); | 215 mutex_->Unlock(); |
| 277 return true; | 216 return result; |
| 278 } | 217 } |
| 279 | 218 |
| 280 | 219 |
| 281 PortMessage* PortMap::ReceiveMessage(int64_t millis) { | |
| 282 // Since only the isolate owning the port can close the port and remove it | |
| 283 // from the port map and flush its messages, we can safely assume that the | |
| 284 // all messages in the message queue are for active ports. | |
| 285 Isolate* isolate = Isolate::Current(); | |
| 286 { | |
| 287 MonitorLocker ml(isolate->monitor()); | |
| 288 PortMessage* result = isolate->message_queue()->Dequeue(); | |
| 289 if (result == NULL) { | |
| 290 ml.Wait(millis); | |
| 291 result = isolate->message_queue()->Dequeue(); | |
| 292 // We will return a NULL message for spurious wakeups or timeouts. | |
| 293 } | |
| 294 return result; | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 | |
| 299 void PortMap::InitOnce() { | 220 void PortMap::InitOnce() { |
| 300 mutex_ = new Mutex(); | 221 mutex_ = new Mutex(); |
| 301 | 222 |
| 302 static const intptr_t kInitialCapacity = 8; | 223 static const intptr_t kInitialCapacity = 8; |
| 303 // TODO(iposva): Verify whether we want to keep exponentially growing. | 224 // TODO(iposva): Verify whether we want to keep exponentially growing. |
| 304 ASSERT(Utils::IsPowerOfTwo(kInitialCapacity)); | 225 ASSERT(Utils::IsPowerOfTwo(kInitialCapacity)); |
| 305 map_ = new Entry[kInitialCapacity]; | 226 map_ = new Entry[kInitialCapacity]; |
| 306 memset(map_, 0, kInitialCapacity * sizeof(Entry)); | 227 memset(map_, 0, kInitialCapacity * sizeof(Entry)); |
| 307 capacity_ = kInitialCapacity; | 228 capacity_ = kInitialCapacity; |
| 308 used_ = 0; | 229 used_ = 0; |
| 309 deleted_ = 0; | 230 deleted_ = 0; |
| 310 } | 231 } |
| 311 | 232 |
| 312 | 233 |
| 313 } // namespace dart | 234 } // namespace dart |
| OLD | NEW |