Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / mojo / public / dart / src / handle_watcher.dart
blob47f36756bba1777b742d92980de5f9834b86eb3b
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.
5 part of core;
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
23 //           per VM process.
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
30 //     '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 {
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;
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.
55   int _controlHandle;
57   // Whether the handle watcher should shut down.
58   bool _shutdown;
60   // The list of handles being watched.
61   List<int> _handles;
62   int _handleCount;
64   // A port for each handle on which to send events back to the isolate that
65   // owns the handle.
66   List<SendPort> _ports;
68   // The signals that we care about for each handle.
69   List<int> _signals;
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
75   // a RawMojoHandle.
76   RawMojoHandle _tempHandle;
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   }
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   }
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;
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   }
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]);
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   }
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   }
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   }
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   }
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   }
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   }
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   }
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);
275   }
277   static void Stop() {
278     _sendControlData(RawMojoHandle.INVALID, null, _encodeCommand(SHUTDOWN));
279   }
281   static MojoResult close(RawMojoHandle mojoHandle) {
282     return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE));
283   }
285   static MojoResult toggleWrite(RawMojoHandle mojoHandle) {
286     return _sendControlData(mojoHandle, null, _encodeCommand(TOGGLE_WRITE));
287   }
289   static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) {
290     return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals));
291   }
293   static MojoResult remove(RawMojoHandle mojoHandle) {
294     return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE));
295   }