blob: 2386c52570be95545fc417924e5820f7658c3055 [file] [log] [blame]
// 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));
}
}
}