Code Monkey home page Code Monkey logo

Comments (38)

SaadArdati avatar SaadArdati commented on August 27, 2024

Also getting this at the end of the stack trace, now that I added the new Squadron logger:

image

from squadron.

d-markey avatar d-markey commented on August 27, 2024

So the code throws on:

final Set<Map<String, dynamic>> nodeReferences = Set<Map<String, dynamic>>.from(dynNodeReferences);

It cannot work because per Set<E>.from documentation, dynNodeReferences must be an iterable "where all elements should be instances of E", so instances of Map<String, dynamic> in your case.

But statically speaking dynNodeReferences is now just a Set = Set<dynamic> even if you know that items in the set are Map<String, dynamic> since they come from your main app.

So you have to rebuild proper type for the map entries first (because your keys are String), and then rebuid the Set. Something like:

final nodeReferences = Set<Map<String, dynamic>>.from(dynNodeReferences.map(
      (refs) => refs.map(
            (key, value) => MapEntry<String, dynamic>(key, value))));

Unfortunately there's not much I can do about this :-( Types in Isolates may be preserved (because Squadron relies on Isolate.spawn() in which case objects are copied and -- I suppose -- types are preserved) but types in Web Workers (JavaScript) are mostly lost or somehow "diminished" during communication and a simple cast is not enough to restore strong types.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

I would have never had guessed I needed to be this verbose with my types but it makes total sense!
Unfortunately, it's still throwing an error :(

image

image

