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 bindings; | 5 part of bindings; |
6 | 6 |
7 /// The object that [ProxyMessageHandler.errorFuture] completes with when there | 7 /// The object that [ProxyMessageHandler.errorFuture] completes with when there |
8 /// is an error. | 8 /// is an error. |
9 class ProxyError { | 9 class ProxyError { |
10 final String message; | 10 final String message; |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
62 /// bound Proxy. For example, every class extending the Application base class | 62 /// bound Proxy. For example, every class extending the Application base class |
63 /// in package:mojo/application.dart inherits an implementation of the | 63 /// in package:mojo/application.dart inherits an implementation of the |
64 /// ServiceConnector interface. | 64 /// ServiceConnector interface. |
65 abstract class ServiceConnector { | 65 abstract class ServiceConnector { |
66 /// Connects [proxy] to the service called [serviceName] that lives at [url]. | 66 /// Connects [proxy] to the service called [serviceName] that lives at [url]. |
67 void connectToService(String url, Proxy proxy, [String serviceName]); | 67 void connectToService(String url, Proxy proxy, [String serviceName]); |
68 } | 68 } |
69 | 69 |
70 abstract class ProxyMessageHandler extends core.MojoEventHandler | 70 abstract class ProxyMessageHandler extends core.MojoEventHandler |
71 implements MojoInterfaceControl { | 71 implements MojoInterfaceControl { |
72 HashMap<int, Completer> _completerMap = new HashMap<int, Completer>(); | 72 HashMap<int, Function> _callbackMap = new HashMap<int, Function>(); |
73 Completer _errorCompleter = new Completer(); | 73 Completer _errorCompleter = new Completer(); |
74 Set<Completer> _errorCompleters; | 74 Set<Completer> _errorCompleters; |
75 int _nextId = 0; | 75 int _nextId = 0; |
76 int _version = 0; | 76 int _version = 0; |
77 int _pendingCount = 0; | 77 int _pendingCount = 0; |
78 | 78 |
79 ProxyMessageHandler.fromEndpoint(core.MojoMessagePipeEndpoint endpoint) | 79 ProxyMessageHandler.fromEndpoint(core.MojoMessagePipeEndpoint endpoint) |
80 : super.fromEndpoint(endpoint); | 80 : super.fromEndpoint(endpoint); |
81 | 81 |
82 ProxyMessageHandler.fromHandle(core.MojoHandle handle) | 82 ProxyMessageHandler.fromHandle(core.MojoHandle handle) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 } | 124 } |
125 } | 125 } |
126 | 126 |
127 @override | 127 @override |
128 void handleWrite() { | 128 void handleWrite() { |
129 proxyError("Unexpected writable signal"); | 129 proxyError("Unexpected writable signal"); |
130 } | 130 } |
131 | 131 |
132 @override | 132 @override |
133 Future close({bool immediate: false}) { | 133 Future close({bool immediate: false}) { |
134 // Drop the completers for outstanding calls. The Futures will never | 134 // Drop the callbacks for outstanding calls. They will never be called. |
135 // complete. | 135 _callbackMap.clear(); |
136 _completerMap.clear(); | |
137 | 136 |
138 // Signal to any pending calls that the ProxyMessageHandler is closed. | 137 // Signal to any pending calls that the ProxyMessageHandler is closed. |
139 if (_pendingCount > 0) { | 138 if (_pendingCount > 0) { |
140 proxyError("The ProxyMessageHandler is closed."); | 139 proxyError("The ProxyMessageHandler is closed."); |
141 } | 140 } |
142 | 141 |
143 return super.close(immediate: immediate); | 142 return super.close(immediate: immediate); |
144 } | 143 } |
145 | 144 |
146 void sendMessage(Struct message, int name) { | 145 void sendMessage(Struct message, int name) { |
147 if (!isBound) { | 146 if (!isBound) { |
148 proxyError("The ProxyMessageHandler is closed."); | 147 proxyError("The ProxyMessageHandler is closed."); |
149 return; | 148 return; |
150 } | 149 } |
151 if (!isOpen) { | 150 if (!isOpen) { |
152 beginHandlingEvents(); | 151 beginHandlingEvents(); |
153 } | 152 } |
154 var header = new MessageHeader(name); | 153 var header = new MessageHeader(name); |
155 var serviceMessage = message.serializeWithHeader(header); | 154 var serviceMessage = message.serializeWithHeader(header); |
156 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, | 155 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, |
157 serviceMessage.handles); | 156 serviceMessage.handles); |
158 if (endpoint.status != core.MojoResult.kOk) { | 157 if (endpoint.status != core.MojoResult.kOk) { |
159 proxyError("Write to message pipe endpoint failed."); | 158 proxyError("Write to message pipe endpoint failed."); |
160 } | 159 } |
161 } | 160 } |
162 | 161 |
163 Future sendMessageWithRequestId(Struct message, int name, int id, int flags) { | 162 void sendMessageWithRequestId( |
164 var completer = new Completer(); | 163 Struct message, int name, int id, int flags, Function callback) { |
165 if (!isBound) { | 164 if (!isBound) { |
166 proxyError("The ProxyMessageHandler is closed."); | 165 proxyError("The Proxy is closed."); |
167 return completer.future; | 166 return; |
168 } | 167 } |
169 if (!isOpen) { | 168 if (!isOpen) { |
170 beginHandlingEvents(); | 169 beginHandlingEvents(); |
171 } | 170 } |
172 if (id == -1) { | 171 if (id == -1) { |
173 id = _nextId++; | 172 id = _nextId++; |
174 } | 173 } |
175 | 174 |
176 var header = new MessageHeader.withRequestId(name, flags, id); | 175 var header = new MessageHeader.withRequestId(name, flags, id); |
177 var serviceMessage = message.serializeWithHeader(header); | 176 var serviceMessage = message.serializeWithHeader(header); |
178 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, | 177 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, |
179 serviceMessage.handles); | 178 serviceMessage.handles); |
180 | 179 |
181 if (endpoint.status == core.MojoResult.kOk) { | 180 if (endpoint.status == core.MojoResult.kOk) { |
182 _completerMap[id] = completer; | 181 _callbackMap[id] = callback; |
183 _pendingCount++; | 182 _pendingCount++; |
184 } else { | 183 } else { |
185 proxyError("Write to message pipe endpoint failed: ${endpoint}"); | 184 proxyError("Write to message pipe endpoint failed: ${endpoint}"); |
186 } | 185 } |
187 return completer.future; | |
188 } | 186 } |
189 | 187 |
190 // Need a getter for this for access in subclasses. | 188 // Need a getter for this for access in subclasses. |
191 HashMap<int, Completer> get completerMap => _completerMap; | 189 HashMap<int, Function> get callbackMap => _callbackMap; |
192 | 190 |
193 @override | 191 @override |
194 String toString() { | 192 String toString() { |
195 var superString = super.toString(); | 193 var superString = super.toString(); |
196 return "ProxyMessageHandler(${superString})"; | 194 return "ProxyMessageHandler(${superString})"; |
197 } | 195 } |
198 | 196 |
199 /// Queries the max version that the remote side supports. | 197 /// Queries the max version that the remote side supports. |
200 /// Updates [version]. | 198 /// Updates [version]. |
201 Future<int> queryVersion() async { | 199 Future<int> queryVersion() { |
200 Completer<int> completer = new Completer<int>(); | |
202 var params = new icm.RunMessageParams(); | 201 var params = new icm.RunMessageParams(); |
203 params.reserved0 = 16; | 202 params.reserved0 = 16; |
204 params.reserved1 = 0; | 203 params.reserved1 = 0; |
205 params.queryVersion = new icm.QueryVersion(); | 204 params.queryVersion = new icm.QueryVersion(); |
206 var response = await sendMessageWithRequestId( | 205 sendMessageWithRequestId( |
207 params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse); | 206 params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse, |
208 _version = response.queryVersionResult.version; | 207 (r0, r1, queryResult) { |
209 return _version; | 208 _version = queryResult.version; |
209 completer.complete(_version); | |
210 }); | |
211 return completer.future; | |
210 } | 212 } |
211 | 213 |
212 /// If the remote side doesn't support the [requiredVersion], it will close | 214 /// If the remote side doesn't support the [requiredVersion], it will close |
213 /// its end of the message pipe asynchronously. This does nothing if it's | 215 /// its end of the message pipe asynchronously. This does nothing if it's |
214 /// already known that the remote side supports [requiredVersion]. | 216 /// already known that the remote side supports [requiredVersion]. |
215 /// Updates [version]. | 217 /// Updates [version]. |
216 void requireVersion(int requiredVersion) { | 218 void requireVersion(int requiredVersion) { |
217 if (requiredVersion <= _version) { | 219 if (requiredVersion <= _version) { |
218 // Already supported. | 220 // Already supported. |
219 return; | 221 return; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
280 return callCompleter.future; | 282 return callCompleter.future; |
281 } | 283 } |
282 | 284 |
283 _handleControlMessageResponse(ServiceMessage message) { | 285 _handleControlMessageResponse(ServiceMessage message) { |
284 // We only expect to see Run messages. | 286 // We only expect to see Run messages. |
285 if (message.header.type != icm.kRunMessageId) { | 287 if (message.header.type != icm.kRunMessageId) { |
286 proxyError("Unexpected header type in control message response: " | 288 proxyError("Unexpected header type in control message response: " |
287 "${message.header.type}"); | 289 "${message.header.type}"); |
288 return; | 290 return; |
289 } | 291 } |
290 | |
291 var response = icm.RunResponseMessageParams.deserialize(message.payload); | 292 var response = icm.RunResponseMessageParams.deserialize(message.payload); |
292 if (!message.header.hasRequestId) { | 293 if (!message.header.hasRequestId) { |
293 proxyError("Expected a message with a valid request Id."); | 294 proxyError("Expected a message with a valid request Id."); |
294 return; | 295 return; |
295 } | 296 } |
296 Completer c = completerMap[message.header.requestId]; | 297 Function callback = callbackMap[message.header.requestId]; |
297 if (c == null) { | 298 if (callback == null) { |
298 proxyError("Message had unknown request Id: ${message.header.requestId}"); | 299 proxyError("Message had unknown request Id: ${message.header.requestId}"); |
299 return; | 300 return; |
300 } | 301 } |
301 completerMap.remove(message.header.requestId); | 302 callbackMap.remove(message.header.requestId); |
302 if (c.isCompleted) { | 303 callback( |
303 proxyError("Control message response completer already completed"); | 304 response.reserved0, response.reserved1, response.queryVersionResult); |
304 return; | 305 return; |
305 } | |
306 c.complete(response); | |
307 } | 306 } |
308 } | 307 } |
308 | |
309 // A class that acts like a function, but which completes a completer with the | |
310 // the result of the function rather than returning the result. E.g.: | |
311 // | |
312 // Completer c = new Completer(); | |
313 // var completerator = new Completerator._(c, f); | |
314 // completerator(a, b); | |
315 // await c.future; | |
316 // | |
317 // This completes the future c with the result of passing a and b to f. | |
318 // | |
319 // More usefully for Mojo, e.g.: | |
320 // await _Completerator.completerate( | |
321 // proxy.method, argList, MethodResponseParams#init); | |
322 class _Completerator implements Function { | |
323 final Completer _c; | |
324 final Function _toComplete; | |
325 | |
326 _Completerator._(this._c, this._toComplete); | |
327 | |
328 static Future completerate(Function f, List args, Function ctor) { | |
329 Completer c = new Completer(); | |
330 var newArgs = new List.from(args); | |
331 newArgs.add(new _Completerator._(c, ctor)); | |
Cutch
2016/06/07 17:16:54
What's the performance hit for using a _Completera
zra
2016/06/07 22:11:50
Compared to the callback-based API it is the cost
| |
332 Function.apply(f, newArgs); | |
333 return c.future; | |
334 } | |
335 | |
336 // Work-around to avoid checked-mode only having grudging support for | |
337 // Function implemented with noSuchMethod. See: | |
338 // https://github.com/dart-lang/sdk/issues/26528 | |
339 dynamic call([ | |
Cutch
2016/06/07 17:16:54
whoa.
zra
2016/06/07 22:11:50
Acknowledged.
| |
340 dynamic a1, dynamic a2, dynamic a3, dynamic a4, dynamic a5, | |
341 dynamic a6, dynamic a7, dynamic a8, dynamic a9, dynamic a10, | |
342 dynamic a11, dynamic a12, dynamic a13, dynamic a14, dynamic a15, | |
343 dynamic a16, dynamic a17, dynamic a18, dynamic a19, dynamic a20]); | |
344 | |
345 @override | |
346 dynamic noSuchMethod(Invocation invocation) => | |
347 (invocation.memberName == #call) | |
348 ? _c.complete(Function.apply(_toComplete, invocation.positionalArguments)) | |
349 : super.noSuchMethod(invocation); | |
350 } | |
351 | |
352 /// Base class for Proxy class Futurizing wrappers. It turns callback-based | |
353 /// methods on the Proxy into Future based methods in derived classes. E.g.: | |
354 /// | |
355 /// class FuturizedHostResolverProxy extends FuturizedProxy<HostResolverProxy> { | |
356 /// Map<Symbol, Function> _mojoMethods; | |
357 /// | |
358 /// FuturizedHostResolverProxy(HostResolverProxy proxy) : super(proxy) { | |
359 /// _mojoMethods = <Symbol, Function>{ | |
Cutch
2016/06/07 17:16:54
Can we get the code generator to generate Futurize
zra
2016/06/07 22:11:50
I think the guidance will be that the callback-bas
| |
360 /// #getHostAddresses: proxy.getHostAddresses, | |
361 /// }; | |
362 /// } | |
363 /// Map<Symbol, Function> get mojoMethods => _mojoMethods; | |
364 /// | |
365 /// FuturizedHostResolverProxy.unbound() : | |
366 /// this(new HostResolverProxy.unbound()); | |
367 /// | |
368 /// static final Map<Symbol, Function> _mojoResponses = { | |
369 /// #getHostAddresses: new HostResolverGetHostAddressesResponseParams#init, | |
370 /// }; | |
371 /// Map<Symbol, Function> get mojoResponses => _mojoResponses; | |
372 /// } | |
373 /// | |
374 /// Then: | |
375 /// | |
376 /// HostResolveProxy proxy = ... | |
377 /// var futurizedProxy = new FuturizedHostResolverProxy(proxy); | |
378 /// var response = await futurizedProxy.getHostAddresses(host, family); | |
379 /// // etc. | |
380 abstract class FuturizedProxy<T extends Proxy> { | |
381 final T _proxy; | |
382 Map<Symbol, Function> get mojoMethods; | |
383 Map<Symbol, Function> get mojoResponses; | |
384 | |
385 FuturizedProxy(T this._proxy); | |
386 | |
387 T get proxy => _proxy; | |
388 Future responseOrError(Future f) => _proxy.responseOrError(f); | |
389 Future close({immediate: false}) => _proxy.close(immediate: immediate); | |
390 | |
391 @override | |
392 dynamic noSuchMethod(Invocation invocation) => | |
393 mojoMethods.containsKey(invocation.memberName) | |
394 ? _Completerator.completerate( | |
395 mojoMethods[invocation.memberName], | |
396 invocation.positionalArguments, | |
397 mojoResponses[invocation.memberName]) | |
398 : super.noSuchMethod(invocation); | |
399 } | |
OLD | NEW |