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