MessagePort not transferring via WebWorker postMessage().

Environment:

  • Dart VM version: 2.1.0 (Tue Nov 13 18:22:02 2018 +0100) on “macos_x64”
  • Version 73.0.3673.0 (Official Build) dev (64-bit)

Issue Or Not
Not black & white, as there is no documentation and a reading of the source also does not shed any light. Never the less, I have raised the issue on StackOverflow and have stalked a few folks on gitter. Unfortunately no luck, I can not accept that I am the first people to attempt to do this, hence it could just be a knowledge gap.

Area

  • dart:html
  • Worker, in particular postMessage(Object message, [ List<Object> transfer]) -> void
  • MessageChannel/MessagePort

Assumptions

  1. MessagePort is a cloneable transferable object. MDN
  2. MessagePort is the way to go when needing to communicating between two workers, independent of the thread which launched each of the workers.

Core Problem
After passing the messagePort to the work it arrives in as an instance of 'JsLinkedHashMap<dynamic, dynamic>' and there is no way to retrieve the MessagePort instance.

Sounds stupid right!

Reproduction
The simplest reproduction I have managed is the following.

  1. Inside a main.dart:
import 'dart:html';

import 'dart:collection';

void main() {
  querySelector('#output').text = 'Your Dart app is running.';

  Worker w = Worker('lib/my_worker.dart.js');

  MessageChannel msgChn = MessageChannel();

  HashMap trsMsg = HashMap.from({'port': msgChn.port1});
  w.postMessage(trsMsg, [msgChn.port1]);
}
  1. In our worker code.
import 'dart:html';

class MyWorker {
  DedicatedWorkerGlobalScope dws = DedicatedWorkerGlobalScope.instance;

  MyWorker() {
    print('Worker Running');

    dws.onMessage.listen((MessageEvent evt) {
      print(evt.data);
      print(evt.data.runtimeType);
      var strSec = evt.data['_strings'];
      print(strSec);
      MessagePort mp = strSec['port'];
    });
  }
}

main() {
  MyWorker();
}
  1. In the build.yaml.
targets:
  $default:
    builders:
      build_web_compilers|entrypoint:
        options:
          compiler: dart2js
          dart2js_args:
            - -O1

The result:

screen shot 2019-01-23 at 12 34 01 pm

I have tried a number of different approaches to this whole problem, including but not limited:

  1. Using a LinkedHashMap for the trsMsg.
  2. Building the trsMsg Hash from Iterables, that was a longshot.
  3. Sending port 2 instead of port 1,
  4. Inlining anything and everything I could.
  5. Cast and recast on the worker side.
  6. Changing every build options I could think of.
  7. Using the Js library to create an anonymous MessageEvent
  8. Not using the dart:html library at all. Instead using @JS() for everything from the worker to the port.

Clearly running out of ideas and getting down to long shots.

Expected Behaviour

  • Instead of getting an unusable [object MessagePort] showing up in the worker, simply being able to access an Instance of MessagePort from within the MessageEvent.data would be ideal. For that matter being to access the transferred MessagePort in everywhere in the worker would do the job.

Thanks in advance and I hope it is something thing simple I am doing or not; communicating between web workers without messagePort is a nightmare.

Author: Fantashit

2 thoughts on “MessagePort not transferring via WebWorker postMessage().

  1. Quick update: I can verify that the 3 issues above address the original bug:

    • need to update Worker.postMessage to ensure we do a structural clone (adding the clone manually fixes the issue too)
    • need to update serialization code to include MessagePort
    • need to list MessagePort as a possible deserialization type (this fixes the worker issue)

    I’ll prepare a fix tomorrow for the 2nd and 3rd issue.
    @terrylucas – could you help us fix the first issue on your end?

  2. I just landed 60d7e8f and 6bcb017 and things are working much better now.

    I can compile these 2 programs with no flags (no --omit-implicit-checks needed), and run them correctly:

    main.dart:

    import 'dart:html';
    
    void main() {
      Worker w = Worker('worker.dart.js');
      MessageChannel msgChn = MessageChannel();
      w.postMessage({'port': msgChn.port1}, [msgChn.port1]);
      msgChn.port2.onMessage.listen((m) => print("received message from worker: " + m.data));
    }

    worker.dart:

    import 'dart:html';
    
    class MyWorker {
      DedicatedWorkerGlobalScope dws = DedicatedWorkerGlobalScope.instance;
    
      MyWorker() {
        print('Worker Running');
    
        dws.onMessage.listen((MessageEvent evt) {
          print(evt.data);
          print(evt.data.runtimeType);
          MessagePort port = evt.data["port"];
          print(port);
          port.postMessage("port is working!");
        });
      }
    }
    
    main() {
      MyWorker();
    }

    The output I get is:

    Worker Running
    {port: Instance of 'MessagePort'}
    JsLinkedHashMap<dynamic, dynamic>
    Instance of 'MessagePort'
    received message from worker: port is working!
    

    The fixes are on the master branch at this time, and will be available in the next dev release.

Comments are closed.