What is an Isolate anyway?

by Slava Egorov (@mraleph)

What is an Isolate anyway?

by Slava Egorov (@mraleph)

What is an Isolate anyway?

by Slava Egorov (@mraleph)

πŸ‘‹

 

I decide how Dart evolves!

I direct creativity of the team

I decide how Dart evolves!

Concurrency in Dart

Dart is a single-threaded run-to-completion language.

Concurrency 101


                        ─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●──► 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

                    

Rules of Isolate Club

  1. You do not talk about Isolate Club
  2. Each thread can enter at most one isolate.
  3. At most one thread can enter specific isolate.
  4. Isolates can't interact directly.

                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚ 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) β”‚
                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    

«Isolate design inspired by Erlang»

— Lars Bak in "Pushing the Limits of Web Browsers" (2012)

«Really? πŸ€” »

— me, whenever I encounter that quote

                            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

JavaScript


                    const worker = new Worker("path/to/worker.js");

                    worker.addEventListener("message", (event) => {
                        // got message from worker: event.data
                        worker.postMessage(responseMessage);
                    });
                    

Experience implementing VMs

Lars & Kasper had bad experience with Java VM (HotSpot), good experience with OOVM (aka Resilient Smalltalk) and V8

Problems with Isolates

  1. Developers often don't get them.
  2. Issues with original design & implementation.

                    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
                    

Lightweight Isolates



                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚ Isolate A      β”‚          β”‚ Isolate B      β”‚
                        | β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚          | β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
                        β”‚ β”‚ GC Heap     β”‚β”‚          β”‚ β”‚ GC Heap     β”‚β”‚
                        | β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚β”‚          | β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚β”‚
                        β”‚ β”‚ β”‚Program   β”‚β”‚β”‚          β”‚ β”‚ β”‚Program   β”‚β”‚β”‚
                        β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚β”‚          β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚β”‚
                        β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚          β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

                    

                      β”Œβ”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”
                      ┆  Isolate Group                                  ┆
                      ┆  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” ┆
                      ┆  β”‚ Isolate A      β”‚          β”‚ Isolate B      β”‚ ┆
                      ┆  | β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚ ┆
                      ┆  β”‚ β”‚ GC Heap      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚β”‚ ┆
                      ┆  | β”‚              β”‚ Program β”‚                β”‚β”‚ ┆
                      ┆  β”‚ β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚β”‚ ┆
                      ┆  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚ ┆
                      ┆  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ┆
                      β””β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”„β”˜
                    

Isolate Groups

πŸ‘ 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

Sharing of objects

  • Compile time constants
  • Deeply immutable objects (e.g. 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􀇅
                    

How do Isolates actually run?

  • Isolates created by Isolate.spawn/spawnUri are running on a thread pool managed by Dart runtime
  • Isolates created by the embedder (e.g. Flutter) run where it wants them to run.

                      ─────────────────────────► 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).
                    

Synchronous interoperability chasm

  • Some APIs are bound to the platform thread.
  • Some APIs can call you back on arbitrary thread.
  • Isolates can't ignore existence of threads if we want better interop.

Crossing the chasm

  • Some APIs are bound to the platform thread.

    ⇒ Bind UI isolate to this thread as well (aka Issue #150525 Merge the platform and UI threads)

  • Some APIs can call you back on arbitrary thread.

    ⇒ πŸ€”


                      ─────────────────────────► 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
                    

Merged threads is a WIP

Though mostly done. Opt-outs will be removed this year.


                      ─────────────────────────► thread #1

                      ──────τ€”·β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί thread #2
                             􀍯 wants to call into Dart
                      ─────────────────────────► thread #3
                    

For async cases we have NativeCallable.listener.

(converts native invocation into a message to a target isolate)

We need an Isolate! But which?

  • πŸ€” Use specific isolate.

    ⇒ Need to wait and target isolate might be bound to specific thread, so can't just enter it

  • πŸ€” Use temporary ephemeral isolate

    ⇒ Everything works, but state resets between each call which is surprising.

🚨 experiments ahead 🚨

shared memory multithreading


                    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 "Β―\_(ツ)_/Β―";
                        }
                      }
                    }
                    

Multithreading is hard.

Ecosystem is not ready for it.


                    // 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 = [];
                    

Small steps...

shared native memory multithreading


                      ─────────────────────────► thread #1

                      ──────τ€”·β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί thread #2
                             􀍯 wants to call into Dart
                      ─────────────────────────► thread #3
                    

πŸ’‘ Don't use an isolate at all!

Allow thread to enter Isolate Group instead

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);
                        }));
                      }
                    }
                    

We are still figuring out details...

What about the Web?

  • Ironically dart:isolate support on the Web was removed in Dart 2.0
  • Not enough capabilities to fully replicate Isolate Groups on the Web.
  • We might be able to replicate shared native memory multithreading: JS has SABs
  • Real solution might arrive via WebAssembly's Shared-Everything Threads Proposal

Thank you!

(Find me if you think you know answers to my puzzles)