Files
sdk/runtime/bin/socket_impl.dart
T
ager@google.com 0a03298fea Fix hang when opening sockets on ports that are not available.
Fix TimeoutTest to use free ports instead of using the same port
always. This does not work because tests are run in parallel and
we can have more than one instance of TimeoutTest running.

R=sgjesse@google.com,whesse@google.com
BUG=
TEST=

Review URL: http://codereview.chromium.org//8498009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@1310 260f80e4-7a28-3924-810f-c04153c831b5
2011-11-08 13:26:51 +00:00

359 lines
9.3 KiB
Dart

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class _SocketBase {
// Bit flags used when communicating between the eventhandler and
// dart code. The EVENT flags are used to indicate events of
// interest when sending a message from dart code to the
// eventhandler. When receiving a message from the eventhandler the
// EVENT flags indicate the events that actually happened. The
// COMMAND flags are used to send commands from dart to the
// eventhandler. COMMAND flags are never received from the
// eventhandler. Additional flags are used to communicate other
// information.
static final int _IN_EVENT = 0;
static final int _OUT_EVENT = 1;
static final int _ERROR_EVENT = 2;
static final int _CLOSE_EVENT = 3;
static final int _CLOSE_COMMAND = 8;
static final int _SHUTDOWN_READ_COMMAND = 9;
static final int _SHUTDOWN_WRITE_COMMAND = 10;
// Flag send to the eventhandler saying that the file descriptor in
// question represents a listening socket.
static final int _LISTENING_SOCKET = 16;
static final int _FIRST_EVENT = _IN_EVENT;
static final int _LAST_EVENT = _CLOSE_EVENT;
static final int _FIRST_COMMAND = _CLOSE_COMMAND;
static final int _LAST_COMMAND = _SHUTDOWN_WRITE_COMMAND;
_SocketBase () {
_handler = new ReceivePort();
_handlerMap = new List(_LAST_EVENT + 1);
_handlerMask = 0;
_canActivateHandlers = true;
_id = -1;
_handler.receive((var message, ignored) {
_multiplex(message);
});
EventHandler._start();
}
/*
* Multiplexes socket events to the right socket handler.
*/
void _multiplex(List<int> message) {
assert(message.length == 1);
_canActivateHandlers = false;
int event_mask = message[0];
for (int i = _FIRST_EVENT; i <= _LAST_EVENT; i++) {
if (((event_mask & (1 << i)) != 0)) {
if ((i == _CLOSE_EVENT) && this is _Socket && _id >= 0) {
if (_closedWrite) _close();
}
var eventHandler = _handlerMap[i];
if (eventHandler != null) {
// Unregister the out handler before executing it.
if (i == _OUT_EVENT) _setHandler(i, null);
// Don't call the in handler if there is no data available
// after all.
if (i == _IN_EVENT && this is _Socket && available() == 0) {
continue;
}
eventHandler();
}
}
}
_canActivateHandlers = true;
_activateHandlers();
}
void _setHandler(int event, void callback()) {
if (callback == null) {
_handlerMask &= ~(1 << event);
} else {
_handlerMask |= (1 << event);
}
_handlerMap[event] = callback;
_activateHandlers();
}
void _getPort() native "Socket_GetPort";
void set errorHandler(void callback()) {
_setHandler(_ERROR_EVENT, callback);
}
void _activateHandlers() {
if (_canActivateHandlers && (_id >= 0)) {
int data = _handlerMask;
if (_isListenSocket()) {
data |= (1 << _LISTENING_SOCKET);
} else {
if (_closedRead) { data &= ~(1 << _IN_EVENT); }
if (_closedWrite) { data &= ~(1 << _OUT_EVENT); }
if (_isListenSocket()) data |= (1 << _LISTENING_SOCKET);
}
EventHandler._sendData(_id, _handler, data);
}
}
void _scheduleEvent(int event) {
_handler.toSendPort().send([1 << event], null);
}
int get port() {
if (_port === null) {
_port = _getPort();
}
return _port;
}
void close([bool halfClose = false]) {
if (_id >= 0) {
if (halfClose) {
_closeWrite();
} else {
_close();
}
} else if (_handler != null) {
// This is to support closing sockets created but never assigned
// any actual socket.
_handler.close();
_handler = null;
}
}
void _closeWrite() {
if (_closedRead) {
_close();
} else {
EventHandler._sendData(_id, _handler, 1 << _SHUTDOWN_WRITE_COMMAND);
}
_closedWrite = true;
}
void _closeRead() {
if (_closedWrite) {
_close();
} else {
EventHandler._sendData(_id, _handler, 1 << _SHUTDOWN_READ_COMMAND);
}
_closedRead = true;
}
void _close() {
EventHandler._sendData(_id, _handler, 1 << _CLOSE_COMMAND);
_handler.close();
_handler = null;
_id = -1;
}
abstract bool _isListenSocket();
/*
* Socket id is set from native. -1 indicates that the socket was closed.
*/
int _id;
/*
* Dedicated ReceivePort for socket events.
*/
ReceivePort _handler;
/*
* Poll event to handler map.
*/
List _handlerMap;
/*
* Indicates for which poll events the socket registered handlers.
*/
int _handlerMask;
/*
* Indicates if native interrupts can be activated.
*/
bool _canActivateHandlers;
/*
* Holds the port of the socket, null if not known.
*/
int _port;
}
class _ServerSocket extends _SocketBase implements ServerSocket {
/*
* Constructor for server socket. First a socket object is allocated
* in which the native socket is stored. After that _createBind
* is called which creates a file descriptor and binds the given address
* and port to the socket. Null is returned if file descriptor creation or
* bind failed.
*/
factory _ServerSocket(String bindAddress, int port, int backlog) {
ServerSocket socket = new _ServerSocket._internal();
if (!socket._createBindListen(bindAddress, port, backlog)) {
socket.close();
return null;
}
if (port != 0) {
socket._port = port;
}
return socket;
}
_ServerSocket._internal();
Socket accept() {
if (_id >= 0) {
_Socket socket = new _Socket._internal();
if (_accept(socket)) {
return socket;
}
return null;
}
throw new
SocketIOException("Error: accept failed - invalid socket handle");
}
bool _accept(Socket socket) native "ServerSocket_Accept";
bool _createBindListen(String bindAddress, int port, int backlog)
native "ServerSocket_CreateBindListen";
void set connectionHandler(void callback()) {
_setHandler(_IN_EVENT, callback);
}
bool _isListenSocket() => true;
}
class _Socket extends _SocketBase implements Socket {
/*
* Constructor for socket. First a socket object is allocated
* in which the native socket is stored. After that _createConnect is
* called which creates a file discriptor and connects to the given
* host on the given port. Null is returned if file descriptor creation
* or connect failed.
*/
factory _Socket(String host, int port) {
Socket socket = new _Socket._internal();
if (!socket._createConnect(host, port)) {
socket.close();
return null;
}
return socket;
}
_Socket._internal();
_Socket._internalInputOnly() : _closedRead = true;
_Socket._internalOutputOnly() : _closedWrite = true;
int available() {
if (_id >= 0) {
return _available();
}
throw new
SocketIOException("Error: available failed - invalid socket handle");
}
int _available() native "Socket_Available";
bool get closed() => _closed;
int readList(List<int> buffer, int offset, int bytes) {
if (_id >= 0) {
if (bytes == 0) {
return 0;
}
if (offset < 0) {
throw new IndexOutOfRangeException(offset);
}
if (bytes < 0) {
throw new IndexOutOfRangeException(bytes);
}
if ((offset + bytes) > buffer.length) {
throw new IndexOutOfRangeException(offset + bytes);
}
return _readList(buffer, offset, bytes);
}
throw new
SocketIOException("Error: readList failed - invalid socket handle");
}
int _readList(List<int> buffer, int offset, int bytes)
native "Socket_ReadList";
int writeList(List<int> buffer, int offset, int bytes) {
if (_id >= 0) {
if (bytes == 0) {
return 0;
}
if (offset < 0) {
throw new IndexOutOfRangeException(offset);
}
if (bytes < 0) {
throw new IndexOutOfRangeException(bytes);
}
if ((offset + bytes) > buffer.length) {
throw new IndexOutOfRangeException(offset + bytes);
}
return _writeList(buffer, offset, bytes);
}
throw new
SocketIOException("Error: writeList failed - invalid socket handle");
}
int _writeList(List<int> buffer, int offset, int bytes)
native "Socket_WriteList";
bool _createConnect(String host, int port) native "Socket_CreateConnect";
void set writeHandler(void callback()) {
_setHandler(_OUT_EVENT, callback);
}
void set connectHandler(void callback()) {
_setHandler(_OUT_EVENT, callback);
}
void set dataHandler(void callback()) {
_setHandler(_IN_EVENT, callback);
}
void set closeHandler(void callback()) {
_setHandler(_CLOSE_EVENT, callback);
}
bool _isListenSocket() => false;
InputStream get inputStream() {
if (_inputStream === null) {
_inputStream = new SocketInputStream(this);
}
return _inputStream;
}
OutputStream get outputStream() {
if (_outputStream === null) {
_outputStream = new SocketOutputStream(this);
}
return _outputStream;
}
bool _closedRead = false;
bool _closedWrite = false;
SocketInputStream _inputStream;
SocketOutputStream _outputStream;
}