WorkerException: TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
    at Object.wrapException (http://localhost:5000/solver_worker.dart.js:762:17)
    at Object._failedAsCheck (http://localhost:5000/solver_worker.dart.js:1931:15)
    at Rti._generalAsCheckImplementation [as _as] (http://localhost:5000/solver_worker.dart.js:1920:9)
    at Object.LinkedHashSet_LinkedHashSet$from (http://localhost:5000/solver_worker.dart.js:3837:28)
    at PositionManagerThread.performLayout$6 (http://localhost:5000/solver_worker.dart.js:16899:28)

Same error.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Tried to replace <String, dynamic> with <dynamic, dynamic> and moved the other Set.from functions before to make sure those are at least passing.

No dice.
image

image

WorkerException: TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
    at Object.wrapException (http://localhost:5000/solver_worker.dart.js:762:17)
    at Object._failedAsCheck (http://localhost:5000/solver_worker.dart.js:1931:15)
    at Rti._generalAsCheckImplementation [as _as] (http://localhost:5000/solver_worker.dart.js:1920:9)
    at Object._$CanvasNodeFromJson (http://localhost:5000/solver_worker.dart.js:6204:37)
    at NodeJsonConverter.fromJson$1 (http://localhost:5000/solver_worker.dart.js:18384:20)
    at PositionManagerThread.performLayout$6 (http://localhost:5000/solver_worker.dart.js:16911:19)
    at http://localhost:5000/solver_worker.dart.js:16678:93
    at _wrapJsFunctionForAsync_closure.$protected (http://localhost:5000/solver_worker.dart.js:3111:15)
    at _wrapJsFunctionForAsync_closure.call$2 (http://localhost:5000/solver_worker.dart.js:10955:12)
    at Object._asyncStartSync (http://localhost:5000/solver_worker.dart.js:3075:20)
solver_worker.dart.js 762:17                                                  wrapException
solver_worker.dart.js 1931:15                                                 _failedAsCheck
solver_worker.dart.js 1920:9                                                  _generalAsCheckImplementation
solver_worker.dart.js 6204:37                                                 _$CanvasNodeFromJson
solver_worker.dart.js 18384:20                                                fromJson$1
solver_worker.dart.js 16911:19                                                performLayout$6
solver_worker.dart.js 16678:93                                                <fn>
solver_worker.dart.js 3111:15                                                 $protected
solver_worker.dart.js 10955:12                                                call$2
solver_worker.dart.js 3075:20                                                 _asyncStartSync
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49  throw_
packages/squadron/src/worker.dart 134:7                                       send
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 60:31            <fn>
dart-sdk/lib/async/zone.dart 1450:47                                          _rootRunBinary
dart-sdk/lib/async/zone.dart 1342:19                                          runBinary
dart-sdk/lib/async/future_impl.dart 174:22                                    handleError
dart-sdk/lib/async/future_impl.dart 778:46                                    handleError
dart-sdk/lib/async/future_impl.dart 799:13                                    _propagateToListeners
dart-sdk/lib/async/future_impl.dart 609:5                                     [_completeError]
dart-sdk/lib/async/future_impl.dart 665:7                                     <fn>
dart-sdk/lib/async/zone.dart 1426:13                                          _rootRun
dart-sdk/lib/async/zone.dart 1328:19                                          run
dart-sdk/lib/async/zone.dart 1236:7                                           runGuarded
dart-sdk/lib/async/zone.dart 1276:23                                          callback
dart-sdk/lib/async/schedule_microtask.dart 40:11                              _microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 49:5                               _startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15           <fn>

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Even a simple

    final nodeReferences = Set.from(dynNodeReferences
        .map((refs) => refs.map((key, value) => MapEntry(key, value))));

is failing. We don't need types on maps, so It's easy to remove in dart, but it's the same error regardless!

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

One clever thing we can do is pass a json String instead of a Set object... I will give that a shot if you don't think this can be resolved any other way.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024
    final Set<Map<String, dynamic>> nodeReferences = {};

    dynNodeReferences.forEach((mapElement) {
      Map map = mapElement as Map;
      nodeReferences.add(map as Map<String, dynamic>);
    });

This also failed with the same error.

  A.PositionManagerThread_performLayout_closure.prototype = {
    call$1(mapElement) {
      this.nodeReferences.add$1(0, type$.Map_String_dynamic._as(type$.Map_dynamic_dynamic._as(mapElement)));
    },
    $signature: 11
  };

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

This one is interesting:

Different error

    final Set<Map<String, dynamic>> nodeReferences = {};

    dynNodeReferences.forEach((mapElement) {
      final Map<String, dynamic> castedMap = {};
      for (final entry in mapElement.entries) {
        castedMap[entry.key] = entry.value;
      }
    });

a null check on the key does not fix it.

        if (entry.key == null) continue;
WorkerException: NoSuchMethodError: method not found: 'toString' on null
TypeError: Cannot read properties of null (reading 'toString')
    at PositionManagerThread_performLayout_closure2.call$1 (http://localhost:5000/solver_worker.dart.js:16998:10)
    at MappedIterator.moveNext$0 (http://localhost:5000/solver_worker.dart.js:9914:48)
    at _LinkedHashSet.addAll$1 (http://localhost:5000/solver_worker.dart.js:12535:99)
    at PositionManagerThread.performLayout$6 (http://localhost:5000/solver_worker.dart.js:16524:13)
    at http://localhost:5000/solver_worker.dart.js:16234:93
    at _wrapJsFunctionForAsync_closure.$protected (http://localhost:5000/solver_worker.dart.js:3111:15)
    at _wrapJsFunctionForAsync_closure.call$2 (http://localhost:5000/solver_worker.dart.js:10888:12)
    at Object._asyncStartSync (http://localhost:5000/solver_worker.dart.js:3075:20)
    at SolverServiceImpl_operations_closure.$call$body$SolverServiceImpl_operations_closure0 (http://localhost:5000/solver_worker.dart.js:16246:16)
    at SolverServiceImpl_operations_closure.call$1 (http://localhost:5000/solver_worker.dart.js:16204:19)
solver_worker.dart.js 16998:10                                                call$1
solver_worker.dart.js 9914:48                                                 moveNext$0
solver_worker.dart.js 12535:99                                                addAll$1
solver_worker.dart.js 16524:13                                                performLayout$6
solver_worker.dart.js 16234:93                                                <fn>
solver_worker.dart.js 3111:15                                                 $protected
solver_worker.dart.js 10888:12                                                call$2
solver_worker.dart.js 3075:20                                                 _asyncStartSync
solver_worker.dart.js 16246:16                                                $call$body$SolverServiceImpl_operations_closure0
solver_worker.dart.js 16204:19                                                call$1
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49  throw_
packages/squadron/src/worker.dart 134:7                                       send
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 60:31            <fn>
dart-sdk/lib/async/zone.dart 1450:47                                          _rootRunBinary
dart-sdk/lib/async/zone.dart 1342:19                                          runBinary
dart-sdk/lib/async/future_impl.dart 174:22                                    handleError
dart-sdk/lib/async/future_impl.dart 778:46                                    handleError
dart-sdk/lib/async/future_impl.dart 799:13                                    _propagateToListeners
dart-sdk/lib/async/future_impl.dart 609:5                                     [_completeError]
dart-sdk/lib/async/future_impl.dart 665:7                                     <fn>
dart-sdk/lib/async/zone.dart 1426:13                                          _rootRun
dart-sdk/lib/async/zone.dart 1328:19                                          run
dart-sdk/lib/async/zone.dart 1236:7                                           runGuarded
dart-sdk/lib/async/zone.dart 1276:23                                          callback
dart-sdk/lib/async/schedule_microtask.dart 40:11                              _microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 49:5                               _startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15           <fn>

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Hello Saad I've been working on a Flutter sample heavily based on your code last night, I'll try to finish that tonight and post it so you can try. I think you're looking at the wrong place and the problem is not in that code, but it's difficult to locate it.

Eg can you try this:

    final Set<Map<String, dynamic>> nodeReferences = {};

    dynNodeReferences.forEach((mapElement) {
      final Map<String, dynamic> castedMap = {};
      for (final entry in mapElement.entries) {
        castedMap['constant'] = entry.value;
      }
    });

I believe it will throw anyway! So the exception is probably not coming from that piece of code.

from squadron.

d-markey avatar d-markey commented on August 27, 2024

BTW I suppose this is addAll(), not forEach()? Maybe it's missing a return castedMap;?

Or rather, it is forEach() and you're missing a nodesReferences.add(castedMap)?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

You're right. trying.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024
ChromeProxyService: Failed to evaluate expression 'nodeReferences'. 
worker start
worker started. result: {Instance of 'ModelFingerPrint'}
WorkerException (workerId=738884832, command=1): NoSuchMethodError: method not found: 'BaseNodeMixin___BaseNodeMixin_id' on null
TypeError: Cannot read properties of null (reading 'BaseNodeMixin___BaseNodeMixin_id')
    at PositionManagerThread.performLayout$6 (http://localhost:5000/solver_worker.dart.js:16443:63)
    at http://localhost:5000/solver_worker.dart.js:16210:93
    at _wrapJsFunctionForAsync_closure.$protected (http://localhost:5000/solver_worker.dart.js:3111:15)
    at _wrapJsFunctionForAsync_closure.call$2 (http://localhost:5000/solver_worker.dart.js:10878:12)
    at Object._asyncStartSync (http://localhost:5000/solver_worker.dart.js:3075:20)
    at SolverServiceImpl_operations_closure.$call$body$SolverServiceImpl_operations_closure0 (http://localhost:5000/solver_worker.dart.js:16222:16)
    at SolverServiceImpl_operations_closure.call$1 (http://localhost:5000/solver_worker.dart.js:16180:19)
    at http://localhost:5000/solver_worker.dart.js:8415:27
    at _wrapJsFunctionForAsync_closure.$protected (http://localhost:5000/solver_worker.dart.js:3111:15)
    at _wrapJsFunctionForAsync_closure.call$2 (http://localhost:5000/solver_worker.dart.js:10878:12)
solver_worker.dart.js 16443:63                                                    performLayout$6
solver_worker.dart.js 16210:93                                                    <fn>
solver_worker.dart.js 3111:15                                                     $protected
solver_worker.dart.js 10878:12                                                    call$2
solver_worker.dart.js 3075:20                                                     _asyncStartSync
solver_worker.dart.js 16222:16                                                    $call$body$SolverServiceImpl_operations_closure0
solver_worker.dart.js 16180:19                                                    call$1
solver_worker.dart.js 8415:27                                                     <fn>
solver_worker.dart.js 3111:15                                                     $protected
solver_worker.dart.js 10878:12                                                    call$2
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49      throw_
packages/squadron/src/worker.dart 134:7                                           send
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 60:31                <fn>
dart-sdk/lib/async/zone.dart 1450:47                                              _rootRunBinary
dart-sdk/lib/async/zone.dart 1342:19                                              runBinary
dart-sdk/lib/async/future_impl.dart 174:22                                        handleError
dart-sdk/lib/async/future_impl.dart 778:46                                        handleError
dart-sdk/lib/async/future_impl.dart 799:13                                        _propagateToListeners
dart-sdk/lib/async/future_impl.dart 592:7                                         [_complete]
dart-sdk/lib/async/stream_pipe.dart 61:11                                         _cancelAndValue
dart-sdk/lib/async/stream.dart 1288:7                                             <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39  dcall
dart-sdk/lib/html/dart2js/html_dart2js.dart 37301:58                              <fn>
dart-sdk/lib/async/zone.dart 1442:13                                              _rootRunUnary
dart-sdk/lib/async/zone.dart 1335:19                                              runUnary
dart-sdk/lib/async/zone.dart 1244:7                                               runUnaryGuarded
dart-sdk/lib/async/zone.dart 1281:26                                              <fn>


Interestingly, we're getting a new error now??
image

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

BaseNodeMixin is nothing fancy:

image

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

The error is consistent, not random.

method not found: 'BaseNodeMixin___BaseNodeMixin_id' on null

Must mean that we're calling .id on a null value. Very odd...

This issue is not from our code, there's just no way. We're a whole team and this is a core class that's battle-tested and has been running flawlesly for a year now... You're correct the errors are not true, there's definitely something else going wrong.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Let's add more context to the problem. Since we don't care about the type of the map at all, I did the following:

    final nodeReferences = dynNodeReferences;

    // final nodeReferences = Set.from(dynNodeReferences
    //     .map((refs) => refs.map((key, value) => MapEntry(key, value))));

    final Map<String, SceneNode> freshlyBakedNodes = {};
    print('LAYOUT START: ----------------------------------------->');
    for (final dynamic nodeJson in nodeReferences) {
      final SceneNode node =
          nodeJsonConverter.fromJson(Map<String, dynamic>.from(nodeJson))!;

I directly passed dynNodeREferences and surprise surprise, still got a similar error.

WorkerException (workerId=287635479, command=1): TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
    at Object.wrapException (http://localhost:5000/solver_worker.dart.js:762:17)
    at Object._failedAsCheck (http://localhost:5000/solver_worker.dart.js:1931:15)
    at Rti._generalAsCheckImplementation [as _as] (http://localhost:5000/solver_worker.dart.js:1920:9)
    at Object._$CanvasNodeFromJson (http://localhost:5000/solver_worker.dart.js:6138:37)
    at NodeJsonConverter.fromJson$1 (http://localhost:5000/solver_worker.dart.js:17844:20)
    at PositionManagerThread.performLayout$6 (http://localhost:5000/solver_worker.dart.js:16383:19)
    at http://localhost:5000/solver_worker.dart.js:16153:93
    at _wrapJsFunctionForAsync_closure.$protected (http://localhost:5000/solver_worker.dart.js:3111:15)
    at _wrapJsFunctionForAsync_closure.call$2 (http://localhost:5000/solver_worker.dart.js:10872:12)
    at Object._asyncStartSync (http://localhost:5000/solver_worker.dart.js:3075:20)
solver_worker.dart.js 762:17                                                      wrapException
solver_worker.dart.js 1931:15                                                     _failedAsCheck
solver_worker.dart.js 1920:9                                                      _generalAsCheckImplementation
solver_worker.dart.js 6138:37                                                     _$CanvasNodeFromJson
solver_worker.dart.js 17844:20                                                    fromJson$1
solver_worker.dart.js 16383:19                                                    performLayout$6
solver_worker.dart.js 16153:93                                                    <fn>
solver_worker.dart.js 3111:15                                                     $protected
solver_worker.dart.js 10872:12                                                    call$2
solver_worker.dart.js 3075:20                                                     _asyncStartSync
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49      throw_
packages/squadron/src/worker.dart 134:7                                           send
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 60:31                <fn>
dart-sdk/lib/async/zone.dart 1450:47                                              _rootRunBinary
dart-sdk/lib/async/zone.dart 1342:19                                              runBinary
dart-sdk/lib/async/future_impl.dart 174:22                                        handleError
dart-sdk/lib/async/future_impl.dart 778:46                                        handleError
dart-sdk/lib/async/future_impl.dart 799:13                                        _propagateToListeners
dart-sdk/lib/async/future_impl.dart 592:7                                         [_complete]
dart-sdk/lib/async/stream_pipe.dart 61:11                                         _cancelAndValue
dart-sdk/lib/async/stream.dart 1288:7                                             <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39  dcall
dart-sdk/lib/html/dart2js/html_dart2js.dart 37301:58                              <fn>
dart-sdk/lib/async/zone.dart 1442:13                                              _rootRunUnary
dart-sdk/lib/async/zone.dart 1335:19                                              runUnary
dart-sdk/lib/async/zone.dart 1244:7                                               runUnaryGuarded
dart-sdk/lib/async/zone.dart 1281:26                                              <fn>

ChromeProxyService: Failed to evaluate expression 'nodeJsonConverter'. 

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

NodeJsonConverter:

image

I'm starting to sense a pattern...

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Hello, here's an example based on your code samples.

The bottom line is to try to handle all type issues related to platform workers as close as possible to the communication layer. You have to take into account the most constrained platform, and here that would be JavaScript.

The best places to handle types aspect are in:

  • the service worker implementation, when calling "send()": make sure you "serialize" your service arguments (use only base types and List/Map) and "deserialize" the worker response
  • the service implementation "operations" map: make sure you "deserialize" the service arguments, and make sure you "serialize" the result.

To sum things up:

architecture

and the sample code:

solver_example.zip

from squadron.

d-markey avatar d-markey commented on August 27, 2024

In fact, for production code, the best way to go is maybe to define classes to communicate with your worker methods through Squadron, with built-in serialization / deserialization.

This standardized approach will make it easier to understand and maintain the code. It also makes serialization/deserialization for one class (the request or the response) a responsibility of the class, so you don't have to bother with reinterpreting types in your service methods anymore.

Something like:

class ServiceResponse {
   static ServiceResponse deserialize(dynamic result) {
      // deserialize "result", knowing it was produced by serialize()
   }

   dynamic serialize() {
      // serialize using only base types or simple List/Map
   }
}

class ServiceRequest {
   static ServiceRequest deserialize(dynamic result) {
      // deserialize "result", knowing it was produced by serialize()
   }

   dynamic serialize() {
      // serialize using only base types or simple List/Map
   }
}

abstract class ServiceDefinition {
   FutureOr<ServiceResponse> serviceMethod(ServiceRequest request);

   static const cmdServiceMethod = 1;
}

class ServiceImplementation implements ServiceDefinition {
   @override
   ServiceResponse serviceMethod(ServiceRequest request) {
      // your code to process the request and send the response
      // because all the rest is quite "automatic", this is were you want to concentrate
      // all the surroundings (request, response, worker, operations map...) becomes simple, standard plumbing
   }

   // in the operations map, deserialize arguments and serialize result

   @override
   late final Map<int, CommandHandler> operations = {
      ServiceDefinition.cmdServiceMethod: (WorkerRequest r) => serviceMethod(ServiceRequest.deserialize(r.args[0])).serialize();
   };
}

class ServiceWorker implements ServiceDefinition, WorkerService {

   // in the worker overrides, serialize arguments and deserialize result

   @override
   ServiceResponse serviceMethod(ServiceRequest request)
      => ServiceResponse.deserialize(await send(ServiceDefinition.cmdServiceMethod, [ request.serialize() ]));
}

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

@d-markey I'm at a loss to be honest with you. Your sample runs perfectly, yet my code does not, and I've tried to mimic it as much as possible since the two code-bases are nearly identical.

I'm still getting the error

WorkerException (workerId=66711533, command=1): TypeError: Instance of 'JsLinkedHashMap<dynamic, dynamic>': type 'JsLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'

multithreading.zip

This is a chunk of the classes I'm dealing with, it's near identical to your sample.

I noticed that you're instantiating a new instance of PositionManagerThread in more than one place in your sample instead of relying on createWorker and SolverWorkerImpl. I don't think it matters, but you could've used it, but you have PositionManagerThread acting like its own Worker and it seems redundant since the operations are now duplicated in your sample.

Are you able to spot what I'm doing wrong? This is driving my a bit crazy at this point. If you need to see some of the other classes, I will provide them.

I think the biggest difference is that I'm not using a WorkerPool because I need a single instance of this thing, but that shouldn't matter since it's created in the exact same fashion?

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Actually I don't instantiate a SolverWorkerImpl anymore in my sample because from the code snippets you provided, SolverWorkerImpl is just a wrapper around PositionManagerThread. It does nothing else but proxy the calls to PositionManagerThread. So I made PositionManagerThread a SolverService and a WorkerService. You can delete SolverWorkerImpl from the sample, it will work the same :-)

Regarding pool/single worker, I've already tested all possibilities. To see for yourself, you can activate single worker instead of pool by changing the main() program in my sample:

void main() {
  Squadron.setId('main');
  Squadron.logLevel = SquadronLogLevel.ALL;
  Squadron.logger = ConsoleSquadronLogger();

  // runApp(MyApp(SolverWorkerPool())); // if you want a pool
  runApp(MyApp(createWorker())); // if you want only one worker
  // runApp(MyApp(PositionManagerThread(notifier: (pos) => Squadron.info(pos)))); // you can even test the service itself!
}

Now, my implementation of PositionManagerThread is a pure invention and the only difference I see is that your version does probably not return Map<String, dynamic> values, and that would be the purpose of toJson() in SolverWorkerImpl to convert the return values from PositionManagerThread to a Map<String, dynamic>.

So I wonder, what does toJson() look like?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

performLayout returns a LayoutResult which is a @JsonSerializable object:

@JsonSerializable(explicitToJson: true, anyMap: true)
class ModelFingerPrint {
  final String id;
  final String? parentId;
  final List<String> children;
  final double x, y, width, height;
  final double summarizedChildrenWidth, summarizedChildrenHeight;

  ModelFingerPrint({
    required this.id,
    required this.parentId,
    required this.children,
    required this.x,
    required this.y,
    required this.width,
    required this.height,
    required this.summarizedChildrenWidth,
    required this.summarizedChildrenHeight,
  });

  factory ModelFingerPrint.fromJson(Map json) =>
      _$ModelFingerPrintFromJson(json);

  Map<String, dynamic> toJson() => _$ModelFingerPrintToJson(this);
}

@JsonSerializable(explicitToJson: true, anyMap: true)
class LayoutResult {
  final Set<ModelFingerPrint> models;
  final Set<String> changedIDs;

  LayoutResult({
    required this.models,
    required this.changedIDs,
  });

  LayoutResult.empty()
      : models = {},
        changedIDs = {};

  factory LayoutResult.fromJson(Map json) => _$LayoutResultFromJson(json);

  Map<String, dynamic> toJson() => _$LayoutResultToJson(this);
}

It works fine on native, so it's not like its not able to serialize/deserialize it, but perhaps its too complex of an object for js?

You're right in thinking its not the contents of performLayout that are the issue because its unable to print the first line inside the function, it instantly throws once it runs.

Here's a sample toJson output produced by the native implementation of this (since browser refuses to even print the first line in performLayout)

{models: [{id: rootNode, children: [0QHxMdQRKSuFJYw1t1ei], x: -0.0, y: -0.0, width: 20000.0, height: 20000.0, summarizedChildrenWidth: 375.0, summarizedChildrenHeight: 812.0}, {id: 0QHxMdQS5gdBIDIvgPZT, parentId: 0QHxMdQS5gdBIDIvgPZR, children: [], x: 10203.74, y: 10123.91, width: 39.96549999937415, height: 22.0, summarizedChildrenWidth: 0.0, summarizedChildrenHeight: 0.0}, {id: 0QHxMdQRKSuFJYw1t1ei, parentId: rootNode, children: [0QHxMdQS5gdBIDIvgPZD, 0QHxMdQS5gdBIDIvgPZR, 0QJ7Yt8mlmIwvbVzdlCm], x: 9815.74, y: 9801.91, width: 375.0, height: 812.0, summarizedChildrenWidth: 528.0, summarizedChildrenHeight: 818.0}, {id: 0QHxMdQS5gdBIDIvgPZD, parentId: 0QHxMdQRKSuFJYw1t1ei, children: [0QHxMdQS5gdBIDIvgPZE], x: 10282.74, y: 10074.91, width: 76.0, height: 80.0, summarizedChildrenWidth: 76.0, summarizedChildrenHeight: 80.0}, {id: 0QHxMdQS5gdBIDIvgPZE, parentId: 0QHxMdQS5gdBIDIvgPZD, children: [], x: 10282.74, y: 10074.91, width: 76.0, height: 80.0, summarizedChildrenWidth: 0.0, summarizedChildrenHeight: 0.0}, {id: 0QHxMdQS5gdBIDIvgPZR, parentId: 0QHxMdQRKSuFJYw1t1ei, children: [0QHxMdQS5gdBIDIvgPZS, 0QHxMdQS5gdBIDIvgPZT], x: 10194.74, y: 10074.91, width: 76.0, height: 80.0, summarizedChildrenWidth: 76.0, summarizedChildrenHeight: 80.0}, {id: 0QHxMdQS5gdBIDIvgPZS, parentId: 0QHxMdQS5gdBIDIvgPZR, children: [], x: 10194.74, y: 10074.91, width: 76.0, height: 80.0, summarizedChildrenWidth: 0.0, summarizedChildrenHeight: 0.0}, {id: 0QJ7Yt8mlmIwvbVzdlCm, parentId: 0QHxMdQRKSuFJYw1t1ei, children: [], x: 9754.74, y: 9836.91, width: 528.0, height: 818.0, summarizedChildrenWidth: 0.0, summarizedChildrenHeight: 0.0}], changedIDs: [0QHxMdQS5gdBIDIvgPZR, rootNode, 0QHxMdQRKSuFJYw1t1ei, 0QHxMdQS5gdBIDIvgPZD, 0QHxMdQS5gdBIDIvgPZT, 0QHxMdQS5gdBIDIvgPZE, 0QHxMdQS5gdBIDIvgPZS, 0QJ7Yt8mlmIwvbVzdlCm]}

Converted to actual json:

{
  "models": [
    {
      "id": "rootNode",
      "children": [
        "0QHxMdQRKSuFJYw1t1ei",
        "0QJ8cQrjMtNjctbJ2hpZ"
      ],
      "x": 0,
      "y": 0,
      "width": 20000,
      "height": 20000,
      "summarizedChildrenWidth": 375,
      "summarizedChildrenHeight": 812
    },
    {
      "id": "0QJ8cQrjMtNjctbJ2hpZ",
      "parentId": "rootNode",
      "children": [],
      "x": 8993.87890625,
      "y": 9838.6328125,
      "width": 109.64453125,
      "height": 310.4140625,
      "summarizedChildrenWidth": 0,
      "summarizedChildrenHeight": 0
    },
    {
      "id": "0QHxMdQS5gdBIDIvgPZT",
      "parentId": "0QHxMdQS5gdBIDIvgPZR",
      "children": [],
      "x": 10203.74,
      "y": 10123.91,
      "width": 39.96549999937415,
      "height": 22,
      "summarizedChildrenWidth": 0,
      "summarizedChildrenHeight": 0
    },
    {
      "id": "0QHxMdQRKSuFJYw1t1ei",
      "parentId": "rootNode",
      "children": [
        "0QHxMdQS5gdBIDIvgPZD",
        "0QHxMdQS5gdBIDIvgPZR",
        "0QJ7Yt8mlmIwvbVzdlCm"
      ],
      "x": 9815.74,
      "y": 9801.91,
      "width": 375,
      "height": 812,
      "summarizedChildrenWidth": 528,
      "summarizedChildrenHeight": 818
    },
    {
      "id": "0QHxMdQS5gdBIDIvgPZD",
      "parentId": "0QHxMdQRKSuFJYw1t1ei",
      "children": [
        "0QHxMdQS5gdBIDIvgPZE"
      ],
      "x": 10282.74,
      "y": 10074.91,
      "width": 76,
      "height": 80,
      "summarizedChildrenWidth": 76,
      "summarizedChildrenHeight": 80
    },
    {
      "id": "0QHxMdQS5gdBIDIvgPZE",
      "parentId": "0QHxMdQS5gdBIDIvgPZD",
      "children": [],
      "x": 10282.74,
      "y": 10074.91,
      "width": 76,
      "height": 80,
      "summarizedChildrenWidth": 0,
      "summarizedChildrenHeight": 0
    },
    {
      "id": "0QHxMdQS5gdBIDIvgPZR",
      "parentId": "0QHxMdQRKSuFJYw1t1ei",
      "children": [
        "0QHxMdQS5gdBIDIvgPZS",
        "0QHxMdQS5gdBIDIvgPZT"
      ],
      "x": 10194.74,
      "y": 10074.91,
      "width": 76,
      "height": 80,
      "summarizedChildrenWidth": 76,
      "summarizedChildrenHeight": 80
    },
    {
      "id": "0QHxMdQS5gdBIDIvgPZS",
      "parentId": "0QHxMdQS5gdBIDIvgPZR",
      "children": [],
      "x": 10194.74,
      "y": 10074.91,
      "width": 76,
      "height": 80,
      "summarizedChildrenWidth": 0,
      "summarizedChildrenHeight": 0
    },
    {
      "id": "0QJ7Yt8mlmIwvbVzdlCm",
      "parentId": "0QHxMdQRKSuFJYw1t1ei",
      "children": [],
      "x": 9754.74,
      "y": 9836.91,
      "width": 528,
      "height": 818,
      "summarizedChildrenWidth": 0,
      "summarizedChildrenHeight": 0
    }
  ],
  "changedIDs": [
    "rootNode",
    "0QHxMdQS5gdBIDIvgPZR",
    "0QHxMdQRKSuFJYw1t1ei",
    "0QHxMdQS5gdBIDIvgPZD",
    "0QJ8cQrjMtNjctbJ2hpZ",
    "0QHxMdQS5gdBIDIvgPZT",
    "0QHxMdQS5gdBIDIvgPZE",
    "0QHxMdQS5gdBIDIvgPZS",
    "0QJ7Yt8mlmIwvbVzdlCm"
  ]
}

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'position_manager_thread.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

ModelFingerPrint _$ModelFingerPrintFromJson(Map json) => ModelFingerPrint(
      id: json['id'] as String,
      parentId: json['parentId'] as String?,
      children:
          (json['children'] as List<dynamic>).map((e) => e as String).toList(),
      x: (json['x'] as num).toDouble(),
      y: (json['y'] as num).toDouble(),
      width: (json['width'] as num).toDouble(),
      height: (json['height'] as num).toDouble(),
      summarizedChildrenWidth:
          (json['summarizedChildrenWidth'] as num).toDouble(),
      summarizedChildrenHeight:
          (json['summarizedChildrenHeight'] as num).toDouble(),
    );

Map<String, dynamic> _$ModelFingerPrintToJson(ModelFingerPrint instance) {
  final val = <String, dynamic>{
    'id': instance.id,
  };

  void writeNotNull(String key, dynamic value) {
    if (value != null) {
      val[key] = value;
    }
  }

  writeNotNull('parentId', instance.parentId);
  val['children'] = instance.children;
  val['x'] = instance.x;
  val['y'] = instance.y;
  val['width'] = instance.width;
  val['height'] = instance.height;
  val['summarizedChildrenWidth'] = instance.summarizedChildrenWidth;
  val['summarizedChildrenHeight'] = instance.summarizedChildrenHeight;
  return val;
}

LayoutResult _$LayoutResultFromJson(Map json) => LayoutResult(
      models: (json['models'] as List<dynamic>)
          .map((e) => ModelFingerPrint.fromJson(e as Map))
          .toSet(),
      changedIDs:
          (json['changedIDs'] as List<dynamic>).map((e) => e as String).toSet(),
    );

Map<String, dynamic> _$LayoutResultToJson(LayoutResult instance) =>
    <String, dynamic>{
      'models': instance.models.map((e) => e.toJson()).toList(),
      'changedIDs': instance.changedIDs.toList(),
    };

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Moving toJson into the inside of performLayout and making the function return the Map<String, dynamic> directly does not fix it :/

from squadron.

d-markey avatar d-markey commented on August 27, 2024

what browser are you using? have you tried several?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

I noticed in the Stack trace the following:
image

The NodeJsonConverter looks like this:
image

Could it be the cause? It's expecting a Map<String, dynamic> but we're giving it a <dynamic, dynamic> from javascript.
This is triggered here:
image

I've been testing in a chrome browser on my macbook pro M1. Chrome is up to date.
image

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Could always try this on all our serializable data? But this assumes that javascript simply can't handle any toJson conversion which is absurd because flutter runs in the web just fine...

image

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Yes, you need to "deep rebuild" the data after communication, not just the first level.

for (final dynamic nodeJson in nodeReferences) {

=> nodeReferences is a Map<String, dynamic> and I guess nodeJson has lost its strong type too. It's probably now just a Map. So I don't think it's valid to call Map<String, dynamic>.from(nodeJson). Maybe try RebuildMap<String>(nodeJson)?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Same error

I want to remark that not even the print in the screenshot is running.
image

image

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Or in NodeJsonConverter, change to fromJson(Map json) which should allow you to call fromJson(nodeJson) directly in performLayout. You might have to check the other fromJson() methods from RootNode, CanvasNode, etc.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Yes. We have a lot of node classes I will need to convert to use anyMap = true... I will need some time to do this.

By the way, once we figure this out, I'm writing a big technical article about squadron :)

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Cross platform multithreading comes with a cost! I hope you'll get there! 🤞

from squadron.

d-markey avatar d-markey commented on August 27, 2024

Regarding print(), I've also seen strange behaviour in the browser. it seems it does not always log to the console...

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

It seems to have finally worked! It's a massive caveat though :( I had to make an extension to cast Map to Map<String, dynamic> and used it where all the errors where because huge parts of the project assume the usage of Map<String, dynamic>

This is... quite the cost... This should 100% be documented very loudly.

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Thank you so much for the continued support and being patient with me <3

from squadron.

d-markey avatar d-markey commented on August 27, 2024

You're welcome, glad you did it! I will document those type aspects to warn people about dealing with complex data, serializing all the way down on one side then deserializing all the way up on the other side.

I recognize json_serializable from your annotations, could you share the configuration required to achieve this serialization?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

Yes, sure.
image

image

That's all that's required for jsonSerializable. You need to regenerate the generated files afterwards.

from squadron.

d-markey avatar d-markey commented on August 27, 2024

I was thinking of the build.yaml configuration, did you just switch any_map to true and that made it work?

from squadron.

SaadArdati avatar SaadArdati commented on August 27, 2024

You CAN use build.yaml and generate it that way, its the same thing. But you'll still need to strip away the types from toJson and fromJson.

I set the any map manually in this case

from squadron.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.