[ package:dds / Service ] Added support for client naming and resume permissions

Also marked `getClientName`, `setClientName`,
`requirePermissionToResume` and `ClientName` as deprecated in the VM
service specification as these features will be moving to DDS and will
be removed in VM service protocol 4.0.

Change-Id: I4628ece96349a9883ee9d726d82e5cfae028a826
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144986
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ben Konyi 2020-04-30 20:00:22 +00:00 committed by commit-bot@chromium.org
parent 8d16dbb17f
commit bb959fc0de
19 changed files with 616 additions and 58 deletions

View file

@ -8,14 +8,19 @@ The Service Protocol uses [JSON-RPC 2.0][].
[JSON-RPC 2.0]: http://www.jsonrpc.org/specification
**Table of Contents**
## Table of Contents
- [RPCs, Requests, and Responses](#rpcs-requests-and-responses)
- [Events](#events)
- [Types](#types)
- [IDs and Names](#ids-and-names)
- [Revision History](#revision-history)
- [Public RPCs](#public-rpcs)
- [getClientName](#getclientname)
- [requirePermissionToResume](#requirepermissiontoresume)
- [setClientName](#setclientname)
- [Public Types](#public-types)
- [ClientName](#clientname)
## RPCs, Requests, and Responses
@ -41,16 +46,95 @@ See the corresponding section in the VM Service protocol [here][service-protocol
The DDS Protocol supports all [public RPCs defined in the VM Service protocol][service-protocol-public-rpcs].
### getClientName
```
ClientName getClientName()
```
The _getClientName_ RPC is used to retrieve the name associated with the currently
connected VM service client. If no name was previously set through the
[setClientName](#setclientname) RPC, a default name will be returned.
See [ClientName](#clientname)
### requirePermissionToResume
```
Success requirePermissionToResume(bool onPauseStart [optional],
bool onPauseReload[optional],
bool onPauseExit [optional])
```
The _requirePermissionToResume_ RPC is used to change the pause/resume behavior
of isolates by providing a way for the VM service to wait for approval to resume
from some set of clients. This is useful for clients which want to perform some
operation on an isolate after a pause without it being resumed by another client.
If the _onPauseStart_ parameter is `true`, isolates will not resume after pausing
on start until the client sends a `resume` request and all other clients which
need to provide resume approval for this pause type have done so.
If the _onPauseReload_ parameter is `true`, isolates will not resume after pausing
after a reload until the client sends a `resume` request and all other clients
which need to provide resume approval for this pause type have done so.
If the _onPauseExit_ parameter is `true`, isolates will not resume after pausing
on exit until the client sends a `resume` request and all other clients which
need to provide resume approval for this pause type have done so.
**Important Notes:**
- All clients with the same client name share resume permissions. Only a
single client of a given name is required to provide resume approval.
- When a client requiring approval disconnects from the service, a paused
isolate may resume if all other clients requiring resume approval have
already given approval. In the case that no other client requires resume
approval for the current pause event, the isolate will be resumed if at
least one other client has attempted to [resume](resume) the isolate.
### setClientName
```
Success setClientName(string name)
```
The _setClientName_ RPC is used to set a name to be associated with the currently
connected VM service client. If the _name_ parameter is a non-empty string, _name_
will become the new name associated with the client. If _name_ is an empty string,
the client's name will be reset to its default name.
See [Success](#success).
## Public Types
The DDS Protocol supports all [public types defined in the VM Service protocol][service-protocol-public-types].
### ClientName
```
class ClientName extends Response {
// The name of the currently connected VM service client.
string name;
}
```
See [getClientName](#getclientname) and [setClientName](#setclientname).
## Revision History
version | comments
------- | --------
0.x | Initial revision
[resume]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#resume
[success]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#success
[service-protocol]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md
[service-protocol-rpcs-requests-and-responses]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses
[service-protocol-events]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#events
[service-protocol-binary-events]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#binary-events
[service-protocol-types]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#types
[service-protocol-ids-and-names]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#ids-and-names
[service-protocol-public-rpcs]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#public-rpcs
[service-protocol-public-rpcs]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#public-rpcs
[service-protocol-public-types]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#public-types

View file

@ -25,7 +25,10 @@ import 'package:web_socket_channel/web_socket_channel.dart';
part 'src/binary_compatible_peer.dart';
part 'src/client.dart';
part 'src/client_manager.dart';
part 'src/constants.dart';
part 'src/dds_impl.dart';
part 'src/isolate_manager.dart';
part 'src/named_lookup.dart';
part 'src/rpc_error_codes.dart';
part 'src/stream_manager.dart';

View file

@ -51,13 +51,13 @@ class _DartDevelopmentServiceClient {
_clientPeer.registerMethod('streamListen', (parameters) async {
final streamId = parameters['streamId'].asString;
await dds.streamManager.streamListen(this, streamId);
return _success;
return _RPCResponses.success;
});
_clientPeer.registerMethod('streamCancel', (parameters) async {
final streamId = parameters['streamId'].asString;
await dds.streamManager.streamCancel(this, streamId);
return _success;
return _RPCResponses.success;
});
_clientPeer.registerMethod('registerService', (parameters) async {
@ -75,9 +75,30 @@ class _DartDevelopmentServiceClient {
serviceId,
alias,
);
return _success;
return _RPCResponses.success;
});
_clientPeer.registerMethod(
'getClientName',
(parameters) => {'type': 'ClientName', 'name': name},
);
_clientPeer.registerMethod(
'setClientName',
(parameters) => dds.clientManager.setClientName(this, parameters),
);
_clientPeer.registerMethod(
'requirePermissionToResume',
(parameters) =>
dds.clientManager.requirePermissionToResume(this, parameters),
);
_clientPeer.registerMethod(
'resume',
(parameters) => dds.isolateManager.resumeIsolate(this, parameters),
);
// When invoked within a fallback, the next fallback will start executing.
// The final fallback forwards the request to the VM service directly.
@alwaysThrows
@ -96,7 +117,7 @@ class _DartDevelopmentServiceClient {
// method, forward the request to that client.
final method = getMethod(parameters.method);
final namespace = getNamespace(parameters.method);
final serviceClient = dds._clients[namespace];
final serviceClient = dds.clientManager.clients[namespace];
if (serviceClient != null && serviceClient.services.containsKey(method)) {
return await Future.any(
[
@ -123,9 +144,20 @@ class _DartDevelopmentServiceClient {
await _vmServicePeer.sendRequest(parameters.method, parameters.asMap));
}
static const _success = <String, dynamic>{
'type': 'Success',
};
static int _idCounter = 0;
final int _id = ++_idCounter;
/// The name given to the client upon its creation.
String get defaultClientName => 'client$_id';
/// The current name associated with this client.
String get name => _name;
// NOTE: this should not be called directly except from:
// - `_ClientManager._clearClientName`
// - `_ClientManager._setClientNameHelper`
set name(String n) => _name = n ?? defaultClientName;
String _name;
final _DartDevelopmentService dds;
final Map<String, String> services = {};

View file

@ -0,0 +1,146 @@
// Copyright (c) 2020, 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.
part of dds;
/// [_ClientResumePermissions] associates a list of
/// [_DartDevelopmentServiceClient]s, all of the same client name, with a
/// permissions mask used to determine which pause event types require approval
/// from one of the listed clients before resuming an isolate.
class _ClientResumePermissions {
final List<_DartDevelopmentServiceClient> clients = [];
int permissionsMask = 0;
}
/// The [_ClientManager] has the responsibility of managing all state and
/// requests related to client connections, including:
/// - A list of all currently connected clients
/// - Tracking client names and associated permissions for isolate resume
/// synchronization
/// - Handling RPC invocations which change client state
class _ClientManager {
_ClientManager(this.dds);
/// Initialize state for a newly connected client.
void addClient(_DartDevelopmentServiceClient client) {
_setClientNameHelper(
client,
client.defaultClientName,
);
clients.add(client);
client.listen().then((_) => removeClient(client));
}
/// Cleanup state for a disconnected client.
void removeClient(_DartDevelopmentServiceClient client) {
_clearClientName(client);
clients.remove(client);
}
/// Cleanup clients on DDS shutdown.
Future<void> shutdown() async {
// Close all incoming websocket connections.
final futures = <Future>[];
for (final client in clients) {
futures.add(client.close());
}
await Future.wait(futures);
}
/// Associates a name with a given client.
///
/// The provided client name is used to track isolate resume approvals.
Map<String, dynamic> setClientName(
_DartDevelopmentServiceClient client,
json_rpc.Parameters parameters,
) {
_setClientNameHelper(client, parameters['name'].asString);
return _RPCResponses.success;
}
/// Require permission from this client before resuming an isolate.
Map<String, dynamic> requirePermissionToResume(
_DartDevelopmentServiceClient client,
json_rpc.Parameters parameters,
) {
int pauseTypeMask = 0;
if (parameters['onPauseStart'].asBoolOr(false)) {
pauseTypeMask |= _PauseTypeMasks.pauseOnStartMask;
}
if (parameters['onPauseReload'].asBoolOr(false)) {
pauseTypeMask |= _PauseTypeMasks.pauseOnReloadMask;
}
if (parameters['onPauseExit'].asBoolOr(false)) {
pauseTypeMask |= _PauseTypeMasks.pauseOnExitMask;
}
clientResumePermissions[client.name].permissionsMask = pauseTypeMask;
return _RPCResponses.success;
}
/// Changes `client`'s name to `name` while also updating resume permissions
/// and approvals.
void _setClientNameHelper(
_DartDevelopmentServiceClient client,
String name,
) {
_clearClientName(client);
client.name = name.isEmpty ? client.defaultClientName : name;
clientResumePermissions.putIfAbsent(
client.name,
() => _ClientResumePermissions(),
);
clientResumePermissions[client.name].clients.add(client);
}
/// Resets the client's name while also cleaning up resume permissions and
/// approvals.
void _clearClientName(
_DartDevelopmentServiceClient client,
) {
final name = client.name;
client.name = null;
final clientsForName = clientResumePermissions[name];
if (clientsForName != null) {
clientsForName.clients.remove(client);
// If this was the last client with a given name, cleanup resume
// permissions.
if (clientsForName.clients.isEmpty) {
clientResumePermissions.remove(name);
// Check to see if we need to resume any isolates now that the last
// client of a given name has disconnected or changed names.
//
// An isolate will be resumed in this situation if:
//
// 1) This client required resume approvals for the current pause event
// associated with the isolate and all other required resume approvals
// have been provided by other clients.
//
// OR
//
// 2) This client required resume approvals for the current pause event
// associated with the isolate, no other clients require resume approvals
// for the current pause event, and at least one client has issued a resume
// request.
dds.isolateManager.isolates.forEach(
(_, isolate) async =>
await isolate.maybeResumeAfterClientChange(name),
);
}
}
}
// Handles namespace generation for service extensions.
static const _kServicePrologue = 's';
final NamedLookup<_DartDevelopmentServiceClient> clients = NamedLookup(
prologue: _kServicePrologue,
);
/// Mapping of client names to all clients of that name and their resume
/// permissions.
final Map<String, _ClientResumePermissions> clientResumePermissions = {};
final _DartDevelopmentService dds;
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2020, 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.
part of dds;
abstract class _RPCResponses {
static const success = <String, dynamic>{
'type': 'Success',
};
static const collectedSentinel = <String, dynamic>{
'type': 'Sentinel',
'kind': 'Collected',
'valueAsString': '<collected>',
};
}
abstract class _PauseTypeMasks {
static const pauseOnStartMask = 1 << 0;
static const pauseOnReloadMask = 1 << 1;
static const pauseOnExitMask = 1 << 2;
}
abstract class _ServiceEvents {
static const isolateExit = 'IsolateExit';
static const isolateSpawn = 'IsolateSpawn';
static const isolateStart = 'IsolateStart';
static const pauseExit = 'PauseExit';
static const pausePostRequest = 'PausePostRequest';
static const pauseStart = 'PauseStart';
static const resume = 'Resume';
}

View file

@ -6,18 +6,24 @@ part of dds;
class _DartDevelopmentService implements DartDevelopmentService {
_DartDevelopmentService(this._remoteVmServiceUri, this._uri) {
_clientManager = _ClientManager(this);
_isolateManager = _IsolateManager(this);
_streamManager = _StreamManager(this);
}
Future<void> startService() async {
// TODO(bkonyi): throw if we've already shutdown.
// Establish the connection to the VM service.
_vmServiceSocket = WebSocketChannel.connect(remoteVmServiceWsUri);
_vmServiceClient = _BinaryCompatiblePeer(_vmServiceSocket, _streamManager);
// Setup the JSON RPC client with the VM service.
unawaited(_vmServiceClient.listen());
unawaited(_vmServiceClient.listen().then((_) => shutdown()));
// Setup stream event handling.
streamManager.listen();
await streamManager.listen();
// Populate initial isolate state.
await _isolateManager.initialize();
// Once we have a connection to the VM service, we're ready to spawn the intermediary.
await _startDDSServer();
@ -52,15 +58,16 @@ class _DartDevelopmentService implements DartDevelopmentService {
/// Stop accepting requests after gracefully handling existing requests.
Future<void> shutdown() async {
if (_done.isCompleted || _shuttingDown) {
// Already shutdown.
return;
}
_shuttingDown = true;
// Don't accept anymore HTTP requests.
await _server.close();
// Close all incoming websocket connections.
final futures = <Future>[];
for (final client in _clients) {
futures.add(client.close());
}
await Future.wait(futures);
// Close connections to clients.
await clientManager.shutdown();
// Close connection to VM service.
await _vmServiceSocket.sink.close();
@ -79,8 +86,7 @@ class _DartDevelopmentService implements DartDevelopmentService {
ws,
_vmServiceClient,
);
_clients.add(client);
client.listen().then((_) => _clients.remove(client));
clientManager.addClient(client);
});
Handler _httpHandler() {
@ -108,7 +114,7 @@ class _DartDevelopmentService implements DartDevelopmentService {
}
String _getNamespace(_DartDevelopmentServiceClient client) =>
_clients.keyOf(client);
clientManager.clients.keyOf(client);
Uri get remoteVmServiceUri => _remoteVmServiceUri;
Uri get remoteVmServiceWsUri => _toWebSocket(_remoteVmServiceUri);
@ -122,16 +128,17 @@ class _DartDevelopmentService implements DartDevelopmentService {
Future<void> get done => _done.future;
Completer _done = Completer<void>();
bool _shuttingDown = false;
_ClientManager get clientManager => _clientManager;
_ClientManager _clientManager;
_IsolateManager get isolateManager => _isolateManager;
_IsolateManager _isolateManager;
_StreamManager get streamManager => _streamManager;
_StreamManager _streamManager;
// Handles namespace generation for service extensions.
static const _kServicePrologue = 's';
final NamedLookup<_DartDevelopmentServiceClient> _clients = NamedLookup(
prologue: _kServicePrologue,
);
json_rpc.Peer _vmServiceClient;
WebSocketChannel _vmServiceSocket;
HttpServer _server;

View file

@ -0,0 +1,228 @@
// Copyright (c) 2020, 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.
part of dds;
/// This file contains functionality used to track the running state of
/// all isolates in a given Dart process.
///
/// [_RunningIsolate] is a representation of a single live isolate and contains
/// running state information for that isolate. In addition, approvals from
/// clients used to synchronize isolate resuming across multiple clients are
/// tracked in this class.
///
/// The [_IsolateManager] keeps track of all the isolates in the
/// target process and handles isolate lifecycle events including:
/// - Startup
/// - Shutdown
/// - Pauses
///
/// The [_IsolateManager] also handles the `resume` RPC, which checks the
/// resume approvals in the target [_RunningIsolate] to determine if the
/// isolate should be resumed or wait for additional approvals to be granted.
enum _IsolateState {
start,
running,
pauseStart,
pauseExit,
pausePostRequest,
}
class _RunningIsolate {
_RunningIsolate(this.isolateManager, this.portId, this.name);
// State setters.
void pausedOnExit() => _state = _IsolateState.pauseExit;
void pausedOnStart() => _state = _IsolateState.pauseStart;
void pausedPostRequest() => _state = _IsolateState.pausePostRequest;
void resumed() => running();
void running() => _state = _IsolateState.running;
void started() => _state = _IsolateState.start;
/// Resumes the isolate if all clients which need to approve a resume have
/// done so. Called when the last client of a given name disconnects or
/// changes name to ensure we don't deadlock waiting for approval to resume
/// from a disconnected client.
Future<void> maybeResumeAfterClientChange(String clientName) async {
// Remove approvals from the disconnected client.
_resumeApprovalsByName.remove(clientName);
if (shouldResume()) {
clearResumeApprovals();
await isolateManager.dds._vmServiceClient.sendRequest('resume', {
'isolateId': 'isolates/$portId',
});
}
}
/// Returns true if this isolate should resume given its client approvals
/// state.
///
/// If `resumingClient` is provided, it will be added to the set of clients
/// which have provided approval to resume this isolate. If not provided,
/// the existing approvals state will be examined to see if the isolate
/// should resume due to a client disconnect or name change.
bool shouldResume({_DartDevelopmentServiceClient resumingClient}) {
if (resumingClient != null) {
// Mark approval by the client.
_resumeApprovalsByName.add(resumingClient.name);
}
final requiredClientApprovals = <String>{};
final permissions =
isolateManager.dds.clientManager.clientResumePermissions;
// Determine which clients require approval for this pause type.
permissions.forEach((name, clientNamePermissions) {
if (clientNamePermissions.permissionsMask & _isolateStateMask != 0) {
requiredClientApprovals.add(name);
}
});
// We require at least a single client to resume, even if that client
// doesn't require resume approval.
if (_resumeApprovalsByName.isEmpty) {
return false;
}
// If all the required approvals are present, we should resume.
return _resumeApprovalsByName.containsAll(requiredClientApprovals);
}
/// Resets the internal resume approvals state.
///
/// Should always be called after an isolate is resumed.
void clearResumeApprovals() => _resumeApprovalsByName.clear();
int get _isolateStateMask => isolateStateToMaskMapping[_state] ?? 0;
static const isolateStateToMaskMapping = {
_IsolateState.pauseStart: _PauseTypeMasks.pauseOnStartMask,
_IsolateState.pausePostRequest: _PauseTypeMasks.pauseOnReloadMask,
_IsolateState.pauseExit: _PauseTypeMasks.pauseOnExitMask,
};
final _IsolateManager isolateManager;
final String name;
final String portId;
final Set<String> _resumeApprovalsByName = {};
_IsolateState _state;
}
class _IsolateManager {
_IsolateManager(this.dds);
/// Handles state changes for isolates.
void handleIsolateEvent(json_rpc.Parameters parameters) {
final event = parameters['event'];
final eventKind = event['kind'].asString;
// There's no interesting information about isolate state associated with
// and IsolateSpawn event.
if (eventKind == _ServiceEvents.isolateSpawn) {
return;
}
final isolateData = event['isolate'];
final id = isolateData['number'].asString;
final name = isolateData['name'].asString;
switch (eventKind) {
case _ServiceEvents.isolateStart:
isolateStarted(id, name);
break;
case _ServiceEvents.isolateExit:
isolateExited(id);
break;
default:
final isolate = isolates[id];
switch (eventKind) {
case _ServiceEvents.pauseExit:
isolate.pausedOnExit();
break;
case _ServiceEvents.pausePostRequest:
isolate.pausedPostRequest();
break;
case _ServiceEvents.pauseStart:
isolate.pausedOnStart();
break;
case _ServiceEvents.resume:
isolate.resumed();
break;
default:
break;
}
}
}
/// Initializes the set of running isolates.
Future<void> initialize() async {
final vm = await dds._vmServiceClient.sendRequest('getVM');
final List<Map> isolates = vm['isolates'].cast<Map<String, dynamic>>();
isolates.forEach(
(isolate) => isolateStarted(
isolate['number'],
isolate['name'],
),
);
}
/// Initializes state for a newly started isolate.
void isolateStarted(String portId, String name) {
final isolate = _RunningIsolate(this, portId, name);
isolate.running();
isolates[portId] = isolate;
}
/// Cleans up state for an isolate that has exited.
void isolateExited(String portId) {
isolates.remove(portId);
}
/// Handles `resume` RPC requests. If the client requires that approval be
/// given before resuming an isolate, this method will:
///
/// - Update the approval state for the isolate.
/// - Resume the isolate if approval has been given by all clients which
/// require approval.
///
/// Returns a collected sentinel if the isolate no longer exists.
Future<Map<String, dynamic>> resumeIsolate(
_DartDevelopmentServiceClient client,
json_rpc.Parameters parameters,
) async {
final isolateId = parameters['isolateId'].asString;
final portId = _isolateIdToPortId(isolateId);
final isolate = isolates[portId];
if (isolate == null) {
return _RPCResponses.collectedSentinel;
}
if (isolate.shouldResume(resumingClient: client)) {
isolate.clearResumeApprovals();
return await _sendResumeRequest(isolateId, parameters);
}
return _RPCResponses.success;
}
/// Forwards a `resume` request to the VM service.
Future<Map<String, dynamic>> _sendResumeRequest(
String isolateId,
json_rpc.Parameters parameters,
) async {
final step = parameters['step'].asStringOr(null);
final frameIndex = parameters['frameIndex'].asIntOr(null);
final resumeResult = await dds._vmServiceClient.sendRequest('resume', {
'isolateId': isolateId,
if (step != null) 'step': step,
if (frameIndex != null) 'frameIndex': frameIndex,
});
return resumeResult;
}
static String _isolateIdToPortId(String isolateId) =>
isolateId.substring('isolates/'.length);
final _DartDevelopmentService dds;
final Map<String, _RunningIsolate> isolates = {};
}

View file

@ -83,13 +83,24 @@ class _StreamManager {
/// Start listening for `streamNotify` events from the VM service and forward
/// them to the clients which have subscribed to the stream.
void listen() => dds._vmServiceClient.registerMethod(
'streamNotify',
(parameters) {
final streamId = parameters['streamId'].asString;
streamNotify(streamId, parameters.value);
},
);
Future<void> listen() async {
// The _IsolateManager requires information from both the Debug and
// Isolate streams, so they must always be subscribed to by DDS.
for (final stream in ddsCoreStreams) {
await streamListen(null, stream);
}
dds._vmServiceClient.registerMethod(
'streamNotify',
(parameters) {
final streamId = parameters['streamId'].asString;
// Forward events from the streams _IsolateManager subscribes to.
if (isolateManagerStreams.contains(streamId)) {
dds.isolateManager.handleIsolateEvent(parameters);
}
streamNotify(streamId, parameters.value);
},
);
}
/// Subscribes `client` to a stream.
///
@ -112,7 +123,9 @@ class _StreamManager {
if (streamListeners[stream].contains(client)) {
throw kStreamAlreadySubscribedException;
}
streamListeners[stream].add(client);
if (client != null) {
streamListeners[stream].add(client);
}
}
/// Unsubscribes `client` from a stream.
@ -129,7 +142,8 @@ class _StreamManager {
throw kStreamNotSubscribedException;
}
listeners.remove(client);
if (listeners.isEmpty) {
// Don't cancel streams DDS needs to function.
if (listeners.isEmpty && !ddsCoreStreams.contains(stream)) {
streamListeners.remove(stream);
final result = await dds._vmServiceClient.sendRequest('streamCancel', {
'streamId': stream,
@ -165,6 +179,21 @@ class _StreamManager {
_RpcErrorCodes.kStreamNotSubscribed,
);
static const kDebugStream = 'Debug';
static const kIsolateStream = 'Isolate';
// Never cancel the Debug or Isolate stream as `_IsolateManager` requires
// them for isolate state notifications.
static const isolateManagerStreams = <String>{
kDebugStream,
kIsolateStream,
};
// The set of streams that DDS requires to function.
static final ddsCoreStreams = <String>{
...isolateManagerStreams,
};
final _DartDevelopmentService dds;
final streamListeners = <String, List<_DartDevelopmentServiceClient>>{};
}

View file

@ -66,6 +66,4 @@ var tests = <VMTest>[
main(args) async => runVMTests(
args,
tests,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -66,6 +66,4 @@ Future<void> main(args) => runIsolateTests(
testeeConcurrent: fooBar,
pause_on_start: true,
pause_on_exit: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -64,6 +64,4 @@ Future<void> main(args) => runIsolateTests(
testeeConcurrent: fooBar,
pause_on_start: true,
pause_on_exit: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -46,6 +46,4 @@ Future<void> main(args) => runIsolateTests(
testeeConcurrent: fooBar,
pause_on_start: true,
pause_on_exit: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -64,6 +64,4 @@ Future<void> main(args) => runIsolateTests(
testeeConcurrent: fooBar,
pause_on_start: true,
pause_on_exit: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -57,6 +57,4 @@ Future<void> main(args) => runIsolateTests(
testeeConcurrent: fooBar,
pause_on_start: true,
pause_on_exit: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -65,6 +65,4 @@ Future<void> main(args) => runIsolateTests(
hotReloadTest,
testeeConcurrent: fooBar,
pause_on_start: true,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -41,6 +41,4 @@ Future<void> main(args) => runIsolateTests(
args,
test,
testeeBefore: fooBar,
// TODO(bkonyi): client names are not yet supported in DDS.
enableDds: false,
);

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
var result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(3));
expect(result['minor'], equals(32));
expect(result['minor'], equals(33));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},

View file

@ -15,7 +15,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 3
#define SERVICE_PROTOCOL_MINOR_VERSION 32
#define SERVICE_PROTOCOL_MINOR_VERSION 33
class Array;
class EmbedderServiceHandler;

View file

@ -1,8 +1,8 @@
# Dart VM Service Protocol 3.32
# Dart VM Service Protocol 3.33
> Please post feedback to the [observatory-discuss group][discuss-list]
This document describes of _version 3.32_ of the Dart VM Service Protocol. This
This document describes of _version 3.33_ of the Dart VM Service Protocol. This
protocol is used to communicate with a running Dart Virtual Machine.
To use the Service Protocol, start the VM with the *--observe* flag.
@ -726,6 +726,9 @@ See [ClassList](#classlist).
### getClientName
_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
An equivalent can be found in the Dart Development Service (DDS) protocol._
```
ClientName getClientName()
```
@ -1201,6 +1204,9 @@ _Collected_ [Sentinel](#sentinel) is returned.
### requirePermissionToResume
_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
An equivalent can be found in the Dart Development Service (DDS) protocol._
```
Success requirePermissionToResume(bool onPauseStart [optional],
bool onPauseReload[optional],
@ -1234,7 +1240,6 @@ need to provide resume approval for this pause type have done so.
approval for the current pause event, the isolate will be resumed if at
least one other client has attempted to [resume](#resume) the isolate.
### resume
```
@ -1271,6 +1276,9 @@ See [Success](#success), [StepOption](#StepOption).
### setClientName
_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
An equivalent can be found in the Dart Development Service (DDS) protocol._
```
Success setClientName(string name)
```
@ -1416,7 +1424,7 @@ The _streamId_ parameter may have the following published values:
streamId | event types provided
-------- | -----------
VM | VMUpdate, VMFlagUpdate
Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, ServiceExtensionAdded
Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, IsolateSpawn, ServiceExtensionAdded
Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, Inspect, None
GC | GC
Extension | Extension
@ -1716,6 +1724,9 @@ class ClassList extends Response {
### ClientName
_**Note**: This class is deprecated and will be removed in v4.0 of the protocol.
An equivalent can be found in the Dart Development Service (DDS) protocol._
```
class ClientName extends Response {
// The name of the currently connected VM service client.
@ -3756,5 +3767,6 @@ version | comments
3.31 | Added single client mode, which allows for the Dart Development Service (DDS) to become the sole client of
the VM service.
3.32 | Added `getClassList` RPC and `ClassList` object.
3.33 | Added deprecation notice for `getClientName`, `setClientName`, `requireResumeApproval`, and `ClientName`. These RPCs are moving to the DDS protocol and will be removed in v4.0 of the VM service protocol.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss