Index: mojo/public/dart/src/handle_watcher.dart |
diff --git a/mojo/public/dart/src/handle_watcher.dart b/mojo/public/dart/src/handle_watcher.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..47f36756bba1777b742d92980de5f9834b86eb3b |
--- /dev/null |
+++ b/mojo/public/dart/src/handle_watcher.dart |
@@ -0,0 +1,296 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+part of core; |
+ |
+class _MojoHandleWatcherNatives { |
+ static int sendControlData( |
+ int controlHandle, int mojoHandle, SendPort port, int data) |
+ native "MojoHandleWatcher_SendControlData"; |
+ static List recvControlData(int controlHandle) |
+ native "MojoHandleWatcher_RecvControlData"; |
+ static int setControlHandle(int controlHandle) |
+ native "MojoHandleWatcher_SetControlHandle"; |
+ static int getControlHandle() |
+ native "MojoHandleWatcher_GetControlHandle"; |
+} |
+ |
+// The MojoHandleWatcher sends a stream of events to application isolates that |
+// register Mojo handles with it. Application isolates make the following calls: |
+// |
+// Start() - Starts up the MojoHandleWatcher isolate. Should be called only once |
+// per VM process. |
+// |
+// Stop() - Causes the MojoHandleWatcher isolate to exit. |
+// |
+// add(handle, port, signals) - Instructs the MojoHandleWatcher isolate to add |
+// 'handle' to the set of handles it watches, and to notify the calling |
+// isolate only for the events specified by 'signals' using the send port |
+// 'port' |
+// |
+// remove(handle) - Instructs the MojoHandleWatcher isolate to remove 'handle' |
+// from the set of handles it watches. This allows the application isolate |
+// to, e.g., pause the stream of events. |
+// |
+// toggleWrite(handle) - Modifies the set of signals that an application isolate |
+// wishes to be notified about. Whether the application isolate should be |
+// be notified about a handle ready for writing is made the opposite. |
+// |
+// close(handle) - Notifies the HandleWatcherIsolate that a handle it is |
+// watching should be removed from its set and closed. |
+class MojoHandleWatcher { |
+ // Control commands. |
+ static const int ADD = 0; |
+ static const int REMOVE = 1; |
+ static const int TOGGLE_WRITE = 2; |
+ static const int CLOSE = 3; |
+ static const int SHUTDOWN = 4; |
+ |
+ static int _encodeCommand(int cmd, [int signals = 0]) => |
+ (cmd << 2) | (signals & MojoHandleSignals.READWRITE); |
+ static int _decodeCommand(int cmd) => cmd >> 2; |
+ |
+ // The Mojo handle over which control messages are sent. |
+ int _controlHandle; |
+ |
+ // Whether the handle watcher should shut down. |
+ bool _shutdown; |
+ |
+ // The list of handles being watched. |
+ List<int> _handles; |
+ int _handleCount; |
+ |
+ // A port for each handle on which to send events back to the isolate that |
+ // owns the handle. |
+ List<SendPort> _ports; |
+ |
+ // The signals that we care about for each handle. |
+ List<int> _signals; |
+ |
+ // A mapping from Mojo handles to their indices in _handles. |
+ Map<int, int> _handleIndices; |
+ |
+ // Since we are not storing wrapped handles, a dummy handle for when we need |
+ // a RawMojoHandle. |
+ RawMojoHandle _tempHandle; |
+ |
+ MojoHandleWatcher(this._controlHandle) : |
+ _shutdown = false, |
+ _handles = new List<int>(), |
+ _ports = new List<SendPort>(), |
+ _signals = new List<int>(), |
+ _handleIndices = new Map<int, int>(), |
+ _handleCount = 1, |
+ _tempHandle = new RawMojoHandle(RawMojoHandle.INVALID) { |
+ // Setup control handle. |
+ _handles.add(_controlHandle); |
+ _ports.add(null); // There is no port for the control handle. |
+ _signals.add(MojoHandleSignals.READABLE); |
+ _handleIndices[_controlHandle] = 0; |
+ } |
+ |
+ static void _handleWatcherIsolate(MojoHandleWatcher watcher) { |
+ while (!watcher._shutdown) { |
+ int res = RawMojoHandle.waitMany(watcher._handles, |
+ watcher._signals, |
+ RawMojoHandle.DEADLINE_INDEFINITE); |
+ if (res == 0) { |
+ watcher._handleControlMessage(); |
+ } else if (res > 0) { |
+ int handle = watcher._handles[res]; |
+ // Route event. |
+ watcher._routeEvent(res); |
+ // Remove the handle from the list. |
+ watcher._removeHandle(handle); |
+ } else if (res == MojoResult.kFailedPrecondition) { |
+ // None of the handles can ever be satisfied, including the control |
+ // handle. This probably means we are going down. Clean up and |
+ // shutdown. |
+ watcher._pruneClosedHandles(); |
+ watcher._shutdown = true; |
+ } else { |
+ // Some handle was closed, but not by us. |
+ // We have to go through the list and find it. |
+ watcher._pruneClosedHandles(); |
+ } |
+ } |
+ } |
+ |
+ void _routeEvent(int idx) { |
+ int client_handle = _handles[idx]; |
+ int signals = _signals[idx]; |
+ SendPort port = _ports[idx]; |
+ |
+ _tempHandle.h = client_handle; |
+ bool readyWrite = |
+ MojoHandleSignals.isWritable(signals) && _tempHandle.readyWrite(); |
+ bool readyRead = |
+ MojoHandleSignals.isReadable(signals) && _tempHandle.readyRead(); |
+ if (readyRead && readyWrite) { |
+ port.send(MojoHandleSignals.READWRITE); |
+ } else if (readyWrite) { |
+ port.send(MojoHandleSignals.WRITABLE); |
+ } else if (readyRead) { |
+ port.send(MojoHandleSignals.READABLE); |
+ } |
+ _tempHandle.h = RawMojoHandle.INVALID; |
+ } |
+ |
+ void _handleControlMessage() { |
+ List result = _MojoHandleWatcherNatives.recvControlData(_controlHandle); |
+ // result[0] = mojo handle if any |
+ // result[1] = SendPort if any |
+ // result[2] = command << 2 | WRITABLE | READABLE |
+ |
+ int signals = result[2] & MojoHandleSignals.READWRITE; |
+ int command = _decodeCommand(result[2]); |
+ switch (command) { |
+ case ADD: |
+ _addHandle(result[0], result[1], signals); |
+ break; |
+ case REMOVE: |
+ _removeHandle(result[0]); |
+ break; |
+ case TOGGLE_WRITE: |
+ _toggleWrite(result[0]); |
+ break; |
+ case CLOSE: |
+ _close(result[0]); |
+ break; |
+ case SHUTDOWN: |
+ _shutdown = true; |
+ break; |
+ default: |
+ throw new Exception("Invalid Command: $command"); |
+ break; |
+ } |
+ } |
+ |
+ void _addHandle(int mojoHandle, SendPort port, int signals) { |
+ _handles.add(mojoHandle); |
+ _ports.add(port); |
+ _signals.add(signals & MojoHandleSignals.READWRITE); |
+ _handleIndices[mojoHandle] = _handleCount; |
+ _handleCount++; |
+ } |
+ |
+ void _removeHandle(int mojoHandle) { |
+ int idx = _handleIndices[mojoHandle]; |
+ if (idx == null) { |
+ throw new Exception("Remove on a non-existent handle: idx = $idx."); |
+ } |
+ if (idx == 0) { |
+ throw new Exception("The control handle (idx = 0) cannot be removed."); |
+ } |
+ // We don't use List.removeAt so that we know how to fix-up _handleIndices. |
+ if (idx == _handleCount - 1) { |
+ int handle = _handles[idx]; |
+ _handleIndices[handle] = null; |
+ _handles.removeLast(); |
+ _signals.removeLast(); |
+ _ports.removeLast(); |
+ _handleCount--; |
+ } else { |
+ int last = _handleCount - 1; |
+ _handles[idx] = _handles[last]; |
+ _signals[idx] = _signals[last]; |
+ _ports[idx] = _ports[last]; |
+ _handles.removeLast(); |
+ _signals.removeLast(); |
+ _ports.removeLast(); |
+ _handleIndices[_handles[idx]] = idx; |
+ _handleCount--; |
+ } |
+ } |
+ |
+ void _close(int mojoHandle) { |
+ int idx = _handleIndices[mojoHandle]; |
+ if (idx == null) { |
+ throw new Exception("Close on a non-existent handle: idx = $idx."); |
+ } |
+ if (idx == 0) { |
+ throw new Exception("The control handle (idx = 0) cannot be closed."); |
+ } |
+ _tempHandle.h = _handles[idx]; |
+ _tempHandle.close(); |
+ _tempHandle.h = RawMojoHandle.INVALID; |
+ _removeHandle(mojoHandle); |
+ } |
+ |
+ void _toggleWrite(int mojoHandle) { |
+ int idx = _handleIndices[mojoHandle]; |
+ if (idx == null) { |
+ throw new Exception("Toggle write on a non-existent handle: idx = $idx."); |
+ } |
+ if (idx == 0) { |
+ throw new Exception("The control handle (idx = 0) cannot be toggled."); |
+ } |
+ _signals[idx] = MojoHandleSignals.toggleWrite(_signals[idx]); |
+ } |
+ |
+ void _pruneClosedHandles() { |
+ List<int> closed = new List(); |
+ for (var h in _handles) { |
+ _tempHandle.h = h; |
+ MojoResult res = _tempHandle.wait(MojoHandleSignals.READWRITE, 0); |
+ if ((!res.isOk) && (!res.isDeadlineExceeded)) { |
+ closed.add(h); |
+ } |
+ _tempHandle.h = RawMojoHandle.INVALID; |
+ } |
+ for (var h in closed) { |
+ _close(h); |
+ } |
+ } |
+ |
+ static MojoResult _sendControlData(RawMojoHandle mojoHandle, |
+ SendPort port, |
+ int data) { |
+ int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); |
+ if (controlHandle == RawMojoHandle.INVALID) { |
+ throw new Exception("Found invalid control handle"); |
+ } |
+ var result = _MojoHandleWatcherNatives.sendControlData( |
+ controlHandle, mojoHandle.h, port, data); |
+ return new MojoResult(result); |
+ } |
+ |
+ static Future<Isolate> Start() { |
+ // 5. Return Future<bool> giving true on success. |
+ |
+ // Make a control message pipe, |
+ MojoMessagePipe pipe = new MojoMessagePipe(); |
+ int consumerHandle = pipe.endpoints[0].handle.h; |
+ int producerHandle = pipe.endpoints[1].handle.h; |
+ |
+ // Make a MojoHandleWatcher with one end. |
+ MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle); |
+ |
+ // Call setControlHandle with the other end. |
+ _MojoHandleWatcherNatives.setControlHandle(producerHandle); |
+ |
+ // Spawn the handle watcher isolate with the MojoHandleWatcher, |
+ return Isolate.spawn(_handleWatcherIsolate, watcher); |
+ } |
+ |
+ static void Stop() { |
+ _sendControlData(RawMojoHandle.INVALID, null, _encodeCommand(SHUTDOWN)); |
+ } |
+ |
+ static MojoResult close(RawMojoHandle mojoHandle) { |
+ return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)); |
+ } |
+ |
+ static MojoResult toggleWrite(RawMojoHandle mojoHandle) { |
+ return _sendControlData(mojoHandle, null, _encodeCommand(TOGGLE_WRITE)); |
+ } |
+ |
+ static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) { |
+ return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals)); |
+ } |
+ |
+ static MojoResult remove(RawMojoHandle mojoHandle) { |
+ return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE)); |
+ } |
+} |