Isolate anyway?@mraleph)
Isolate anyway?@mraleph)
Isolate anyway?@mraleph)
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
βΆ β M e m o r y β
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
β β M e m o r y β
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
βΆ β M e m o r y β
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
β β M e m o r y β
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
βΆ β M e m o r y β
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββΊ CPU
βββββββββββββββββββββββββββ
β β M e m o r y β
βββββββββββββββββββββββββββ
ββββββββββββββββββΊ Thread 1 βββββββββββββββββββββββββββ
ββββββββββββββββΊ Thread 2 β M e m o r y β
ββββββββββββββΊ Thread 3 β β
βββββββββββΊ Thread 4 βββββββββββββββββββββββββββ
β{Thread 1}{Thread 2}{Thread 3}{Thread 2}ββΊ CPU CORE #0
β{Thread 2}{Thread 3}{Thread 1}{Thread 1}ββΊ CPU CORE #1
βββββββββββββββββββββ{Thread 2}ββββββββββββΊ CPU CORE #2
β{Thread 3}ββββββββββββββββββββ{Thread 4}ββΊ CPU CORE #3
β{T1}{T2}{T3}{T2}ββΊ CORE0 βββββββββββββββββββββββββββ
β{T2}{T3}{T1}{T1}ββΊ CORE1 β M e m o r y β
βββββββββ{T2}ββββββΊ CORE2 β β
β{T3}ββββββββ{T4}ββΊ CORE3 βββββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ
β Isolate A β
| ββββββββ ββββββββββ β
β β HEAP β β cnt: 1 β β
β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ
β Isolate A β
| ββββββββ ββββββββββ β
β β HEAP β β cnt: 1 β β
β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ
β Isolate A β
| ββββββββ ββββββββββ β
β β HEAP β β cnt: 2 β β
β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
β Isolate A β β Isolate A β
| ββββββββ ββββββββββ β | ββββββββ ββββββββββ β
β β HEAP β β cnt: 3 β β β β HEAP β β cnt: 0 β β
β ββββββββ ββββββββββ β β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
β Isolate A β β Isolate A β
| ββββββββ ββββββββββ β | ββββββββ ββββββββββ β
β β HEAP β β cnt: 4 β β β β HEAP β β cnt: 1 β β
β ββββββββ ββββββββββ β β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
var cnt = 0;
void foo() {
while (cnt < 100) {
print('${cnt++}');
sleep(const Duration(seconds: 1));
}
}
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
β Isolate A β β Isolate A β
| ββββββββ ββββββββββ β | ββββββββ ββββββββββ β
β β HEAP β β cnt: 5 β β β β HEAP β β cnt: 2 β β
β ββββββββ ββββββββββ β β ββββββββ ββββββββββ β
βββββββββββββββββββββββββ βββββββββββββββββββββββββ
βββββββββββββ βββββββββββββ
β Isolate A β β Isolate B β
βββββββββββββ βββββββββββββ
τββββββββββββfooβββββββββββτ
βββββββββββββββββββββββββββββββββββββββββββββΊ Thread 1
τ― enter A τ― exit A
τββββββββββββfooβββββββββββτ
ββββββββββββββββββββββββββββββββββββββββββββββΊ Thread 2
τ― enter B τ― exit B
βββββββββββββββββββββββββ
β Isolate A β
| ββββββββββββ΄βββ τ₯Message to Port X
β Ports ββββ Message Box ββτ‘β΄Message to Port Y
β ββββββββββββ¬βββ τ£Message to Port Z
βββββββββββββββββββββββββ
τββββββHandle Messagesββββτ
ββββββββββββββββββββββββββββββββββββββββββββΊ Thread
τ― enter A τ― exit A
βββββββββββββββββτ± βββββββββββββ
β Isolate A ββββτ± τ―ββββΊβ Isolate B β
βββββββββββββ τ―ββββββββββββββββββ
β΄βΎ messages β΄βΎ
βββββββββββββββββββββββββββββββββ
β Native World (e.g. async I/O) β
βββββββββββββββββββββββββββββββββ
2010 2011 2012 2013
βββββτβββββββτ’βββββββτβββββββτ’ββββββββββββββββββββββββΊ
Dart β² 0.x β² 1.0
τ― I join V8 τ― I join Dart VM
design of Dart's core was complete by the time I joined the team
const worker = new Worker("path/to/worker.js");
worker.addEventListener("message", (event) => {
// got message from worker: event.data
worker.postMessage(responseMessage);
});
Lars & Kasper had bad experience with Java VM (HotSpot), good experience with OOVM (aka Resilient Smalltalk) and V8
final HttpClient httpClient = ...;
Future<Map<String, dynamic>> loadSomeData(Uri uri) async {
return jsonDecode(await httpClient.get(uri));
}
final HttpClient httpClient = ...;
Future<Map<String, dynamic>> loadSomeData(Uri uri) async {
return jsonDecode(await httpClient.get(uri));
}
final HttpClient httpClient = ...;
Future<Map<String, dynamic>> loadSomeData(Uri uri) async {
return Isolate.run(() =>
jsonDecode(await httpClient.get(uri)));
}
final HttpClient httpClient = ...;
Future<...> loadSomeData1(Uri uri) async {
return Isolate.run(() =>
jsonDecode(await httpClient.get(uri)));
}
// PUZZLE: what is the difference?
Future<...> loadSomeData2(Uri uri) async {
final data = await httpClient.get(uri);
return Isolate.run(() => jsonDecode(data));
}
final HttpClient httpClient = ...;
Future<...> loadSomeData1(Uri uri) async {
return Isolate.run(() =>
jsonDecode(await httpClient.get(uri)));
}
// PUZZLE: what is the difference?
class Foo {
final HttpClient httpClient = ...;
Future<...> loadSomeData2(Uri uri) async {
return Isolate.run(() =>
jsonDecode(await httpClient.get(uri)));
}
}
final HttpClient httpClient = ...;
Future<Map<String, dynamic>> loadSomeData(Uri uri) async {
return Isolate.run(() =>
jsonDecode(await httpClient.get(uri)));
}
ββββββββββββββββββ ββββββββββββββββββ
β UI Isolate β β Temp Isolate β
| β | β
β β ββ{Map}β β β
| β | β
β β β β
β β β β
β β β β
ββββββββββββββββββ ββββββββββββββββββ
ββββββββββββββββββ ββββββββββββββββββ
β UI Isolate β β Temp Isolate β
| ββββββββββββββββ | ββββββββββββββββ
β β GC Heap ββ ββ{Map}β β β GC Heap ββ
| β ββ | β ββ
β β ββ β β ββ
β β ββ β β ββ
β ββββββββββββββββ β ββββββββββββββββ
ββββββββββββββββββ ββββββββββββββββββ
ββββββββββββββββββ ββββββββββββββββββ
β UI Isolate β β Temp Isolate β
| ββββββββββββββββ | ββββββββββββββββ
β β GC Heap ββ ββ{Map}β β β GC Heap ββ
| β ββββββββββββββ | β ββββββββββββββ
β β βProgram βββ β β βProgram βββ
β β ββββββββββββββ β β ββββββββββββββ
β ββββββββββββββββ β ββββββββββββββββ
ββββββββββββββββββ ββββββββββββββββββ
τunpack msgτ
βββτ·ββββββββββββββββββββββββββββββββββββββββββββββΊ
β² β²
[spawn temp isolate] τ΅
τ³ {Map}
βΌ β±
βββββββββββββββββββββββββτΉββΊ
τspawningτ
τβββrunningββτ
τpack msgτ
TotalTime = Spawn + Init + Run + Pack + Unpack
ββββββββββββββββββ ββββββββββββββββββ
β Isolate A β β Isolate B β
| ββββββββββββββββ | ββββββββββββββββ
β β GC Heap ββ β β GC Heap ββ
| β ββββββββββββββ | β ββββββββββββββ
β β βProgram βββ β β βProgram βββ
β β ββββββββββββββ β β ββββββββββββββ
β ββββββββββββββββ β ββββββββββββββββ
ββββββββββββββββββ ββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Isolate Group β
β ββββββββββββββββββ ββββββββββββββββββ β
β β Isolate A β β Isolate B β β
β | ββββββββββββββββββββββββββββββββββββββββββββ β
β β β GC Heap βββββββββββ ββ β
β | β β Program β ββ β
β β β βββββββββββ ββ β
β β ββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββ ββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
π Make isolates smaller and faster to start
π Allow isolates to share some objects directly
π Sending can implemented as cloning rather than serialization/deserialization.
π Sending can avoid copying altogether is sender exits (Isolate.exit API)
π€ GCs are no longer independent
π€ There is limit on number of concurrently "running" mutators
String or objects marked with @pragma('vm:deeply-immutable'))
τunpack msgτ
βββτ·ββββββββββββββββββββββββββββββββββββββββββββββΊ
β² β²
[spawn temp isolate] τ΅
τ³ {Map}
βΌ β±
βββββββββββββββββββββββββτΉββΊ
τspawningτ
τβββrunningββτ
τpack msgτ
βββτ·ββββββββββββββββββββββββββββββββββββββββββββββΊ
β² β²
[spawn temp isolate] τ΅
τ³ {Map} [passed directly]
βΌ β±
βββββββββββββββββτΉββΊ
ττ
τβββrunningββτ
τexitτ
Isolate.spawn/spawnUri are running on a thread pool managed by Dart runtime
ββββββββββββββββββββββββββΊ flutter ui thread #1
τUI isolate #1 runsτ
ββββββββββββββββββββββΊ flutter ui thread #2
τUI isolate #2 runsτ
ββββββββββββββββββββββββββββΊ platform (ui) thread
τ― Many OS APIs are "bound" to this thread.
τ― That's where platform channels handle messages (by default).
⇒ Bind UI isolate to this thread as well (aka Issue #150525 Merge the platform and UI threads)
⇒ π€
ββββββββββββββββββββββββββΊ flutter ui thread #1
τUI isolate #1 runsτ
ββββββββββββββββββββββΊ flutter ui thread #2
τUI isolate #2 runsτ
ββββββββββββββββββββββββββββΊ platform (ui) thread
τ― handles platform messages
τ² runs all ui isolates
ββββββββββββββββββββββββββββΊ platform (ui) thread
τ― handles platform messages
ββββββββββββββββββββββββββΊ thread #1
ββββββτ·βββββββββββββββββββΊ thread #2
τ― wants to call into Dart
ββββββββββββββββββββββββββΊ thread #3
NativeCallable.listener.Isolate! But which?⇒ Need to wait and target isolate might be bound to specific thread, so can't just enter it
⇒ Everything works, but state resets between each call which is surprising.
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup);
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup);
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup); // puzzle: implement this
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup);
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup);
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
class Cup { Drink? drink; }
class Drink { final int volume; Drink(this.volume); }
void test() {
final cup = Cup();
final sharedCup = share(cup);
Isolate.run(() => barista(sharedCup.value));
drinker(cup);
}
void barista(Cup cup) {
while (true) {
cup.drink = Drink(42);
// Spin until it is taken.
while (cup.drink != null)
microNap();
}
}
void drinker(Cup cup) {
while (true) {
// Spin until we have a drink
while (cup.drink == null)
microNap();
final drink = cup.drink!;
cup.drink = null;
if (drink.volume != 42) {
throw "Β―\_(γ)_/Β―";
}
}
}
// Something as innocent looking as [].toString():
class Iterable<E> {
static String iterableToFullString(...) {
if (isToStringVisiting(iterable)) {
return /* ... */;
}
toStringVisiting.add(iterable);
try {
// ...
} finally {
toStringVisiting.removeLast();
}
}
}
/// A collection used to identify cyclic lists during `toString` calls.
final List<Object> toStringVisiting = [];
ββββββββββββββββββββββββββΊ thread #1
ββββββτ·βββββββββββββββββββΊ thread #2
τ― wants to call into Dart
ββββββββββββββββββββββββββΊ thread #3
NativeCallable.isolateGroupBound(callback)callback executes almost like any other Dart code - but when you try to access global static state you get an exception.
@pragma('vm:shared')Allow static state to be shared across all isolates in a group - if it does not circumvent isolation boundaries (= no mutable Dart state yet).
@pragma('vm:shared') Pointer<SomeStruct> data;
void doSomething() {
// Spawn 10 native threads all working on the same
// data.
for (var i = 0; i < 10; i++) {
pthread_create(NativeCallable.isolateGroupBound((_) {
doSomethingWith(data);
}));
}
}
dart:isolate support on the Web was removed in Dart 2.0(Find me if you think you know answers to my puzzles)