OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 part of core; | 5 part of core; |
6 | 6 |
7 class _MojoHandleWatcherNatives { | 7 class _MojoHandleWatcherNatives { |
8 static int sendControlData( | 8 static int sendControlData( |
9 int controlHandle, int mojoHandle, SendPort port, int data) | 9 int controlHandle, int mojoHandle, SendPort port, int data) |
10 native "MojoHandleWatcher_SendControlData"; | 10 native "MojoHandleWatcher_SendControlData"; |
(...skipping 15 matching lines...) Expand all Loading... |
26 // | 26 // |
27 // add(handle, port, signals) - Instructs the MojoHandleWatcher isolate to add | 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 | 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 | 29 // isolate only for the events specified by 'signals' using the send port |
30 // 'port' | 30 // 'port' |
31 // | 31 // |
32 // remove(handle) - Instructs the MojoHandleWatcher isolate to remove 'handle' | 32 // remove(handle) - Instructs the MojoHandleWatcher isolate to remove 'handle' |
33 // from the set of handles it watches. This allows the application isolate | 33 // from the set of handles it watches. This allows the application isolate |
34 // to, e.g., pause the stream of events. | 34 // to, e.g., pause the stream of events. |
35 // | 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 | 36 // close(handle) - Notifies the HandleWatcherIsolate that a handle it is |
41 // watching should be removed from its set and closed. | 37 // watching should be removed from its set and closed. |
42 class MojoHandleWatcher { | 38 class MojoHandleWatcher { |
43 // Control commands. | 39 // Control commands. |
44 static const int ADD = 0; | 40 static const int ADD = 0; |
45 static const int REMOVE = 1; | 41 static const int REMOVE = 1; |
46 static const int TOGGLE_WRITE = 2; | 42 static const int CLOSE = 2; |
47 static const int CLOSE = 3; | 43 static const int TIMER = 3; |
48 static const int TIMER = 4; | 44 static const int SHUTDOWN = 4; |
49 static const int SHUTDOWN = 5; | |
50 | 45 |
51 static int _encodeCommand(int cmd, [int signals = 0]) => | 46 static int _encodeCommand(int cmd, [int signals = 0]) => |
52 (cmd << 2) | (signals & MojoHandleSignals.READWRITE); | 47 (cmd << 2) | (signals & MojoHandleSignals.kReadWrite); |
53 static int _decodeCommand(int cmd) => cmd >> 2; | 48 static int _decodeCommand(int cmd) => cmd >> 2; |
54 | 49 |
55 // The Mojo handle over which control messages are sent. | 50 // The Mojo handle over which control messages are sent. |
56 int _controlHandle; | 51 int _controlHandle; |
57 | 52 |
58 // Whether the handle watcher should shut down. | 53 // Whether the handle watcher should shut down. |
59 bool _shutdown; | 54 bool _shutdown; |
60 | 55 |
61 // The list of handles being watched. | 56 // The list of handles being watched. |
62 List<int> _handles; | 57 List<int> _handles; |
(...skipping 21 matching lines...) Expand all Loading... |
84 _handles = new List<int>(), | 79 _handles = new List<int>(), |
85 _ports = new List<SendPort>(), | 80 _ports = new List<SendPort>(), |
86 _signals = new List<int>(), | 81 _signals = new List<int>(), |
87 _handleIndices = new Map<int, int>(), | 82 _handleIndices = new Map<int, int>(), |
88 _handleCount = 1, | 83 _handleCount = 1, |
89 _tempHandle = new RawMojoHandle(RawMojoHandle.INVALID), | 84 _tempHandle = new RawMojoHandle(RawMojoHandle.INVALID), |
90 _timerQueue = new TimerQueue() { | 85 _timerQueue = new TimerQueue() { |
91 // Setup control handle. | 86 // Setup control handle. |
92 _handles.add(_controlHandle); | 87 _handles.add(_controlHandle); |
93 _ports.add(null); // There is no port for the control handle. | 88 _ports.add(null); // There is no port for the control handle. |
94 _signals.add(MojoHandleSignals.READABLE); | 89 _signals.add(MojoHandleSignals.kReadable); |
95 _handleIndices[_controlHandle] = 0; | 90 _handleIndices[_controlHandle] = 0; |
96 } | 91 } |
97 | 92 |
98 static void _handleWatcherIsolate(int consumerHandle) { | 93 static void _handleWatcherIsolate(int consumerHandle) { |
99 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle); | 94 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle); |
100 while (!watcher._shutdown) { | 95 while (!watcher._shutdown) { |
101 int deadline = watcher._processTimerDeadlines(); | 96 int deadline = watcher._processTimerDeadlines(); |
102 int res = RawMojoHandle.waitMany(watcher._handles, | 97 int res = RawMojoHandle.waitMany(watcher._handles, |
103 watcher._signals, | 98 watcher._signals, |
104 deadline); | 99 deadline); |
105 if (res == 0) { | 100 if (res == 0) { |
106 watcher._handleControlMessage(); | 101 watcher._handleControlMessage(); |
107 } else if (res > 0) { | 102 } else if (res > 0) { |
108 int handle = watcher._handles[res]; | 103 int handle = watcher._handles[res]; |
109 // Route event. | 104 // Route event. |
110 watcher._routeEvent(res); | 105 watcher._routeEvent(res); |
111 // Remove the handle from the list. | 106 // Remove the handle from the list. |
112 watcher._removeHandle(handle); | 107 watcher._removeHandle(handle); |
113 } else if (res != MojoResult.kDeadlineExceeded) { | 108 } else if (res != MojoResult.kDeadlineExceeded) { |
| 109 var result = new MojoResult(res); |
114 // Some handle was closed, but not by us. | 110 // Some handle was closed, but not by us. |
115 // We have to go through the list and find it. | 111 // We have to go through the list and find it. |
116 watcher._pruneClosedHandles(); | 112 watcher._pruneClosedHandles(); |
117 } | 113 } |
118 } | 114 } |
119 } | 115 } |
120 | 116 |
121 void _routeEvent(int idx) { | 117 void _routeEvent(int idx) { |
122 int client_handle = _handles[idx]; | 118 int client_handle = _handles[idx]; |
123 int signals = _signals[idx]; | 119 var signals = new MojoHandleSignals(_signals[idx]); |
124 SendPort port = _ports[idx]; | 120 SendPort port = _ports[idx]; |
125 | 121 |
126 _tempHandle.h = client_handle; | 122 _tempHandle.h = client_handle; |
127 bool readyWrite = | 123 bool readyWrite = signals.isWritable && _tempHandle.readyWrite; |
128 MojoHandleSignals.isWritable(signals) && _tempHandle.readyWrite(); | 124 bool readyRead = signals.isReadable && _tempHandle.readyRead; |
129 bool readyRead = | |
130 MojoHandleSignals.isReadable(signals) && _tempHandle.readyRead(); | |
131 if (readyRead && readyWrite) { | |
132 port.send(MojoHandleSignals.READWRITE); | |
133 } else if (readyWrite) { | |
134 port.send(MojoHandleSignals.WRITABLE); | |
135 } else if (readyRead) { | |
136 port.send(MojoHandleSignals.READABLE); | |
137 } | |
138 _tempHandle.h = RawMojoHandle.INVALID; | 125 _tempHandle.h = RawMojoHandle.INVALID; |
| 126 |
| 127 var event = MojoHandleSignals.NONE; |
| 128 event += readyRead ? MojoHandleSignals.READABLE : MojoHandleSignals.NONE; |
| 129 event += readyWrite ? MojoHandleSignals.WRITABLE : MojoHandleSignals.NONE; |
| 130 port.send([signals.value, event.value]); |
139 } | 131 } |
140 | 132 |
141 void _handleControlMessage() { | 133 void _handleControlMessage() { |
142 List result = _MojoHandleWatcherNatives.recvControlData(_controlHandle); | 134 List result = _MojoHandleWatcherNatives.recvControlData(_controlHandle); |
143 // result[0] = mojo handle if any, or a timer deadline in milliseconds. | 135 // result[0] = mojo handle if any, or a timer deadline in milliseconds. |
144 // result[1] = SendPort if any. | 136 // result[1] = SendPort if any. |
145 // result[2] = command << 2 | WRITABLE | READABLE | 137 // result[2] = command << 2 | WRITABLE | READABLE |
146 | 138 |
147 int signals = result[2] & MojoHandleSignals.READWRITE; | 139 var signals = new MojoHandleSignals( |
| 140 result[2] & MojoHandleSignals.kReadWrite); |
148 int command = _decodeCommand(result[2]); | 141 int command = _decodeCommand(result[2]); |
149 switch (command) { | 142 switch (command) { |
150 case ADD: | 143 case ADD: |
151 _addHandle(result[0], result[1], signals); | 144 _addHandle(result[0], result[1], signals); |
152 break; | 145 break; |
153 case REMOVE: | 146 case REMOVE: |
154 _removeHandle(result[0]); | 147 _removeHandle(result[0]); |
155 break; | 148 break; |
156 case TOGGLE_WRITE: | |
157 _toggleWrite(result[0]); | |
158 break; | |
159 case CLOSE: | 149 case CLOSE: |
160 _close(result[0]); | 150 _close(result[0]); |
161 break; | 151 break; |
162 case TIMER: | 152 case TIMER: |
163 _timer(result[1], result[0]); | 153 _timer(result[1], result[0]); |
164 break; | 154 break; |
165 case SHUTDOWN: | 155 case SHUTDOWN: |
166 _shutdownHandleWatcher(); | 156 _shutdownHandleWatcher(result[1]); |
167 break; | 157 break; |
168 default: | 158 default: |
169 throw new Exception("Invalid Command: $command"); | 159 throw "Invalid Command: $command"; |
170 break; | 160 break; |
171 } | 161 } |
172 } | 162 } |
173 | 163 |
174 void _addHandle(int mojoHandle, SendPort port, int signals) { | 164 void _addHandle(int mojoHandle, SendPort port, MojoHandleSignals signals) { |
175 _handles.add(mojoHandle); | 165 int idx = _handleIndices[mojoHandle]; |
176 _ports.add(port); | 166 if (idx == null) { |
177 _signals.add(signals & MojoHandleSignals.READWRITE); | 167 _handles.add(mojoHandle); |
178 _handleIndices[mojoHandle] = _handleCount; | 168 _ports.add(port); |
179 _handleCount++; | 169 _signals.add(signals.value); |
| 170 _handleIndices[mojoHandle] = _handleCount; |
| 171 _handleCount++; |
| 172 } else { |
| 173 assert(_ports[idx] == port); |
| 174 assert(_handles[idx] == mojoHandle); |
| 175 _signals[idx] |= signals.value; |
| 176 } |
180 } | 177 } |
181 | 178 |
182 void _removeHandle(int mojoHandle) { | 179 void _removeHandle(int mojoHandle) { |
183 int idx = _handleIndices[mojoHandle]; | 180 int idx = _handleIndices[mojoHandle]; |
184 if (idx == null) { | 181 if (idx == null) { |
185 throw new Exception("Remove on a non-existent handle: idx = $idx."); | 182 throw "Remove on a non-existent handle: idx = $idx."; |
186 } | 183 } |
187 if (idx == 0) { | 184 if (idx == 0) { |
188 throw new Exception("The control handle (idx = 0) cannot be removed."); | 185 throw "The control handle (idx = 0) cannot be removed."; |
189 } | 186 } |
190 // We don't use List.removeAt so that we know how to fix-up _handleIndices. | 187 // We don't use List.removeAt so that we know how to fix-up _handleIndices. |
191 if (idx == _handleCount - 1) { | 188 if (idx == _handleCount - 1) { |
192 int handle = _handles[idx]; | 189 int handle = _handles[idx]; |
193 _handleIndices[handle] = null; | 190 _handleIndices[handle] = null; |
194 _handles.removeLast(); | 191 _handles.removeLast(); |
195 _signals.removeLast(); | 192 _signals.removeLast(); |
196 _ports.removeLast(); | 193 _ports.removeLast(); |
197 _handleCount--; | 194 _handleCount--; |
198 } else { | 195 } else { |
199 int last = _handleCount - 1; | 196 int last = _handleCount - 1; |
| 197 _handleIndices[_handles[idx]] = null; |
200 _handles[idx] = _handles[last]; | 198 _handles[idx] = _handles[last]; |
201 _signals[idx] = _signals[last]; | 199 _signals[idx] = _signals[last]; |
202 _ports[idx] = _ports[last]; | 200 _ports[idx] = _ports[last]; |
203 _handles.removeLast(); | 201 _handles.removeLast(); |
204 _signals.removeLast(); | 202 _signals.removeLast(); |
205 _ports.removeLast(); | 203 _ports.removeLast(); |
206 _handleIndices[_handles[idx]] = idx; | 204 _handleIndices[_handles[idx]] = idx; |
207 _handleCount--; | 205 _handleCount--; |
208 } | 206 } |
209 } | 207 } |
210 | 208 |
211 void _close(int mojoHandle, {bool pruning : false}) { | 209 void _close(int mojoHandle, {bool pruning : false}) { |
212 int idx = _handleIndices[mojoHandle]; | 210 int idx = _handleIndices[mojoHandle]; |
213 if (idx == null) { | 211 if (idx == null) { |
214 throw new Exception("Close on a non-existent handle: idx = $idx."); | 212 // A client may request to close a handle that has already been closed on |
| 213 // the other side and pruned, but before receiving notification from the |
| 214 // handle watcher. |
| 215 return; |
215 } | 216 } |
216 if (idx == 0) { | 217 if (idx == 0) { |
217 throw new Exception("The control handle (idx = 0) cannot be closed."); | 218 throw "The control handle (idx = 0) cannot be closed."; |
218 } | 219 } |
219 _tempHandle.h = _handles[idx]; | 220 _tempHandle.h = _handles[idx]; |
220 _tempHandle.close(); | 221 _tempHandle.close(); |
221 _tempHandle.h = RawMojoHandle.INVALID; | 222 _tempHandle.h = RawMojoHandle.INVALID; |
222 if (pruning) { | 223 if (pruning) { |
223 // If this handle is being pruned, notify the application isolate | 224 // If this handle is being pruned, notify the application isolate |
224 // by sending MojoHandleSignals.NONE. | 225 // by sending MojoHandleSignals.PEER_CLOSED. |
225 _ports[idx].send(MojoHandleSignals.NONE); | 226 _ports[idx].send([_signals[idx], MojoHandleSignals.kPeerClosed]); |
226 } | 227 } |
227 _removeHandle(mojoHandle); | 228 _removeHandle(mojoHandle); |
228 } | 229 } |
229 | 230 |
230 // Returns the next timer deadline in units of microseconds from 'now'. | 231 // Returns the next timer deadline in units of microseconds from 'now'. |
231 int _processTimerDeadlines() { | 232 int _processTimerDeadlines() { |
232 int now = (new DateTime.now()).millisecondsSinceEpoch; | 233 int now = (new DateTime.now()).millisecondsSinceEpoch; |
233 while (_timerQueue.hasTimer && (now >= _timerQueue.currentTimeout)) { | 234 while (_timerQueue.hasTimer && (now >= _timerQueue.currentTimeout)) { |
234 _timerQueue.currentPort.send(null); | 235 _timerQueue.currentPort.send(null); |
235 _timerQueue.removeCurrent(); | 236 _timerQueue.removeCurrent(); |
236 now = (new DateTime.now()).millisecondsSinceEpoch; | 237 now = (new DateTime.now()).millisecondsSinceEpoch; |
237 } | 238 } |
238 return _timerQueue.hasTimer ? (_timerQueue.currentTimeout - now) * 1000 | 239 return _timerQueue.hasTimer ? (_timerQueue.currentTimeout - now) * 1000 |
239 : RawMojoHandle.DEADLINE_INDEFINITE; | 240 : RawMojoHandle.DEADLINE_INDEFINITE; |
240 } | 241 } |
241 | 242 |
242 void _timer(SendPort port, int deadline) { | 243 void _timer(SendPort port, int deadline) { |
243 _timerQueue.updateTimer(port, deadline); | 244 _timerQueue.updateTimer(port, deadline); |
244 } | 245 } |
245 | 246 |
246 void _toggleWrite(int mojoHandle) { | |
247 int idx = _handleIndices[mojoHandle]; | |
248 if (idx == null) { | |
249 throw new Exception( | |
250 "Toggle write on a non-existent handle: $mojoHandle."); | |
251 } | |
252 if (idx == 0) { | |
253 throw new Exception("The control handle (idx = 0) cannot be toggled."); | |
254 } | |
255 _signals[idx] = MojoHandleSignals.toggleWrite(_signals[idx]); | |
256 } | |
257 | |
258 void _pruneClosedHandles() { | 247 void _pruneClosedHandles() { |
259 List<int> closed = new List(); | 248 List<int> closed = new List(); |
260 for (var h in _handles) { | 249 for (var h in _handles) { |
261 _tempHandle.h = h; | 250 _tempHandle.h = h; |
262 MojoResult res = _tempHandle.wait(MojoHandleSignals.READWRITE, 0); | 251 MojoResult res = _tempHandle.wait(MojoHandleSignals.kReadWrite, 0); |
263 if ((!res.isOk) && (!res.isDeadlineExceeded)) { | 252 if ((!res.isOk) && (!res.isDeadlineExceeded)) { |
264 closed.add(h); | 253 closed.add(h); |
265 } | 254 } |
266 _tempHandle.h = RawMojoHandle.INVALID; | 255 _tempHandle.h = RawMojoHandle.INVALID; |
267 } | 256 } |
268 for (var h in closed) { | 257 for (var h in closed) { |
269 _close(h, pruning: true); | 258 _close(h, pruning: true); |
270 } | 259 } |
271 } | 260 } |
272 | 261 |
273 void _shutdownHandleWatcher() { | 262 void _shutdownHandleWatcher(SendPort shutdownSendPort) { |
274 _shutdown = true; | 263 _shutdown = true; |
275 _tempHandle.h = _controlHandle; | 264 _tempHandle.h = _controlHandle; |
276 _tempHandle.close(); | 265 _tempHandle.close(); |
277 _tempHandle.h = RawMojoHandle.INVALID; | 266 _tempHandle.h = RawMojoHandle.INVALID; |
| 267 shutdownSendPort.send(null); |
278 } | 268 } |
279 | 269 |
280 static MojoResult _sendControlData(RawMojoHandle mojoHandle, | 270 static MojoResult _sendControlData(RawMojoHandle mojoHandle, |
281 SendPort port, | 271 SendPort port, |
282 int data) { | 272 int data) { |
283 int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); | 273 int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); |
284 if (controlHandle == RawMojoHandle.INVALID) { | 274 if (controlHandle == RawMojoHandle.INVALID) { |
285 throw new Exception("Found invalid control handle"); | 275 return MojoResult.FAILED_PRECONDITION; |
286 } | 276 } |
287 | 277 |
288 int rawHandle = RawMojoHandle.INVALID; | 278 int rawHandle = RawMojoHandle.INVALID; |
289 if (mojoHandle != null) { | 279 if (mojoHandle != null) { |
290 rawHandle = mojoHandle.h; | 280 rawHandle = mojoHandle.h; |
291 } | 281 } |
292 var result = _MojoHandleWatcherNatives.sendControlData( | 282 var result = _MojoHandleWatcherNatives.sendControlData( |
293 controlHandle, rawHandle, port, data); | 283 controlHandle, rawHandle, port, data); |
294 return new MojoResult(result); | 284 return new MojoResult(result); |
295 } | 285 } |
296 | 286 |
297 static Future<Isolate> Start() { | 287 static Future<Isolate> Start() { |
298 // Make a control message pipe, | 288 // Make a control message pipe, |
299 MojoMessagePipe pipe = new MojoMessagePipe(); | 289 MojoMessagePipe pipe = new MojoMessagePipe(); |
300 int consumerHandle = pipe.endpoints[0].handle.h; | 290 int consumerHandle = pipe.endpoints[0].handle.h; |
301 int producerHandle = pipe.endpoints[1].handle.h; | 291 int producerHandle = pipe.endpoints[1].handle.h; |
302 | 292 |
303 // Call setControlHandle with the other end. | 293 // Call setControlHandle with the other end. |
| 294 assert(producerHandle != RawMojoHandle.INVALID); |
304 _MojoHandleWatcherNatives.setControlHandle(producerHandle); | 295 _MojoHandleWatcherNatives.setControlHandle(producerHandle); |
305 | 296 |
306 // Spawn the handle watcher isolate with the MojoHandleWatcher, | 297 // Spawn the handle watcher isolate with the MojoHandleWatcher, |
307 return Isolate.spawn(_handleWatcherIsolate, consumerHandle); | 298 return Isolate.spawn(_handleWatcherIsolate, consumerHandle); |
308 } | 299 } |
309 | 300 |
310 static void Stop() { | 301 static void Stop() { |
| 302 // Create a port for notification that the handle watcher has shutdown. |
| 303 var shutdownReceivePort = new ReceivePort(); |
| 304 var shutdownSendPort = shutdownReceivePort.sendPort; |
| 305 |
311 // Send the shutdown command. | 306 // Send the shutdown command. |
312 _sendControlData(null, null, _encodeCommand(SHUTDOWN)); | 307 _sendControlData(null, shutdownSendPort, _encodeCommand(SHUTDOWN)); |
313 | 308 |
314 // Close the control handle. | 309 // Close the control handle. |
315 int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); | 310 int controlHandle = _MojoHandleWatcherNatives.getControlHandle(); |
316 var handle = new RawMojoHandle(controlHandle); | 311 var handle = new RawMojoHandle(controlHandle); |
317 handle.close(); | 312 handle.close(); |
318 | 313 |
319 // Invalidate the control handle. | 314 // Invalidate the control handle. |
320 _MojoHandleWatcherNatives.setControlHandle(RawMojoHandle.INVALID); | 315 _MojoHandleWatcherNatives.setControlHandle(RawMojoHandle.INVALID); |
| 316 |
| 317 // Wait for the handle watcher isolate to exit. |
| 318 shutdownReceivePort.first.then((_) { |
| 319 shutdownReceivePort.close(); |
| 320 }); |
321 } | 321 } |
322 | 322 |
323 static MojoResult close(RawMojoHandle mojoHandle) { | 323 static MojoResult close(RawMojoHandle mojoHandle) { |
324 return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)); | 324 return _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)); |
325 } | 325 } |
326 | 326 |
327 static MojoResult toggleWrite(RawMojoHandle mojoHandle) { | |
328 return _sendControlData(mojoHandle, null, _encodeCommand(TOGGLE_WRITE)); | |
329 } | |
330 | |
331 static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) { | 327 static MojoResult add(RawMojoHandle mojoHandle, SendPort port, int signals) { |
332 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals)); | 328 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals)); |
333 } | 329 } |
334 | 330 |
335 static MojoResult remove(RawMojoHandle mojoHandle) { | 331 static MojoResult remove(RawMojoHandle mojoHandle) { |
336 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE)); | 332 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE)); |
337 } | 333 } |
338 | 334 |
339 static MojoResult timer(SendPort port, int deadline) { | 335 static MojoResult timer(SendPort port, int deadline) { |
340 // The deadline will be unwrapped before sending to the handle watcher. | 336 // The deadline will be unwrapped before sending to the handle watcher. |
341 return _sendControlData( | 337 return _sendControlData( |
342 new RawMojoHandle(deadline), port, _encodeCommand(TIMER)); | 338 new RawMojoHandle(deadline), port, _encodeCommand(TIMER)); |
343 } | 339 } |
344 } | 340 } |
OLD | NEW |