Files
sdk/runtime/bin/process_impl.dart
T
sgjesse@google.com 516d71ab0b Change the handling of closing sockets
Sockets now supports being half-closed for either reading or writing. When a socket is closed it is by default closed for both read and write and the underlying file descriptor is destroyed. However the socket close can be asked to only half-close the socket to send end of stream to the other end and still have the ability to receive more data.

The close event on a socket is only emitted when the socket is closed by the other end. Both half-close and full-close by the other end is reported as a close event. If a socket is already half closed the close event will automatically destroy the socket.

The streams on the sockets also takes advantage of this. A socket input stream will report a close event when the other end closed. A socket output stream will half-close the socket when close is called making it possible to still receive data on the input stream. When both streams have been closed the socket is destroyed.

For sockets operating on pipes they are initially created as half-closed for either reading or writing depending on which type of pipe a socket object is based on. A pipe for writing will start half-closed for reading so if it is only half-closed for writing the socket will still be destroyed. Same with a pipe for reading that will start half-closed for writing and when a close event is received half-closing the other direction will destroy the socket. Socket objects based on pipes are only exposed through streams.

Extended the socket close test to test a number of different scenarios.

Also refactor the socket data C++ object to encapsulate more socket information and operations.

Now the Linux and Mac OS versions are 100% the same as it turned out that using POLLRDHUP on Linux was not required any more.

R=ager@google.com

BUG=
TEST=

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@1196 260f80e4-7a28-3924-810f-c04153c831b5
2011-11-04 12:34:43 +00:00

171 lines
4.4 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 _ProcessStartStatus {
int _errorCode; // Set to OS error code if process start failed.
String _errorMessage; // Set to OS error message if process start failed.
}
class _Process implements Process {
_Process(String path, List<String> arguments) {
if (path is !String) {
throw new ProcessException("Path is not a String: $path");
}
_path = path;
if (arguments is !List) {
throw new ProcessException("Arguments is not a List: $arguments");
}
int len = arguments.length;
_arguments = new ObjectArray<String>(len);
for (int i = 0; i < len; i++) {
var arg = arguments[i];
if (arg is !String) {
throw new ProcessException("Non-string argument: $arg");
}
_arguments[i] = arguments[i];
}
_in = new _Socket._internalOutputOnly();
_out = new _Socket._internalInputOnly();
_err = new _Socket._internalOutputOnly();
_exitHandler = new _Socket._internal();
_closed = false;
_killed = false;
_started = false;
_exitHandlerCallback = null;
}
void start() {
var status = new _ProcessStartStatus();
bool success = _start(
_path, _arguments, _in, _out, _err, _exitHandler, status);
if (!success) {
close();
throw new ProcessException(status._errorMessage, status._errorCode);
}
_started = true;
// Make sure to activate socket handlers now that the file
// descriptors have been set.
_in._activateHandlers();
_out._activateHandlers();
_err._activateHandlers();
// Setup an exit handler to handle internal cleanup and possible
// callback when a process terminates.
_exitHandler.dataHandler = () {
final int EXIT_DATA_SIZE = 8;
List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE);
InputStream input = _exitHandler.inputStream;
int exitDataRead = 0;
int exitCode(List<int> ints) {
return ints[4] + (ints[5] << 8) + (ints[6] << 16) + (ints[7] << 24);
}
int exitPid(List<int> ints) {
return ints[0] + (ints[1] << 8) + (ints[2] << 16) + (ints[3] << 24);
}
void handleExit() {
_processExit(exitPid(exitDataBuffer));
if (_exitHandlerCallback !== null) {
_exitHandlerCallback(exitCode(exitDataBuffer));
}
}
void exitData() {
exitDataRead += input.readInto(
exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead);
if (exitDataRead == EXIT_DATA_SIZE) handleExit();
}
input.dataHandler = exitData;
};
}
bool _start(String path,
List<String> arguments,
Socket input,
Socket output,
Socket error,
Socket exitHandler,
_ProcessStartStatus status) native "Process_Start";
void _processExit(int pid) native "Process_Exit";
InputStream get stdout() {
if (_closed) {
throw new ProcessException("Process closed");
}
return _in.inputStream;
}
InputStream get stderr() {
if (_closed) {
throw new ProcessException("Process closed");
}
return _err.inputStream;
}
OutputStream get stdin() {
if (_closed) {
throw new ProcessException("Process closed");
}
return _out.outputStream;
}
bool kill() {
if (_closed && _pid === null) {
throw new ProcessException("Process closed");
}
if (_killed) {
return true;
}
if (_kill(_pid)) {
_killed = true;
return true;
}
return false;
}
void _kill(int pid) native "Process_Kill";
void close() {
if (_closed) {
throw new ProcessException("Process closed");
}
_in.close();
_out.close();
_err.close();
_exitHandler.close();
_closed = true;
}
void set exitHandler(void callback(int exitCode)) {
if (_closed) {
throw new ProcessException("Process closed");
}
if (_killed) {
throw new ProcessException("Process killed");
}
_exitHandlerCallback = callback;
}
String _path;
ObjectArray<String> _arguments;
Socket _in;
Socket _out;
Socket _err;
Socket _exitHandler;
int _pid;
bool _closed;
bool _killed;
bool _started;
var _exitHandlerCallback;
}