Index: mojo/public/dart/system/lib/src/handle_watcher.dart |
diff --git a/mojo/public/dart/system/lib/src/handle_watcher.dart b/mojo/public/dart/system/lib/src/handle_watcher.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4cc3dc8f12b047fa5df0a77c9556508cca01e80b |
--- /dev/null |
+++ b/mojo/public/dart/system/lib/src/handle_watcher.dart |
@@ -0,0 +1,248 @@ |
+// 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 control_handle, int mojo_handle, SendPort port, int data) |
+ native "MojoHandleWatcher_SendControlData"; |
+ static List recvControlData(int control_handle) |
+ native "MojoHandleWatcher_RecvControlData"; |
+ static int setControlHandle(int control_handle) |
+ native "MojoHandleWatcher_SetControlHandle"; |
+ static int getControlHandle() |
+ native "MojoHandleWatcher_GetControlHandle"; |
+} |
+ |
+ |
+class MojoHandleWatcher { |
+ 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; |
+ |
+ RawMojoHandle _dummy_handle; |
+ int _control_handle; |
+ bool _shutdown; |
+ List<int> _handles; |
+ List<SendPort> _ports; |
+ List<int> _signals; |
+ int _handle_count; |
+ Map<int, int> _handle_indices; |
+ |
+ MojoHandleWatcher(this._control_handle) : |
+ _shutdown = false, |
+ _ports = [null], |
+ _signals = [MojoHandleSignals.READABLE], |
+ _handle_count = 1 { |
+ _handles = [_control_handle]; |
+ _handle_indices = new Map(); |
+ _handle_indices[_control_handle] = 0; |
+ _dummy_handle = new RawMojoHandle(RawMojoHandle.INVALID); |
+ } |
+ |
+ static void _handleWatcherIsolate(MojoHandleWatcher eh) { |
+ while (!eh._shutdown) { |
+ int res = RawMojoHandle.waitMany(eh._handles, |
+ eh._signals, |
+ RawMojoHandle.DEADLINE_INDEFINITE); |
+ if (res == 0) { |
+ eh._handleControlMessage(); |
+ } else if (res > 0) { |
+ int handle = eh._handles[res]; |
+ // Route event. |
+ eh._routeEvent(res); |
+ // Remove the handle from the list. |
+ eh._removeHandle(handle); |
+ } else if (res == MojoResult.FAILED_PRECONDITION) { |
+ // None of the handles can ever be satisfied, including the control |
+ // handle. This probably means we are going down. Clean up and |
+ // shutdown. |
+ eh._pruneClosedHandles(); |
+ eh._shutdown = true; |
+ } else { |
+ // Some handle is closed. We have to go through the list and find it. |
+ eh._pruneClosedHandles(); |
+ } |
+ } |
+ } |
+ |
+ void _routeEvent(int idx) { |
+ int client_handle = _handles[idx]; |
+ int signals = _signals[idx]; |
+ SendPort port = _ports[idx]; |
+ |
+ _dummy_handle.h = client_handle; |
+ bool readyWrite = |
+ MojoHandleSignals.isWritable(signals) && _dummy_handle.readyWrite(); |
+ bool readyRead = |
+ MojoHandleSignals.isReadable(signals) && _dummy_handle.readyRead(); |
+ if (readyRead && readyWrite) { |
+ //print("routing READWRITE for handle $client_handle"); |
+ port.send(MojoHandleSignals.READWRITE); |
+ } else if (readyWrite) { |
+ //print("routing WRITABLE for handle $client_handle"); |
+ port.send(MojoHandleSignals.WRITABLE); |
+ } else if (readyRead) { |
+ //print("routing READABLE for handle $client_handle"); |
+ port.send(MojoHandleSignals.READABLE); |
+ } |
+ _dummy_handle.h = RawMojoHandle.INVALID; |
+ } |
+ |
+ void _handleControlMessage() { |
+ List result = _MojoHandleWatcherNatives.recvControlData(_control_handle); |
+ // result[0] = mojo handle if any |
+ // result[1] = SendPort if any |
+ // result[2] = command << 2 | WRITABLE | READABLE |
+ |
+ int signals = result[2] & MojoHandleSignals.READWRITE; |
+ switch (result[2] >> 2) { |
+ 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: |
+ break; |
+ } |
+ } |
+ |
+ void _addHandle(int mojo_handle, SendPort port, int signals) { |
+ _handles.add(mojo_handle); |
+ _ports.add(port); |
+ _signals.add(signals & MojoHandleSignals.READWRITE); |
+ _handle_indices[mojo_handle] = _handle_count; |
+ _handle_count++; |
+ } |
+ |
+ void _removeHandle(int mojo_handle) { |
+ int idx = _handle_indices[mojo_handle]; |
+ if (idx == null) { |
+ return; |
+ } |
+ if (idx == 0) { |
+ // Cannot remove control handle. |
+ return; |
+ } |
+ if (idx == _handle_count - 1) { |
+ int handle = _handles[idx]; |
+ _handle_indices[handle] = null; |
+ _handles.removeLast(); |
+ _signals.removeLast(); |
+ _ports.removeLast(); |
+ _handle_count--; |
+ } else { |
+ int last = _handle_count - 1; |
+ _handles[idx] = _handles[last]; |
+ _signals[idx] = _signals[last]; |
+ _ports[idx] = _ports[last]; |
+ _handles.removeLast(); |
+ _signals.removeLast(); |
+ _ports.removeLast(); |
+ _handle_indices[_handles[idx]] = idx; |
+ _handle_count--; |
+ } |
+ } |
+ |
+ void _close(int mojo_handle) { |
+ int idx = _handle_indices[mojo_handle]; |
+ if (idx == null) { |
+ return; |
+ } |
+ if (idx == 0) { |
+ // Cannot remove control handle. |
+ return; |
+ } |
+ _dummy_handle.h = _handles[idx]; |
+ _dummy_handle.close(); |
+ _dummy_handle.h = RawMojoHandle.INVALID; |
+ _removeHandle(mojo_handle); |
+ } |
+ |
+ void _toggleWrite(int mojo_handle) { |
+ int idx = _handle_indices[mojo_handle]; |
+ if (idx == null) { |
+ return; |
+ } |
+ if (idx == 0) { |
+ // Cannot toggle the control handle. |
+ return; |
+ } |
+ _signals[idx] = MojoHandleSignals.toggleWrite(_signals[idx]); |
+ } |
+ |
+ void _pruneClosedHandles() { |
+ List<int> closed = new List(); |
+ for (var h in _handles) { |
+ int res = 0; |
+ _dummy_handle.h = h; |
+ res = _dummy_handle.wait(MojoHandleSignals.READWRITE, 0); |
+ if ((res != MojoResult.OK) && (res != MojoResult.DEADLINE_EXCEEDED)) { |
+ closed.add(h); |
+ } |
+ _dummy_handle.h = RawMojoHandle.INVALID; |
+ } |
+ |
+ for (var h in closed) { |
+ _close(h); |
+ } |
+ } |
+ |
+ static Future<bool> Start() { |
+ // 1. make control message pipe, |
+ // 2. make MojoHandleWatcher with one end, |
+ // 3. setControlHandle with the other end. |
+ // 4. spawn isolate with MojoHandleWatcher, |
+ // 5. return Future<bool> giving true on success. |
+ MojoMessagePipe pipe = new MojoMessagePipe(); |
+ int consumer_handle = pipe.endpoints[0].handle.h; |
+ int producer_handle = pipe.endpoints[1].handle.h; |
+ MojoHandleWatcher eh = new MojoHandleWatcher(consumer_handle); |
+ _MojoHandleWatcherNatives.setControlHandle(producer_handle); |
+ return Isolate.spawn(_handleWatcherIsolate, eh).then((isolate) { |
+ return true; |
+ }); |
+ } |
+ |
+ static void Stop() { |
+ sendControlData(RawMojoHandle.INVALID, null, SHUTDOWN << 2); |
+ } |
+ |
+ static int sendControlData(int mojo_handle, SendPort port, int data) { |
+ int control_handle = _MojoHandleWatcherNatives.getControlHandle(); |
+ if (control_handle == RawMojoHandle.INVALID) { |
+ throw new Exception("Found invalid control handle"); |
+ } |
+ return _MojoHandleWatcherNatives.sendControlData( |
+ control_handle, mojo_handle, port, data); |
+ } |
+ |
+ static int close(int mojo_handle) { |
+ return sendControlData(mojo_handle, null, CLOSE << 2); |
+ } |
+ |
+ static int toggleWrite(int mojo_handle) { |
+ return sendControlData(mojo_handle, null, TOGGLE_WRITE << 2); |
+ } |
+ |
+ static int add(int mojo_handle, SendPort port, int signals) { |
+ return sendControlData( |
+ mojo_handle, |
+ port, |
+ (ADD << 2) | (signals & MojoHandleSignals.READWRITE)); |
+ } |
+} |