| Index: mojo/dart/packages/mojo/lib/src/proxy.dart
|
| diff --git a/mojo/dart/packages/mojo/lib/src/proxy.dart b/mojo/dart/packages/mojo/lib/src/proxy.dart
|
| index c0576b5ae25812da3be8a3d49a02bf3d7da17a83..7ca3644a76d14e7133fcb38ca3a0e725dcd7684e 100644
|
| --- a/mojo/dart/packages/mojo/lib/src/proxy.dart
|
| +++ b/mojo/dart/packages/mojo/lib/src/proxy.dart
|
| @@ -69,7 +69,7 @@ abstract class ServiceConnector {
|
|
|
| abstract class ProxyMessageHandler extends core.MojoEventHandler
|
| implements MojoInterfaceControl {
|
| - HashMap<int, Completer> _completerMap = new HashMap<int, Completer>();
|
| + HashMap<int, Function> _callbackMap = new HashMap<int, Function>();
|
| Completer _errorCompleter = new Completer();
|
| Set<Completer> _errorCompleters;
|
| int _nextId = 0;
|
| @@ -131,9 +131,8 @@ abstract class ProxyMessageHandler extends core.MojoEventHandler
|
|
|
| @override
|
| Future close({bool immediate: false}) {
|
| - // Drop the completers for outstanding calls. The Futures will never
|
| - // complete.
|
| - _completerMap.clear();
|
| + // Drop the callbacks for outstanding calls. They will never be called.
|
| + _callbackMap.clear();
|
|
|
| // Signal to any pending calls that the ProxyMessageHandler is closed.
|
| if (_pendingCount > 0) {
|
| @@ -160,11 +159,11 @@ abstract class ProxyMessageHandler extends core.MojoEventHandler
|
| }
|
| }
|
|
|
| - Future sendMessageWithRequestId(Struct message, int name, int id, int flags) {
|
| - var completer = new Completer();
|
| + void sendMessageWithRequestId(
|
| + Struct message, int name, int id, int flags, Function callback) {
|
| if (!isBound) {
|
| - proxyError("The ProxyMessageHandler is closed.");
|
| - return completer.future;
|
| + proxyError("The Proxy is closed.");
|
| + return;
|
| }
|
| if (!isOpen) {
|
| beginHandlingEvents();
|
| @@ -179,16 +178,15 @@ abstract class ProxyMessageHandler extends core.MojoEventHandler
|
| serviceMessage.handles);
|
|
|
| if (endpoint.status == core.MojoResult.kOk) {
|
| - _completerMap[id] = completer;
|
| + _callbackMap[id] = callback;
|
| _pendingCount++;
|
| } else {
|
| proxyError("Write to message pipe endpoint failed: ${endpoint}");
|
| }
|
| - return completer.future;
|
| }
|
|
|
| // Need a getter for this for access in subclasses.
|
| - HashMap<int, Completer> get completerMap => _completerMap;
|
| + HashMap<int, Function> get callbackMap => _callbackMap;
|
|
|
| @override
|
| String toString() {
|
| @@ -198,15 +196,19 @@ abstract class ProxyMessageHandler extends core.MojoEventHandler
|
|
|
| /// Queries the max version that the remote side supports.
|
| /// Updates [version].
|
| - Future<int> queryVersion() async {
|
| + Future<int> queryVersion() {
|
| + Completer<int> completer = new Completer<int>();
|
| var params = new icm.RunMessageParams();
|
| params.reserved0 = 16;
|
| params.reserved1 = 0;
|
| params.queryVersion = new icm.QueryVersion();
|
| - var response = await sendMessageWithRequestId(
|
| - params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse);
|
| - _version = response.queryVersionResult.version;
|
| - return _version;
|
| + sendMessageWithRequestId(
|
| + params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse,
|
| + (r0, r1, queryResult) {
|
| + _version = queryResult.version;
|
| + completer.complete(_version);
|
| + });
|
| + return completer.future;
|
| }
|
|
|
| /// If the remote side doesn't support the [requiredVersion], it will close
|
| @@ -287,22 +289,119 @@ abstract class ProxyMessageHandler extends core.MojoEventHandler
|
| "${message.header.type}");
|
| return;
|
| }
|
| -
|
| var response = icm.RunResponseMessageParams.deserialize(message.payload);
|
| if (!message.header.hasRequestId) {
|
| proxyError("Expected a message with a valid request Id.");
|
| return;
|
| }
|
| - Completer c = completerMap[message.header.requestId];
|
| - if (c == null) {
|
| + Function callback = callbackMap[message.header.requestId];
|
| + if (callback == null) {
|
| proxyError("Message had unknown request Id: ${message.header.requestId}");
|
| return;
|
| }
|
| - completerMap.remove(message.header.requestId);
|
| - if (c.isCompleted) {
|
| - proxyError("Control message response completer already completed");
|
| - return;
|
| - }
|
| - c.complete(response);
|
| + callbackMap.remove(message.header.requestId);
|
| + callback(
|
| + response.reserved0, response.reserved1, response.queryVersionResult);
|
| + return;
|
| + }
|
| +}
|
| +
|
| +// A class that acts like a function, but which completes a completer with the
|
| +// the result of the function rather than returning the result. E.g.:
|
| +//
|
| +// Completer c = new Completer();
|
| +// var completerator = new Completerator._(c, f);
|
| +// completerator(a, b);
|
| +// await c.future;
|
| +//
|
| +// This completes the future c with the result of passing a and b to f.
|
| +//
|
| +// More usefully for Mojo, e.g.:
|
| +// await _Completerator.completerate(
|
| +// proxy.method, argList, MethodResponseParams#init);
|
| +class _Completerator implements Function {
|
| + final Completer _c;
|
| + final Function _toComplete;
|
| +
|
| + _Completerator._(this._c, this._toComplete);
|
| +
|
| + static Future completerate(Function f, List args, Function ctor) {
|
| + Completer c = new Completer();
|
| + var newArgs = new List.from(args);
|
| + newArgs.add(new _Completerator._(c, ctor));
|
| + Function.apply(f, newArgs);
|
| + return c.future;
|
| }
|
| +
|
| + // Work-around to avoid checked-mode only having grudging support for
|
| + // Function implemented with noSuchMethod. See:
|
| + // https://github.com/dart-lang/sdk/issues/26528
|
| + dynamic call([
|
| + dynamic a1, dynamic a2, dynamic a3, dynamic a4, dynamic a5,
|
| + dynamic a6, dynamic a7, dynamic a8, dynamic a9, dynamic a10,
|
| + dynamic a11, dynamic a12, dynamic a13, dynamic a14, dynamic a15,
|
| + dynamic a16, dynamic a17, dynamic a18, dynamic a19, dynamic a20]);
|
| +
|
| + @override
|
| + dynamic noSuchMethod(Invocation invocation) =>
|
| + (invocation.memberName == #call)
|
| + ? _c.complete(Function.apply(_toComplete, invocation.positionalArguments))
|
| + : super.noSuchMethod(invocation);
|
| +}
|
| +
|
| +/// Base class for Proxy class Futurizing wrappers. It turns callback-based
|
| +/// methods on the Proxy into Future based methods in derived classes. E.g.:
|
| +///
|
| +/// class FuturizedHostResolverProxy extends FuturizedProxy<HostResolverProxy> {
|
| +/// Map<Symbol, Function> _mojoMethods;
|
| +///
|
| +/// FuturizedHostResolverProxy(HostResolverProxy proxy) : super(proxy) {
|
| +/// _mojoMethods = <Symbol, Function>{
|
| +/// #getHostAddresses: proxy.getHostAddresses,
|
| +/// };
|
| +/// }
|
| +/// Map<Symbol, Function> get mojoMethods => _mojoMethods;
|
| +///
|
| +/// FuturizedHostResolverProxy.unbound() :
|
| +/// this(new HostResolverProxy.unbound());
|
| +///
|
| +/// static final Map<Symbol, Function> _mojoResponses = {
|
| +/// #getHostAddresses: new HostResolverGetHostAddressesResponseParams#init,
|
| +/// };
|
| +/// Map<Symbol, Function> get mojoResponses => _mojoResponses;
|
| +/// }
|
| +///
|
| +/// Then:
|
| +///
|
| +/// HostResolveProxy proxy = ...
|
| +/// var futurizedProxy = new FuturizedHostResolverProxy(proxy);
|
| +/// var response = await futurizedProxy.getHostAddresses(host, family);
|
| +/// // etc.
|
| +///
|
| +/// Warning 1: The list of methods and return object constructors in
|
| +/// FuturizedHostResolverProxy has to be kept up-do-date by hand with changes
|
| +/// to the Mojo interface.
|
| +///
|
| +/// Warning 2: The recommended API to use is the generated callback-based API.
|
| +/// This wrapper class is exposed only for convenience during development,
|
| +/// and has no guarantee of optimal performance.
|
| +abstract class FuturizedProxy<T extends Proxy> {
|
| + final T _proxy;
|
| + Map<Symbol, Function> get mojoMethods;
|
| + Map<Symbol, Function> get mojoResponses;
|
| +
|
| + FuturizedProxy(T this._proxy);
|
| +
|
| + T get proxy => _proxy;
|
| + Future responseOrError(Future f) => _proxy.responseOrError(f);
|
| + Future close({immediate: false}) => _proxy.close(immediate: immediate);
|
| +
|
| + @override
|
| + dynamic noSuchMethod(Invocation invocation) =>
|
| + mojoMethods.containsKey(invocation.memberName)
|
| + ? _Completerator.completerate(
|
| + mojoMethods[invocation.memberName],
|
| + invocation.positionalArguments,
|
| + mojoResponses[invocation.memberName])
|
| + : super.noSuchMethod(invocation);
|
| }
|
|
|