| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| part of zircon; |
| |
| // ignore_for_file: public_member_api_docs |
| |
| class ChannelReaderError { |
| final Object error; |
| final StackTrace stacktrace; |
| |
| ChannelReaderError(this.error, this.stacktrace); |
| |
| @override |
| String toString() => error.toString(); |
| } |
| |
| typedef ChannelReaderReadableHandler = void Function(); |
| typedef ChannelReaderErrorHandler = void Function(ChannelReaderError error); |
| |
| class ChannelReader { |
| Channel get channel => _channel; |
| Channel _channel; |
| |
| bool get isBound => _channel != null; |
| |
| HandleWaiter _waiter; |
| |
| ChannelReaderReadableHandler onReadable; |
| ChannelReaderErrorHandler onError; |
| |
| void bind(Channel channel) { |
| if (isBound) { |
| throw ZirconApiError('ChannelReader is already bound.'); |
| } |
| _channel = channel; |
| _asyncWait(); |
| } |
| |
| Channel unbind() { |
| if (!isBound) { |
| throw ZirconApiError('ChannelReader is not bound'); |
| } |
| _waiter?.cancel(); |
| final Channel result = _channel; |
| _channel = null; |
| return result; |
| } |
| |
| void close() { |
| if (!isBound) { |
| return; |
| } |
| _waiter.cancel(); |
| _channel.close(); |
| _channel = null; |
| } |
| |
| void _asyncWait() { |
| _waiter = _channel.handle |
| .asyncWait(Channel.READABLE | Channel.PEER_CLOSED, _handleWaitComplete); |
| } |
| |
| void _errorSoon(ChannelReaderError error) { |
| if (onError == null) { |
| return; |
| } |
| scheduleMicrotask(() { |
| // We need to re-check onError because it might have changed during the |
| // asynchronous gap. |
| if (onError != null) { |
| onError(error); |
| } |
| }); |
| } |
| |
| @override |
| String toString() => 'ChannelReader($_channel)'; |
| |
| void _handleWaitComplete(int status, int pending) { |
| assert(isBound); |
| if (status != ZX.OK) { |
| close(); |
| _errorSoon(ChannelReaderError( |
| 'Wait completed with status ${getStringForStatus(status)} ($status)', |
| null)); |
| return; |
| } |
| // TODO(abarth): Change this try/catch pattern now that we don't use |
| // RawReceivePort any more. |
| try { |
| if ((pending & Channel.READABLE) != 0) { |
| if (onReadable != null) { |
| onReadable(); |
| } |
| if (isBound) { |
| _asyncWait(); |
| } |
| } else if ((pending & Channel.PEER_CLOSED) != 0) { |
| close(); |
| _errorSoon(ChannelReaderError('Peer unexpectedly closed', null)); |
| } |
| // ignore: avoid_catching_errors |
| } on Error catch (_) { |
| // An Error exception from the core libraries is probably a programming |
| // error that can't be handled. We rethrow the error so that |
| // FidlEventHandlers can't swallow it by mistake. |
| rethrow; |
| // ignore: avoid_catches_without_on_clauses |
| } catch (e, s) { |
| print(e); |
| close(); |
| _errorSoon(ChannelReaderError(e, s)); |
| } |
| } |
| } |