Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: runtime/vm/message_queue.cc

Issue 8297004: Allow embedders to provide custom message delivery for an isolate. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/vm/message_queue.h ('k') | runtime/vm/message_queue_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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) {
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
OLDNEW
« no previous file with comments | « runtime/vm/message_queue.h ('k') | runtime/vm/message_queue_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698