mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:33:28 +00:00
[vm/regexp] Copy, rather than share RegExp objects between isolates.
RegExp code objects keep backtracking and registers stacks in object pools, can't be shared between isolates. BUG=https://github.com/dart-lang/sdk/issues/50082 TEST=one_regexp_many_workers Change-Id: Ic7db8d7a75a0951178b2f4800f96224d52506545 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273480 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
parent
6c9873bf42
commit
4275d595b6
|
@ -73,7 +73,6 @@ final sharableObjects = [
|
|||
const [1, 2, 3],
|
||||
const {1: 1, 2: 2, 3: 2},
|
||||
const {1, 2, 3},
|
||||
RegExp('a'),
|
||||
Isolate.current.pauseCapability,
|
||||
Int32x4(1, 2, 3, 4),
|
||||
StackTrace.current,
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
//
|
||||
// Verifies that many isolate workers can use one RegExp.
|
||||
|
||||
import 'dart:isolate';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
worker(List<dynamic> args) {
|
||||
final re = args[0] as RegExp;
|
||||
final i = args[1] as int;
|
||||
print('worker $i');
|
||||
final sendPort = args[2] as SendPort;
|
||||
final sw = Stopwatch()..start();
|
||||
while (sw.elapsedMilliseconds < 2000) {
|
||||
re.firstMatch('h' * i * 1000);
|
||||
}
|
||||
sendPort.send(true);
|
||||
}
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
|
||||
int nWorkers = 5;
|
||||
final r = RegExp(r'(?<=\W|\b|^)(a.? b c.?) ?(\(.*\))?$');
|
||||
final rps = List<ReceivePort>.generate(nWorkers, (_) => ReceivePort());
|
||||
|
||||
for (int i = 0; i < nWorkers; i++) {
|
||||
Isolate.spawn(worker, <dynamic>[r, i, rps[i].sendPort]);
|
||||
}
|
||||
|
||||
Future.wait(List<Future<dynamic>>.generate(nWorkers, (i) => rps[i].first))
|
||||
.whenComplete(() {
|
||||
rps.forEach((rp) => rp.close());
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
|
@ -74,7 +74,6 @@ final sharableObjects = [
|
|||
const [1, 2, 3],
|
||||
const {1: 1, 2: 2, 3: 2},
|
||||
const {1, 2, 3},
|
||||
RegExp('a'),
|
||||
Isolate.current.pauseCapability,
|
||||
Int32x4(1, 2, 3, 4),
|
||||
StackTrace.current,
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
//
|
||||
// @dart = 2.9
|
||||
//
|
||||
// Verifies that many isolate workers can use one RegExp.
|
||||
|
||||
import 'dart:isolate';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
worker(List<dynamic> args) {
|
||||
final re = args[0] as RegExp;
|
||||
final i = args[1] as int;
|
||||
print('worker $i');
|
||||
final sendPort = args[2] as SendPort;
|
||||
final sw = Stopwatch()..start();
|
||||
while (sw.elapsedMilliseconds < 2000) {
|
||||
re.firstMatch('h' * i * 1000);
|
||||
}
|
||||
sendPort.send(true);
|
||||
}
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
|
||||
int nWorkers = 5;
|
||||
final r = RegExp(r'(?<=\W|\b|^)(a.? b c.?) ?(\(.*\))?$');
|
||||
final rps = List<ReceivePort>.generate(nWorkers, (_) => ReceivePort());
|
||||
|
||||
for (int i = 0; i < nWorkers; i++) {
|
||||
Isolate.spawn(worker, <dynamic>[r, i, rps[i].sendPort]);
|
||||
}
|
||||
|
||||
Future.wait(List<Future<dynamic>>.generate(nWorkers, (i) => rps[i].first))
|
||||
.whenComplete(() {
|
||||
rps.forEach((rp) => rp.close());
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include "vm/longjump.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/regexp.h"
|
||||
#include "vm/snapshot.h"
|
||||
#include "vm/symbols.h"
|
||||
#include "vm/timeline.h"
|
||||
|
@ -68,7 +69,6 @@
|
|||
V(Pointer) \
|
||||
V(ReceivePort) \
|
||||
V(RecordType) \
|
||||
V(RegExp) \
|
||||
V(Script) \
|
||||
V(Sentinel) \
|
||||
V(SendPort) \
|
||||
|
@ -170,7 +170,13 @@ static bool CanShareObject(ObjectPtr obj, uword tags) {
|
|||
if (cid == kInt32x4Cid) return true; // No field guards here.
|
||||
if (cid == kSendPortCid) return true;
|
||||
if (cid == kCapabilityCid) return true;
|
||||
|
||||
// Generated code for regexp can't be shared.
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (cid == kRegExpCid) return true;
|
||||
#else
|
||||
if (FLAG_interpret_irregexp && cid == kRegExpCid) return true;
|
||||
#endif
|
||||
|
||||
if (cid == kClosureCid) {
|
||||
// We can share a closure iff it doesn't close over any state.
|
||||
|
@ -214,7 +220,11 @@ static bool MightNeedReHashing(ObjectPtr object) {
|
|||
// a map or a value in a set, they will already have the identity hash code
|
||||
// set.
|
||||
if (cid == kImmutableArrayCid) return false;
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (cid == kRegExpCid) return false;
|
||||
#else
|
||||
if (FLAG_interpret_irregexp && cid == kRegExpCid) return false;
|
||||
#endif
|
||||
if (cid == kInt32x4Cid) return false;
|
||||
|
||||
// We copy those (instead of sharing them) - see [CanShareObjct]. They rely
|
||||
|
@ -661,6 +671,7 @@ class FastForwardMap : public ForwardMapBase {
|
|||
void AddExternalTypedData(ExternalTypedDataPtr to) {
|
||||
raw_external_typed_data_to_.Add(to);
|
||||
}
|
||||
void AddRegExp(RegExpPtr to) { raw_reg_exp_to_.Add(to); }
|
||||
|
||||
void AddObjectToRehash(ObjectPtr to) { raw_objects_to_rehash_.Add(to); }
|
||||
void AddExpandoToRehash(ObjectPtr to) { raw_expandos_to_rehash_.Add(to); }
|
||||
|
@ -672,6 +683,7 @@ class FastForwardMap : public ForwardMapBase {
|
|||
IdentityMap* map_;
|
||||
GrowableArray<ObjectPtr> raw_from_to_;
|
||||
GrowableArray<TransferableTypedDataPtr> raw_transferables_from_to_;
|
||||
GrowableArray<RegExpPtr> raw_reg_exp_to_;
|
||||
GrowableArray<ExternalTypedDataPtr> raw_external_typed_data_to_;
|
||||
GrowableArray<ObjectPtr> raw_objects_to_rehash_;
|
||||
GrowableArray<ObjectPtr> raw_expandos_to_rehash_;
|
||||
|
@ -714,6 +726,7 @@ class SlowForwardMap : public ForwardMapBase {
|
|||
transferables_from_to_.Add(&TransferableTypedData::Handle(from.ptr()));
|
||||
transferables_from_to_.Add(&TransferableTypedData::Handle(to.ptr()));
|
||||
}
|
||||
void AddRegExp(const RegExp& to) { reg_exps_.Add(&RegExp::Handle(to.ptr())); }
|
||||
void AddWeakProperty(const WeakProperty& from) {
|
||||
weak_properties_.Add(&WeakProperty::Handle(from.ptr()));
|
||||
}
|
||||
|
@ -740,6 +753,29 @@ class SlowForwardMap : public ForwardMapBase {
|
|||
}
|
||||
}
|
||||
|
||||
void FinalizeRegExps() {
|
||||
if (FLAG_interpret_irregexp) {
|
||||
return;
|
||||
}
|
||||
if (reg_exps_.length() == 0) {
|
||||
return;
|
||||
}
|
||||
const Library& lib = Library::Handle(zone_, Library::CoreLibrary());
|
||||
const Class& owner =
|
||||
Class::Handle(zone_, lib.LookupClass(Symbols::RegExp()));
|
||||
|
||||
for (intptr_t i = 0; i < reg_exps_.length(); i++) {
|
||||
auto regexp = reg_exps_[i];
|
||||
for (intptr_t cid = kOneByteStringCid; cid <= kExternalTwoByteStringCid;
|
||||
cid++) {
|
||||
CreateSpecializedFunction(thread_, zone_, *regexp, cid,
|
||||
/*sticky=*/false, owner);
|
||||
CreateSpecializedFunction(thread_, zone_, *regexp, cid,
|
||||
/*sticky=*/true, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeExternalTypedData() {
|
||||
for (intptr_t i = 0; i < external_typed_data_.length(); i++) {
|
||||
auto to = external_typed_data_[i];
|
||||
|
@ -756,6 +792,7 @@ class SlowForwardMap : public ForwardMapBase {
|
|||
GrowableArray<const PassiveObject*> from_to_transition_;
|
||||
GrowableObjectArray& from_to_;
|
||||
GrowableArray<const TransferableTypedData*> transferables_from_to_;
|
||||
GrowableArray<const RegExp*> reg_exps_;
|
||||
GrowableArray<const ExternalTypedData*> external_typed_data_;
|
||||
GrowableArray<const Object*> objects_to_rehash_;
|
||||
GrowableArray<const Object*> expandos_to_rehash_;
|
||||
|
@ -1030,6 +1067,7 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
|||
TransferableTypedDataPtr to) {
|
||||
fast_forward_map_.AddTransferable(from, to);
|
||||
}
|
||||
void EnqueueRegExp(RegExpPtr to) { fast_forward_map_.AddRegExp(to); }
|
||||
void EnqueueWeakProperty(WeakPropertyPtr from) {
|
||||
fast_forward_map_.AddWeakProperty(from);
|
||||
}
|
||||
|
@ -1235,6 +1273,7 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
|||
const TransferableTypedData& to) {
|
||||
slow_forward_map_.AddTransferable(from, to);
|
||||
}
|
||||
void EnqueueRegExp(const RegExp& to) { slow_forward_map_.AddRegExp(to); }
|
||||
void EnqueueWeakProperty(const WeakProperty& from) {
|
||||
slow_forward_map_.AddWeakProperty(from);
|
||||
}
|
||||
|
@ -1447,6 +1486,49 @@ class ObjectCopy : public Base {
|
|||
Record::field_offset(0) + Record::kBytesPerElement * num_fields);
|
||||
}
|
||||
|
||||
void CopyRegExp(typename Types::RegExp from, typename Types::RegExp to) {
|
||||
Base::StoreCompressedPointers(from, to,
|
||||
OFFSET_OF(UntaggedRegExp, capture_name_map_),
|
||||
OFFSET_OF(UntaggedRegExp, pattern_));
|
||||
|
||||
UntagRegExp(to)->num_bracket_expressions_ =
|
||||
UntagRegExp(from)->num_bracket_expressions_;
|
||||
UntagRegExp(to)->num_one_byte_registers_ =
|
||||
UntagRegExp(from)->num_one_byte_registers_;
|
||||
UntagRegExp(to)->num_two_byte_registers_ =
|
||||
UntagRegExp(from)->num_two_byte_registers_;
|
||||
UntagRegExp(to)->type_flags_ = UntagRegExp(from)->type_flags_;
|
||||
Base::StoreCompressedPointerNoBarrier(Types::GetRegExpPtr(to),
|
||||
OFFSET_OF(UntaggedRegExp, one_byte_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(Types::GetRegExpPtr(to),
|
||||
OFFSET_OF(UntaggedRegExp, one_byte_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(Types::GetRegExpPtr(to),
|
||||
OFFSET_OF(UntaggedRegExp, two_byte_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to), OFFSET_OF(UntaggedRegExp, external_one_byte_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to), OFFSET_OF(UntaggedRegExp, external_two_byte_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to), OFFSET_OF(UntaggedRegExp, one_byte_sticky_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to), OFFSET_OF(UntaggedRegExp, two_byte_sticky_),
|
||||
Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to),
|
||||
OFFSET_OF(UntaggedRegExp, external_one_byte_sticky_), Object::null());
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetRegExpPtr(to),
|
||||
OFFSET_OF(UntaggedRegExp, external_two_byte_sticky_), Object::null());
|
||||
|
||||
Base::EnqueueRegExp(to);
|
||||
}
|
||||
|
||||
template <intptr_t one_for_set_two_for_map, typename T>
|
||||
void CopyLinkedHashBase(T from,
|
||||
T to,
|
||||
|
@ -2066,6 +2148,7 @@ class ObjectGraphCopier : public StackResource {
|
|||
// The copy was successful, then detach transferable data from the sender
|
||||
// and attach to the copied graph.
|
||||
slow_object_copy_.slow_forward_map_.FinalizeTransferables();
|
||||
slow_object_copy_.slow_forward_map_.FinalizeRegExps();
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
|
@ -2111,6 +2194,7 @@ class ObjectGraphCopier : public StackResource {
|
|||
result_array.SetAt(2, fast_object_copy_.tmp_);
|
||||
HandlifyExternalTypedData();
|
||||
HandlifyTransferables();
|
||||
HandlifyRegExp();
|
||||
allocated_bytes_ =
|
||||
fast_object_copy_.fast_forward_map_.allocated_bytes;
|
||||
copied_objects_ =
|
||||
|
@ -2172,6 +2256,7 @@ class ObjectGraphCopier : public StackResource {
|
|||
HandlifyWeakProperties();
|
||||
HandlifyWeakReferences();
|
||||
HandlifyExternalTypedData();
|
||||
HandlifyRegExp();
|
||||
HandlifyObjectsToReHash();
|
||||
HandlifyExpandosToReHash();
|
||||
HandlifyFromToObjects();
|
||||
|
@ -2218,6 +2303,10 @@ class ObjectGraphCopier : public StackResource {
|
|||
Handlify(&fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_,
|
||||
&slow_object_copy_.slow_forward_map_.external_typed_data_);
|
||||
}
|
||||
void HandlifyRegExp() {
|
||||
Handlify(&fast_object_copy_.fast_forward_map_.raw_reg_exp_to_,
|
||||
&slow_object_copy_.slow_forward_map_.reg_exps_);
|
||||
}
|
||||
void HandlifyObjectsToReHash() {
|
||||
Handlify(&fast_object_copy_.fast_forward_map_.raw_objects_to_rehash_,
|
||||
&slow_object_copy_.slow_forward_map_.objects_to_rehash_);
|
||||
|
|
Loading…
Reference in a new issue