blob: ef2068b76017a3330ed4db8007cac3d69a778c22 [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 SocketReaderError extends Error {
final Object error;
final StackTrace stacktrace;
SocketReaderError(this.error, this.stacktrace);
@override
String toString() => error.toString();
}
typedef SocketReaderReadableHandler = void Function();
typedef SocketReaderErrorHandler = void Function(SocketReaderError error);
class SocketReader {
Socket get socket => _socket;
Socket _socket;
bool get isBound => _socket != null;
HandleWaiter _waiter;
SocketReaderReadableHandler onReadable;
SocketReaderErrorHandler onError;
void bind(Socket socket) {
if (isBound) {
throw ZirconApiError('SocketReader is already bound.');
}
_socket = socket;
_asyncWait();
}
Socket unbind() {
if (!isBound) {
throw ZirconApiError('SocketReader is not bound');
}
_waiter.cancel();
final Socket result = _socket;
_socket = null;
return result;
}
void close() {
if (!isBound) {
return;
}
_waiter.cancel();
_socket.close();
_socket = null;
}
void _asyncWait() {
_waiter = _socket.handle
.asyncWait(Socket.READABLE | Socket.PEER_CLOSED, _handleWaitComplete);
}
void _errorSoon(SocketReaderError 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() => 'SocketReader($_socket)';
void _handleWaitComplete(int status, int pending) {
assert(isBound);
if (status != ZX.OK) {
close();
_errorSoon(SocketReaderError(
'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 & Socket.READABLE) != 0) {
if (onReadable != null) {
onReadable();
}
if (isBound) {
_asyncWait();
}
} else if ((pending & Socket.PEER_CLOSED) != 0) {
close();
_errorSoon(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) {
close();
_errorSoon(SocketReaderError(e, s));
}
}
}