| 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 |