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