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.
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";
19 // The MojoHandleWatcher sends a stream of events to application isolates that
20 // register Mojo handles with it. Application isolates make the following calls:
22 // Start() - Starts up the MojoHandleWatcher isolate. Should be called only once
25 // Stop() - Causes the MojoHandleWatcher isolate to exit.
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
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.
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.
40 // close(handle) - Notifies the HandleWatcherIsolate that a handle it is
41 // watching should be removed from its set and closed.
42 class MojoHandleWatcher {
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;
50 static int _encodeCommand(int cmd, [int signals = 0]) =>
51 (cmd << 2) | (signals & MojoHandleSignals.READWRITE);
52 static int _decodeCommand(int cmd) => cmd >> 2;
54 // The Mojo handle over which control messages are sent.
57 // Whether the handle watcher should shut down.
60 // The list of handles being watched.
64 // A port for each handle on which to send events back to the isolate that
66 List<SendPort> _ports;
68 // The signals that we care about for each handle.
71 // A mapping from Mojo handles to their indices in _handles.
72 Map<int, int> _handleIndices;
74 // Since we are not storing wrapped handles, a dummy handle for when we need
76 RawMojoHandle _tempHandle;
78 MojoHandleWatcher(this._controlHandle) :
80 _handles = new List<int>(),
81 _ports = new List<SendPort>(),
82 _signals = new List<int>(),
83 _handleIndices = new Map<int, int>(),
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;
93 static void _handleWatcherIsolate(MojoHandleWatcher watcher) {
94 while (!watcher._shutdown) {
95 int res = RawMojoHandle.waitMany(watcher._handles,
97 RawMojoHandle.DEADLINE_INDEFINITE);
99 watcher._handleControlMessage();
100 } else if (res > 0) {
101 int handle = watcher._handles[res];
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
110 watcher._pruneClosedHandles();
111 watcher._shutdown = true;
113 // Some handle was closed, but not by us.
114 // We have to go through the list and find it.
115 watcher._pruneClosedHandles();
120 void _routeEvent(int idx) {
121 int client_handle = _handles[idx];
122 int signals = _signals[idx];
123 SendPort port = _ports[idx];
125 _tempHandle.h = client_handle;
127 MojoHandleSignals.isWritable(signals) && _tempHandle.readyWrite();
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);
137 _tempHandle.h = RawMojoHandle.INVALID;
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
146 int signals = result[2] & MojoHandleSignals.READWRITE;
147 int command = _decodeCommand(result[2]);
150 _addHandle(result[0], result[1], signals);
153 _removeHandle(result[0]);
156 _toggleWrite(result[0]);
165 throw new Exception("Invalid Command: $command");
170 void _addHandle(int mojoHandle, SendPort port, int signals) {
171 _handles.add(mojoHandle);
173 _signals.add(signals & MojoHandleSignals.READWRITE);
174 _handleIndices[mojoHandle] = _handleCount;
178 void _removeHandle(int mojoHandle) {
179 int idx = _handleIndices[mojoHandle];
181 throw new Exception("Remove on a non-existent handle: idx = $idx.");
184 throw new Exception("The control handle (idx = 0) cannot be removed.");
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();
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();
202 _handleIndices[_handles[idx]] = idx;
207 void _close(int mojoHandle) {
208 int idx = _handleIndices[mojoHandle];
210 throw new Exception("Close on a non-existent handle: idx = $idx.");
213 throw new Exception("The control handle (idx = 0) cannot be closed.");
215 _tempHandle.h = _handles[idx];
217 _tempHandle.h = RawMojoHandle.INVALID;
218 _removeHandle(mojoHandle);
221 void _toggleWrite(int mojoHandle) {
222 int idx = _handleIndices[mojoHandle];
224 throw new Exception("Toggle write on a non-existent handle: idx = $idx.");
227 throw new Exception("The control handle (idx = 0) cannot be toggled.");
229 _signals[idx] = MojoHandleSignals.toggleWrite(_signals[idx]);
232 void _pruneClosedHandles() {
233 List<int> closed = new List();
234 for (var h in _handles) {
236 MojoResult res = _tempHandle.wait(MojoHandleSignals.READWRITE, 0);
237 if ((!res.isOk) && (!res.isDeadlineExceeded)) {
240 _tempHandle.h = RawMojoHandle.INVALID;
242 for (var h in closed) {
247 static MojoResult _sendControlData(RawMojoHandle mojoHandle,
250 int controlHandle = _MojoHandleWatcherNatives.getControlHandle();
251 if (controlHandle == RawMojoHandle.INVALID) {
252 throw new Exception("Found invalid control handle");
254 var result = _MojoHandleWatcherNatives.sendControlData(
255 controlHandle, mojoHandle.h, port, data);
256 return new MojoResult(result);
259 static Future<Isolate> Start() {
260 // 5. Return Future<bool> giving true on success.
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;
267 // Make a MojoHandleWatcher with one end.
268 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle);
270 // Call setControlHandle with the other end.
271 _MojoHandleWatcherNatives.setControlHandle(producerHandle);
273 // Spawn the handle watcher isolate with the MojoHandleWatcher,
274 return Isolate.spawn(_handleWatcherIsolate, watcher);
278 _sendControlData(RawMojoHandle.INVALID, null, _encodeCommand(SHUTDOWN));
281 static MojoResult close(RawMojoHandle mojoHandle) {
282 return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE));
285 static MojoResult toggleWrite(RawMojoHandle mojoHandle) {
286 return _sendControlData(mojoHandle, null, _encodeCommand(TOGGLE_WRITE));
289 static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) {
290 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals));
293 static MojoResult remove(RawMojoHandle mojoHandle) {
294 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE));