New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum + Set is broken when used via an Isolate #35626

Open
SteveAlexander opened this Issue Jan 10, 2019 · 3 comments

Comments

Projects
None yet
5 participants
@SteveAlexander
Copy link

SteveAlexander commented Jan 10, 2019

$ dart --version
Dart VM version: 2.1.1-dev.0.1.flutter-2cb346bd0c (Tue Jan 8 15:52:54 2019 +0000) on "macos_x64"
import 'dart:isolate';

class Content {
  Content(partOfDay) : this.partOfDay = Set()..add(partOfDay);
  final Set<PartOfDay> partOfDay;
}

enum PartOfDay {
  morning,
  afternoon,
  evening
}

foo() async {
  final r = ReceivePort();
  await Isolate.spawn(bar, r.sendPort);
  final Content content = await r.first;

  Set<PartOfDay> pod = Set()..add(PartOfDay.afternoon);
  print('A: ${content.partOfDay}');
  print('B: $pod');
  print('C: ${pod.intersection(content.partOfDay)}');
  print('D: ${content.partOfDay.intersection(pod)}');
}

bar(SendPort sendport) {
  sendport.send(Content(PartOfDay.afternoon));
}

main() async {
  foo();
}

Output:

A: {PartOfDay.afternoon}
B: {PartOfDay.afternoon}
C: {}
D: {PartOfDay.afternoon}

Expected output:

A: {PartOfDay.afternoon}
B: {PartOfDay.afternoon}
C: {PartOfDay.afternoon}
D: {PartOfDay.afternoon}

This looks similar in scope to #23244

I originally encountered this problem in a Flutter project, and reported it as a Flutter bug: flutter/flutter#26013

@liamappelbe liamappelbe self-assigned this Jan 10, 2019

@liamappelbe

This comment has been minimized.

Copy link
Contributor

liamappelbe commented Jan 10, 2019

Interesting bug. Seems to be specific to Set<Enum> (Set<int> works fine).

As a workaround, duplicating the set immediately after you receive it seems to fix the issue:

  final r = ReceivePort();
  await Isolate.spawn(bar, r.sendPort);
  final Content content = await r.first;
  content.partOfDay = Set()..addAll(content.partOfDay);
@rmacnak-google

This comment has been minimized.

Copy link
Member

rmacnak-google commented Jan 10, 2019

Only instances of _InternalLinkedHashMap are rehashed when received through an isolate message. The hash code of enums is probably not stable across isolates.

@lrhn

This comment has been minimized.

Copy link
Member

lrhn commented Jan 15, 2019

Yes, our isolate communication is too low-level to understand the logical structure of objects, it just copies them bit-by-bit, and it also doesn't preserve the hash-code of objects (except some value-like types like int and String). That means that hash-based structures cannot be safely sent over a send-port. We have a hack for the platform hash maps to rehash when copying. We should probably extend it to hash sets. (And maybe come up with a better isolate communication strategy in the future).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment