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 // toggleWrite(handle) - Modifies the set of signals that an application isolate |
| 37 // wishes to be notified about. Whether the application isolate should be |
| 38 // be notified about a handle ready for writing is made the opposite. |
| 39 // |
| 40 // close(handle) - Notifies the HandleWatcherIsolate that a handle it is |
| 41 // watching should be removed from its set and closed. |
| 42 class MojoHandleWatcher { |
| 43 // Control commands. |
| 44 static const int ADD = 0; |
| 45 static const int REMOVE = 1; |
| 46 static const int TOGGLE_WRITE = 2; |
| 47 static const int CLOSE = 3; |
| 48 static const int SHUTDOWN = 4; |
| 49 |
| 50 static int _encodeCommand(int cmd, [int signals = 0]) => |
| 51 (cmd << 2) | (signals & MojoHandleSignals.READWRITE); |
| 52 static int _decodeCommand(int cmd) => cmd >> 2; |
| 53 |
| 54 // The Mojo handle over which control messages are sent. |
| 55 int _controlHandle; |
| 56 |
| 57 // Whether the handle watcher should shut down. |
| 58 bool _shutdown; |
| 59 |
| 60 // The list of handles being watched. |
| 61 List<int> _handles; |
| 62 int _handleCount; |
| 63 |
| 64 // A port for each handle on which to send events back to the isolate that |
| 65 // owns the handle. |
| 66 List<SendPort> _ports; |
| 67 |
| 68 // The signals that we care about for each handle. |
| 69 List<int> _signals; |
| 70 |
| 71 // A mapping from Mojo handles to their indices in _handles. |
| 72 Map<int, int> _handleIndices; |
| 73 |
| 74 // Since we are not storing wrapped handles, a dummy handle for when we need |
| 75 // a RawMojoHandle. |
| 76 RawMojoHandle _tempHandle; |
| 77 |
| 78 MojoHandleWatcher(this._controlHandle) : |
| 79 _shutdown = false, |
| 80 _handles = new List<int>(), |
| 81 _ports = new List<SendPort>(), |
| 82 _signals = new List<int>(), |
| 83 _handleIndices = new Map<int, int>(), |
| 84 _handleCount = 1, |
| 85 _tempHandle = new RawMojoHandle(RawMojoHandle.INVALID) { |
| 86 // Setup control handle. |
| 87 _handles.add(_controlHandle); |
| 88 _ports.add(null); // There is no port for the control handle. |
| 89 _signals.add(MojoHandleSignals.READABLE); |
| 90 _handleIndices[_controlHandle] = 0; |
| 91 } |
| 92 |
| 93 static void _handleWatcherIsolate(MojoHandleWatcher watcher) { |
| 94 while (!watcher._shutdown) { |
| 95 int res = RawMojoHandle.waitMany(watcher._handles, |
| 96 watcher._signals, |
| 97 RawMojoHandle.DEADLINE_INDEFINITE); |
| 98 if (res == 0) { |
| 99 watcher._handleControlMessage(); |
| 100 } else if (res > 0) { |
| 101 int handle = watcher._handles[res]; |
| 102 // Route event. |
| 103 watcher._routeEvent(res); |
| 104 // Remove the handle from the list. |
| 105 watcher._removeHandle(handle); |
| 106 } else if (res == MojoResult.kFailedPrecondition) { |
| 107 // None of the handles can ever be satisfied, including the control |
| 108 // handle. This probably means we are going down. Clean up and |
| 109 // shutdown. |
| 110 watcher._pruneClosedHandles(); |
| 111 watcher._shutdown = true; |
| 112 } else { |
| 113 // Some handle was closed, but not by us. |
| 114 // We have to go through the list and find it. |
| 115 watcher._pruneClosedHandles(); |
| 116 } |
| 117 } |
| 118 } |
| 119 |
| 120 void _routeEvent(int idx) { |
| 121 int client_handle = _handles[idx]; |
| 122 int signals = _signals[idx]; |
| 123 SendPort port = _ports[idx]; |
| 124 |
| 125 _tempHandle.h = client_handle; |
| 126 bool readyWrite = |
| 127 MojoHandleSignals.isWritable(signals) && _tempHandle.readyWrite(); |
| 128 bool readyRead = |
| 129 MojoHandleSignals.isReadable(signals) && _tempHandle.readyRead(); |
| 130 if (readyRead && readyWrite) { |
| 131 port.send(MojoHandleSignals.READWRITE); |
| 132 } else if (readyWrite) { |
| 133 port.send(MojoHandleSignals.WRITABLE); |
| 134 } else if (readyRead) { |
| 135 port.send(MojoHandleSignals.READABLE); |
| 136 } |
| 137 _tempHandle.h = RawMojoHandle.INVALID; |
| 138 } |
| 139 |
| 140 void _handleControlMessage() { |
| 141 List result = _MojoHandleWatcherNatives.recvControlData(_controlHandle); |
| 142 // result[0] = mojo handle if any |
| 143 // result[1] = SendPort if any |
| 144 // result[2] = command << 2 | WRITABLE | READABLE |
| 145 |
| 146 int signals = result[2] & MojoHandleSignals.READWRITE; |
| 147 int command = _decodeCommand(result[2]); |
| 148 switch (command) { |
| 149 case ADD: |
| 150 _addHandle(result[0], result[1], signals); |
| 151 break; |
| 152 case REMOVE: |
| 153 _removeHandle(result[0]); |
| 154 break; |
| 155 case TOGGLE_WRITE: |
| 156 _toggleWrite(result[0]); |
| 157 break; |
| 158 case CLOSE: |
| 159 _close(result[0]); |
| 160 break; |
| 161 case SHUTDOWN: |
| 162 _shutdown = true; |
| 163 break; |
| 164 default: |
| 165 throw new Exception("Invalid Command: $command"); |
| 166 break; |
| 167 } |
| 168 } |
| 169 |
| 170 void _addHandle(int mojoHandle, SendPort port, int signals) { |
| 171 _handles.add(mojoHandle); |
| 172 _ports.add(port); |
| 173 _signals.add(signals & MojoHandleSignals.READWRITE); |
| 174 _handleIndices[mojoHandle] = _handleCount; |
| 175 _handleCount++; |
| 176 } |
| 177 |
| 178 void _removeHandle(int mojoHandle) { |
| 179 int idx = _handleIndices[mojoHandle]; |
| 180 if (idx == null) { |
| 181 throw new Exception("Remove on a non-existent handle: idx = $idx."); |
| 182 } |
| 183 if (idx == 0) { |
| 184 throw new Exception("The control handle (idx = 0) cannot be removed."); |
| 185 } |
| 186 // We don't use List.removeAt so that we know how to fix-up _handleIndices. |
| 187 if (idx == _handleCount - 1) { |
| 188 int handle = _handles[idx]; |
| 189 _handleIndices[handle] = null; |
| 190 _handles.removeLast(); |
| 191 _signals.removeLast(); |
| 192 _ports.removeLast(); |
| 193 _handleCount--; |
| 194 } else { |
| 195 int last = _handleCount - 1; |
| 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) { |
| 208 int idx = _handleIndices[mojoHandle]; |
| 209 if (idx == null) { |
| 210 throw new Exception("Close on a non-existent handle: idx = $idx."); |
| 211 } |
| 212 if (idx == 0) { |
| 213 throw new Exception("The control handle (idx = 0) cannot be closed."); |
| 214 } |
| 215 _tempHandle.h = _handles[idx]; |
| 216 _tempHandle.close(); |
| 217 _tempHandle.h = RawMojoHandle.INVALID; |
| 218 _removeHandle(mojoHandle); |
| 219 } |
| 220 |
| 221 void _toggleWrite(int mojoHandle) { |
| 222 int idx = _handleIndices[mojoHandle]; |
| 223 if (idx == null) { |
| 224 throw new Exception("Toggle write on a non-existent handle: idx = $idx."); |
| 225 } |
| 226 if (idx == 0) { |
| 227 throw new Exception("The control handle (idx = 0) cannot be toggled."); |
| 228 } |
| 229 _signals[idx] = MojoHandleSignals.toggleWrite(_signals[idx]); |
| 230 } |
| 231 |
| 232 void _pruneClosedHandles() { |
| 233 List<int> closed = new List(); |
| 234 for (var h in _handles) { |
| 235 _tempHandle.h = h; |
| 236 MojoResult res = _tempHandle.wait(MojoHandleSignals.READWRITE, 0); |
| 237 if ((!res.isOk) && (!res.isDeadlineExceeded)) { |
| 238 closed.add(h); |
| 239 } |
| 240 _tempHandle.h = RawMojoHandle.INVALID; |
| 241 } |
| 242 for (var h in closed) { |
| 243 _close(h); |
| 244 } |
| 245 } |
| 246 |
| 247 static MojoResult _sendControlData(RawMojoHandle mojoHandle, |
| 248 SendPort port, |
| 249 int data) { |
| 250 int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); |
| 251 if (controlHandle == RawMojoHandle.INVALID) { |
| 252 throw new Exception("Found invalid control handle"); |
| 253 } |
| 254 var result = _MojoHandleWatcherNatives.sendControlData( |
| 255 controlHandle, mojoHandle.h, port, data); |
| 256 return new MojoResult(result); |
| 257 } |
| 258 |
| 259 static Future<Isolate> Start() { |
| 260 // 5. Return Future<bool> giving true on success. |
| 261 |
| 262 // Make a control message pipe, |
| 263 MojoMessagePipe pipe = new MojoMessagePipe(); |
| 264 int consumerHandle = pipe.endpoints[0].handle.h; |
| 265 int producerHandle = pipe.endpoints[1].handle.h; |
| 266 |
| 267 // Make a MojoHandleWatcher with one end. |
| 268 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle); |
| 269 |
| 270 // Call setControlHandle with the other end. |
| 271 _MojoHandleWatcherNatives.setControlHandle(producerHandle); |
| 272 |
| 273 // Spawn the handle watcher isolate with the MojoHandleWatcher, |
| 274 return Isolate.spawn(_handleWatcherIsolate, watcher); |
| 275 } |
| 276 |
| 277 static void Stop() { |
| 278 _sendControlData(RawMojoHandle.INVALID, null, _encodeCommand(SHUTDOWN)); |
| 279 } |
| 280 |
| 281 static MojoResult close(RawMojoHandle mojoHandle) { |
| 282 return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)); |
| 283 } |
| 284 |
| 285 static MojoResult toggleWrite(RawMojoHandle mojoHandle) { |
| 286 return _sendControlData(mojoHandle, null, _encodeCommand(TOGGLE_WRITE)); |
| 287 } |
| 288 |
| 289 static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) { |
| 290 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals)); |
| 291 } |
| 292 |
| 293 static MojoResult remove(RawMojoHandle mojoHandle) { |
| 294 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE)); |
| 295 } |
| 296 } |
OLD | NEW |