blob: 1313609002de2d77afab21a30c262f499610bcf0 [file] [log] [blame]
// Copyright (c) 2012, 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.
import "dart:collection" show HashMap;
patch class ReceivePort {
/* patch */ factory ReceivePort() = _ReceivePortImpl;
/* patch */ factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) =
_ReceivePortImpl.fromRawReceivePort;
}
patch class Capability {
/* patch */ factory Capability() = _CapabilityImpl;
}
class _CapabilityImpl implements Capability {
factory _CapabilityImpl() native "CapabilityImpl_factory";
}
patch class RawReceivePort {
/**
* Opens a long-lived port for receiving messages.
*
* A [RawReceivePort] is low level and does not work with [Zone]s. It
* can not be paused. The data-handler must be set before the first
* event is received.
*/
/* patch */ factory RawReceivePort([void handler(event)]) {
_RawReceivePortImpl result = new _RawReceivePortImpl();
result.handler = handler;
return result;
}
}
class _ReceivePortImpl extends Stream implements ReceivePort {
_ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePort());
_ReceivePortImpl.fromRawReceivePort(this._rawPort) {
_controller = new StreamController(onCancel: close, sync: true);
_rawPort.handler = _controller.add;
}
SendPort get sendPort {
return _rawPort.sendPort;
}
StreamSubscription listen(void onData(var message),
{ Function onError,
void onDone(),
bool cancelOnError }) {
return _controller.stream.listen(onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError);
}
close() {
_rawPort.close();
_controller.close();
}
final RawReceivePort _rawPort;
StreamController _controller;
}
typedef void ImmediateCallback();
/// The callback that has been registered through `scheduleImmediate`.
ImmediateCallback _pendingImmediateCallback;
/// The closure that should be used as scheduleImmediateClosure, when the VM
/// is responsible for the event loop.
void _isolateScheduleImmediate(void callback()) {
assert(_pendingImmediateCallback == null);
_pendingImmediateCallback = callback;
}
void _runPendingImmediateCallback() {
if (_pendingImmediateCallback != null) {
var callback = _pendingImmediateCallback;
_pendingImmediateCallback = null;
callback();
}
}
/// The embedder can execute this function to get hold of
/// [_isolateScheduleImmediate] above.
Function _getIsolateScheduleImmediateClosure() {
return _isolateScheduleImmediate;
}
class _RawReceivePortImpl implements RawReceivePort {
factory _RawReceivePortImpl() native "RawReceivePortImpl_factory";
close() {
// Close the port and remove it from the handler map.
_handlerMap.remove(this._closeInternal());
}
SendPort get sendPort {
return _get_sendport();
}
bool operator==(var other) {
return (other is _RawReceivePortImpl) &&
(this._get_id() == other._get_id());
}
int get hashCode {
return sendPort.hashCode();
}
/**** Internal implementation details ****/
_get_id() native "RawReceivePortImpl_get_id";
_get_sendport() native "RawReceivePortImpl_get_sendport";
// Called from the VM to retrieve the handler for a message.
static _lookupHandler(int id) {
var result = _handlerMap[id];
return result;
}
// Called from the VM to dispatch to the handler.
static void _handleMessage(Function handler, var message) {
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
}
// Call into the VM to close the VM maintained mappings.
_closeInternal() native "RawReceivePortImpl_closeInternal";
void set handler(Function value) {
_handlerMap[this._get_id()] = value;
}
// TODO(iposva): Ideally keep this map in the VM.
// id to handler mapping.
static _initHandlerMap() {
// TODO(18511): Workaround bad CheckSmi hoisting.
var tempMap = new HashMap();
// Collect feedback that not all keys are Smis.
tempMap["."] = 1;
tempMap["."] = 2;
return new HashMap();
}
static final Map _handlerMap = _initHandlerMap();
}
class _SendPortImpl implements SendPort {
/*--- public interface ---*/
void send(var message) {
_sendInternal(message);
}
bool operator==(var other) {
return (other is _SendPortImpl) && (this._get_id() == other._get_id());
}
int get hashCode {
return _get_hashcode();
}
/*--- private implementation ---*/
_get_id() native "SendPortImpl_get_id";
_get_hashcode() native "SendPortImpl_get_hashcode";
// Forward the implementation of sending messages to the VM.
void _sendInternal(var message) native "SendPortImpl_sendInternal_";
}
typedef _MainFunction();
typedef _MainFunctionArgs(args);
typedef _MainFunctionArgsMessage(args, message);
/**
* Takes the real entry point as argument and invokes it with the
* initial message. Defers execution of the entry point until the
* isolate is in the message loop.
*/
void _startMainIsolate(Function entryPoint,
List<String> args) {
RawReceivePort port = new RawReceivePort();
port.handler = (_) {
port.close();
_startIsolate(null, // no parent port
entryPoint,
args,
null, // no message
true, // isSpawnUri
null, // no control port
null); // no capabilities
};
port.sendPort.send(null);
}
/**
* Takes the real entry point as argument and invokes it with the initial
* message.
*/
void _startIsolate(SendPort parentPort,
Function entryPoint,
List<String> args,
var message,
bool isSpawnUri,
RawReceivePort controlPort,
List capabilities) {
if (controlPort != null) {
controlPort.handler = (_) {}; // Nobody home on the control port.
}
if (parentPort != null) {
// Build a message to our parent isolate providing access to the
// current isolate's control port and capabilities.
//
// TODO(floitsch): Send an error message if we can't find the entry point.
var readyMessage = new List(2);
readyMessage[0] = controlPort.sendPort;
readyMessage[1] = capabilities;
// Out of an excess of paranoia we clear the capabilities from the
// stack. Not really necessary.
capabilities = null;
parentPort.send(readyMessage);
}
assert(capabilities == null);
if (isSpawnUri) {
if (entryPoint is _MainFunctionArgsMessage) {
entryPoint(args, message);
} else if (entryPoint is _MainFunctionArgs) {
entryPoint(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
_runPendingImmediateCallback();
}
patch class Isolate {
/* patch */ static Future<Isolate> spawn(
void entryPoint(message), var message, { bool paused: false }) {
// `paused` isn't handled yet.
RawReceivePort readyPort;
try {
// The VM will invoke [_startIsolate] with entryPoint as argument.
readyPort = new RawReceivePort();
_spawnFunction(readyPort.sendPort, entryPoint, message);
Completer completer = new Completer<Isolate>.sync();
readyPort.handler = (readyMessage) {
readyPort.close();
assert(readyMessage is List);
assert(readyMessage.length == 2);
SendPort controlPort = readyMessage[0];
List capabilities = readyMessage[1];
completer.complete(new Isolate(controlPort,
pauseCapability: capabilities[0],
terminateCapability: capabilities[1]));
};
return completer.future;
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
return new Future<Isolate>.error(e, st);
};
}
/* patch */ static Future<Isolate> spawnUri(
Uri uri, List<String> args, var message,
{ bool paused: false, Uri packageRoot }) {
// `paused` isn't handled yet.
RawReceivePort readyPort;
try {
// The VM will invoke [_startIsolate] and not `main`.
readyPort = new RawReceivePort();
var packageRootString =
(packageRoot == null) ? null : packageRoot.toString();
_spawnUri(
readyPort.sendPort, uri.toString(), args, message, packageRootString);
Completer completer = new Completer<Isolate>.sync();
readyPort.handler = (readyMessage) {
readyPort.close();
assert(readyMessage is List);
assert(readyMessage.length == 2);
SendPort controlPort = readyMessage[0];
List capabilities = readyMessage[1];
completer.complete(new Isolate(controlPort,
pauseCapability: capabilities[0],
terminateCapability: capabilities[1]));
};
return completer.future;
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
return new Future<Isolate>.error(e, st);
};
return completer.future;
}
// TODO(iposva): Cleanup to have only one definition.
// These values need to be kept in sync with the class IsolateMessageHandler
// in vm/isolate.cc.
static const _PAUSE = 1;
static const _RESUME = 2;
static SendPort _spawnFunction(SendPort readyPort, Function topLevelFunction,
var message)
native "Isolate_spawnFunction";
static SendPort _spawnUri(SendPort readyPort, String uri,
List<String> args, var message,
String packageRoot)
native "Isolate_spawnUri";
static void _sendOOB(port, msg) native "Isolate_sendOOB";
/* patch */ void _pause(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOM message type.
..[1] = _PAUSE
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
/* patch */ void resume(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOM message type.
..[1] = _RESUME
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
/* patch */ void addOnExitListener(SendPort responsePort) {
throw new UnsupportedError("addOnExitListener");
}
/* patch */ void removeOnExitListener(SendPort responsePort) {
throw new UnsupportedError("removeOnExitListener");
}
/* patch */ void setErrorsFatal(bool errorsAreFatal) {
throw new UnsupportedError("setErrorsFatal");
}
/* patch */ void kill([int priority = BEFORE_NEXT_EVENT]) {
throw new UnsupportedError("kill");
}
/* patch */ void ping(SendPort responsePort, [int pingType = IMMEDIATE]) {
throw new UnsupportedError("ping");
}
/* patch */ void addErrorListener(SendPort port) {
throw new UnsupportedError("addErrorListener");
}
/* patch */ void removeErrorListener(SendPort port) {
throw new UnsupportedError("removeErrorListener");
}
}