OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 part of internal; | |
6 | |
7 // The MojoHandleWatcher sends a stream of events to application isolates that | |
8 // register Mojo handles with it. Application isolates make the following calls: | |
9 // | |
10 // add(handle, port, signals) - Instructs the MojoHandleWatcher isolate to add | |
11 // 'handle' to the set of handles it watches, and to notify the calling | |
12 // isolate only for the events specified by 'signals' using the send port | |
13 // 'port' | |
14 // | |
15 // remove(handle) - Instructs the MojoHandleWatcher isolate to remove 'handle' | |
16 // from the set of handles it watches. This allows the application isolate | |
17 // to, e.g., pause the stream of events. | |
18 // | |
19 // close(handle) - Notifies the HandleWatcherIsolate that a handle it is | |
20 // watching should be removed from its set and closed. | |
21 class MojoHandleWatcher { | |
22 // Control commands. | |
23 static const int ADD = 0; | |
24 static const int REMOVE = 1; | |
25 static const int CLOSE = 2; | |
26 static const int TIMER = 3; | |
27 static const int SHUTDOWN = 4; | |
28 | |
29 static const int kMojoHandleInvalid = 0; | |
30 static const int kDeadlineIndefinite = -1; | |
31 | |
32 static const int kMojoResultOk = 0; | |
33 static const int kMojoResultDeadlineExceeded = 4; | |
34 static const int kMojoResultFailedPrecondition = 9; | |
35 | |
36 static const int kMojoSignalsReadable = (1 << 0); | |
37 static const int kMojoSignalsWritable = (1 << 1); | |
38 static const int kMojoSignalsPeerClosed = (1 << 2); | |
39 static const int kMojoSignalsAll = | |
40 kMojoSignalsReadable | kMojoSignalsWritable | kMojoSignalsPeerClosed; | |
41 | |
42 static int _encodeCommand(int cmd, [int signals = 0]) => | |
43 (cmd << 3) | (signals & kMojoSignalsAll); | |
44 static int _decodeCommand(int cmd) { | |
45 assert(kMojoSignalsAll < 1 << 3); | |
46 return cmd >> 3; | |
47 } | |
48 static int _decodeSignals(int cmd) { | |
49 return cmd & kMojoSignalsAll; | |
50 } | |
51 | |
52 // The Mojo handle over which control messages are sent. | |
53 int _controlHandle; | |
54 | |
55 // Whether the handle watcher should shut down. | |
56 bool _shutdown; | |
57 | |
58 // The list of handles being watched. | |
59 List<int> _handles; | |
60 int _handleCount; | |
61 | |
62 // A port for each handle on which to send events back to the isolate that | |
63 // owns the handle. | |
64 List<SendPort> _ports; | |
65 | |
66 // The signals that we care about for each handle. | |
67 List<int> _signals; | |
68 | |
69 // A mapping from Mojo handles to their indices in _handles. | |
70 Map<int, int> _handleIndices; | |
71 | |
72 // Priority queue of timers registered with the watcher. | |
73 TimerQueue _timerQueue; | |
74 | |
75 MojoHandleWatcher(this._controlHandle) | |
76 : _shutdown = false, | |
77 _handles = new List<int>(), | |
78 _ports = new List<SendPort>(), | |
79 _signals = new List<int>(), | |
80 _handleIndices = new Map<int, int>(), | |
81 _handleCount = 1, | |
82 _timerQueue = new TimerQueue() { | |
83 // Setup control handle. | |
84 _handles.add(_controlHandle); | |
85 _ports.add(null); // There is no port for the control handle. | |
86 _signals.add(kMojoSignalsReadable); | |
87 _handleIndices[_controlHandle] = 0; | |
88 } | |
89 | |
90 static void _handleWatcherIsolate(int consumerHandle) { | |
91 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle); | |
92 while (!watcher._shutdown) { | |
93 int deadline = watcher._processTimerDeadlines(); | |
94 // mwmr[0]: result, mwmr[1]: index, mwmr[2]: list of signal states. | |
95 List mwmr = MojoHandleNatives.waitMany( | |
96 watcher._handles, watcher._signals, deadline); | |
97 if ((mwmr[0] == kMojoResultOk) && (mwmr[1] == 0)) { | |
98 watcher._handleControlMessage(); | |
99 } else if ((mwmr[0] == kMojoResultOk) && (mwmr[1] > 0)) { | |
100 int handle = watcher._handles[mwmr[1]]; | |
101 | |
102 // Route event. | |
103 watcher._routeEvent(mwmr[2][mwmr[1]][0], mwmr[1]); | |
104 // Remove the handle from the list. | |
105 watcher._removeHandle(handle); | |
106 } else if (mwmr[0] != kMojoResultDeadlineExceeded) { | |
107 // Some handle was closed, but not by us. | |
108 // Find it and close it on our side. | |
109 watcher._pruneClosedHandles(mwmr[2]); | |
110 } | |
111 } | |
112 } | |
113 | |
114 void _routeEvent(int satisfiedSignals, int idx) { | |
115 _ports[idx].send([_signals[idx], satisfiedSignals & _signals[idx]]); | |
116 } | |
117 | |
118 void _handleControlMessage() { | |
119 List result = MojoHandleWatcherNatives.recvControlData(_controlHandle); | |
120 // result[0] = mojo handle if any, or a timer deadline in milliseconds. | |
121 // result[1] = SendPort if any. | |
122 // result[2] = command << 2 | WRITABLE | READABLE | |
123 | |
124 var signals = _decodeSignals(result[2]); | |
125 int command = _decodeCommand(result[2]); | |
126 switch (command) { | |
127 case ADD: | |
128 _addHandle(result[0], result[1], signals); | |
129 break; | |
130 case REMOVE: | |
131 _removeHandle(result[0]); | |
132 break; | |
133 case CLOSE: | |
134 _close(result[0], result[1]); | |
135 break; | |
136 case TIMER: | |
137 _timer(result[1], result[0]); | |
138 break; | |
139 case SHUTDOWN: | |
140 _shutdownHandleWatcher(result[1]); | |
141 break; | |
142 default: | |
143 throw "Invalid Command: $command"; | |
144 break; | |
145 } | |
146 } | |
147 | |
148 void _addHandle(int mojoHandle, SendPort port, int signals) { | |
149 int idx = _handleIndices[mojoHandle]; | |
150 if (idx == null) { | |
151 _handles.add(mojoHandle); | |
152 _ports.add(port); | |
153 _signals.add(signals); | |
154 _handleIndices[mojoHandle] = _handleCount; | |
155 _handleCount++; | |
156 } else { | |
157 assert(_ports[idx] == port); | |
158 assert(_handles[idx] == mojoHandle); | |
159 _signals[idx] |= signals; | |
160 } | |
161 } | |
162 | |
163 void _removeHandle(int mojoHandle) { | |
164 int idx = _handleIndices[mojoHandle]; | |
165 if (idx == null) { | |
166 throw "Remove on a non-existent handle: idx = $idx."; | |
167 } | |
168 if (idx == 0) { | |
169 throw "The control handle (idx = 0) cannot be removed."; | |
170 } | |
171 // We don't use List.removeAt so that we know how to fix-up _handleIndices. | |
172 if (idx == _handleCount - 1) { | |
173 int handle = _handles[idx]; | |
174 _handleIndices[handle] = null; | |
175 _handles.removeLast(); | |
176 _signals.removeLast(); | |
177 _ports.removeLast(); | |
178 _handleCount--; | |
179 } else { | |
180 int last = _handleCount - 1; | |
181 _handleIndices[_handles[idx]] = null; | |
182 _handles[idx] = _handles[last]; | |
183 _signals[idx] = _signals[last]; | |
184 _ports[idx] = _ports[last]; | |
185 _handles.removeLast(); | |
186 _signals.removeLast(); | |
187 _ports.removeLast(); | |
188 _handleIndices[_handles[idx]] = idx; | |
189 _handleCount--; | |
190 } | |
191 } | |
192 | |
193 void _close(int mojoHandle, SendPort port, {bool pruning: false}) { | |
194 assert(!pruning || (port == null)); | |
195 int idx = _handleIndices[mojoHandle]; | |
196 if (idx == null) { | |
197 // An app isolate may request that the handle watcher close a handle that | |
198 // has already been pruned. This happens when the app isolate has not yet | |
199 // received the PEER_CLOSED event. The app isolate will not close the | |
200 // handle, so we must do so here. | |
201 MojoHandleNatives.close(mojoHandle); | |
202 if (port != null) port.send(null); // Notify that close is done. | |
203 return; | |
204 } | |
205 if (idx == 0) { | |
206 throw "The control handle (idx = 0) cannot be closed."; | |
207 } | |
208 MojoHandleNatives.close(_handles[idx]); | |
209 if (port != null) port.send(null); // Notify that close is done. | |
210 if (pruning) { | |
211 // If this handle is being pruned, notify the application isolate | |
212 // by sending MojoHandleSignals.PEER_CLOSED. | |
213 _ports[idx].send([_signals[idx], kMojoSignalsPeerClosed]); | |
214 } | |
215 _removeHandle(mojoHandle); | |
216 } | |
217 | |
218 // Returns the next timer deadline in units of microseconds from 'now'. | |
219 int _processTimerDeadlines() { | |
220 int now = (new DateTime.now()).millisecondsSinceEpoch; | |
221 while (_timerQueue.hasTimer && (now >= _timerQueue.currentTimeout)) { | |
222 _timerQueue.currentPort.send(null); | |
223 _timerQueue.removeCurrent(); | |
224 now = (new DateTime.now()).millisecondsSinceEpoch; | |
225 } | |
226 return _timerQueue.hasTimer | |
227 ? (_timerQueue.currentTimeout - now) * 1000 | |
228 : kDeadlineIndefinite; | |
229 } | |
230 | |
231 void _timer(SendPort port, int deadline) { | |
232 _timerQueue.updateTimer(port, deadline); | |
233 } | |
234 | |
235 void _pruneClosedHandles(List<List<int>> states) { | |
236 List<int> closed = new List(); | |
237 for (var i = 0; i < _handles.length; i++) { | |
238 if (states != null) { | |
239 int signals = states[i][0]; | |
240 if ((signals & kMojoSignalsPeerClosed) != 0) { | |
241 closed.add(_handles[i]); | |
242 } | |
243 } else { | |
244 List mwr = MojoHandleNatives.wait(_handles[i], kMojoSignalsAll, 0); | |
245 if ((mwr[0] != kMojoResultOk) && | |
246 (mwr[0] != kMojoResultDeadlineExceeded)) { | |
247 closed.add(_handles[i]); | |
248 } | |
249 } | |
250 } | |
251 for (var h in closed) { | |
252 _close(h, null, pruning: true); | |
253 } | |
254 // '_close' updated the '_handles' array, so at this point the '_handles' | |
255 // array and the caller's 'states' array are mismatched. | |
256 } | |
257 | |
258 void _shutdownHandleWatcher(SendPort shutdownSendPort) { | |
259 _shutdown = true; | |
260 MojoHandleNatives.close(_controlHandle); | |
261 shutdownSendPort.send(null); | |
262 } | |
263 | |
264 static int _sendControlData(int rawHandle, SendPort port, int data) { | |
265 int controlHandle = MojoHandleWatcherNatives.getControlHandle(); | |
266 if (controlHandle == kMojoHandleInvalid) { | |
267 return kMojoResultFailedPrecondition; | |
268 } | |
269 | |
270 var result = MojoHandleWatcherNatives.sendControlData( | |
271 controlHandle, rawHandle, port, data); | |
272 return result; | |
273 } | |
274 | |
275 // Starts up the MojoHandleWatcher isolate. Should be called only once | |
276 // per VM process. | |
277 static Future<Isolate> _start() { | |
278 // Make a control message pipe, | |
279 List pipeEndpoints = MojoMessagePipeNatives.MojoCreateMessagePipe(0); | |
280 assert(pipeEndpoints != null); | |
281 assert((pipeEndpoints is List) && (pipeEndpoints.length == 3)); | |
282 assert(pipeEndpoints[0] == kMojoResultOk); | |
283 | |
284 int consumerHandle = pipeEndpoints[1]; | |
285 int producerHandle = pipeEndpoints[2]; | |
286 | |
287 // Call setControlHandle with the other end. | |
288 assert(producerHandle != kMojoHandleInvalid); | |
289 MojoHandleWatcherNatives.setControlHandle(producerHandle); | |
290 | |
291 // Spawn the handle watcher isolate with the MojoHandleWatcher, | |
292 return Isolate.spawn(_handleWatcherIsolate, consumerHandle); | |
293 } | |
294 | |
295 // Causes the MojoHandleWatcher isolate to exit. Should be called only | |
296 // once per VM process. | |
297 static void _stop() { | |
298 // Create a port for notification that the handle watcher has shutdown. | |
299 var shutdownReceivePort = new ReceivePort(); | |
300 var shutdownSendPort = shutdownReceivePort.sendPort; | |
301 | |
302 // Send the shutdown command. | |
303 _sendControlData( | |
304 kMojoHandleInvalid, shutdownSendPort, _encodeCommand(SHUTDOWN)); | |
305 | |
306 // Close the control handle. | |
307 int controlHandle = MojoHandleWatcherNatives.getControlHandle(); | |
308 MojoHandleNatives.close(controlHandle); | |
309 | |
310 // Invalidate the control handle. | |
311 MojoHandleWatcherNatives.setControlHandle(kMojoHandleInvalid); | |
312 | |
313 // Wait for the handle watcher isolate to exit. | |
314 shutdownReceivePort.first.then((_) { | |
315 shutdownReceivePort.close(); | |
316 }); | |
317 } | |
318 | |
319 // If wait is true, returns a future that resolves only after the handle | |
320 // has actually been closed by the handle watcher. Otherwise, returns a | |
321 // future that resolves immediately. | |
322 static Future<int> close(int mojoHandle, {bool wait: false}) { | |
323 //assert(MojoHandle._removeUnclosedHandle(mojoHandle)); | |
324 if (!wait) { | |
325 return new Future.value( | |
326 _sendControlData(mojoHandle, null, _encodeCommand(CLOSE))); | |
327 } | |
328 int result; | |
329 var completer = new Completer(); | |
330 var rawPort = new RawReceivePort((_) { | |
331 completer.complete(result); | |
332 }); | |
333 result = | |
334 _sendControlData(mojoHandle, rawPort.sendPort, _encodeCommand(CLOSE)); | |
335 return completer.future.then((r) { | |
336 rawPort.close(); | |
337 return r; | |
338 }); | |
339 } | |
340 | |
341 static int add(int mojoHandle, SendPort port, int signals) { | |
342 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals)); | |
343 } | |
344 | |
345 static int remove(int mojoHandle) { | |
346 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE)); | |
347 } | |
348 | |
349 static int timer(Object ignored, SendPort port, int deadline) { | |
350 // The deadline will be unwrapped before sending to the handle watcher. | |
351 return _sendControlData(deadline, port, _encodeCommand(TIMER)); | |
352 } | |
353 } | |
OLD | NEW |