-
-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WorkerException: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'SendPort?' #3
Comments
Hello @SwissCheese5, sorry I missed your post tonight. I'll have a look tomorrow and will keep you posted. |
Thank you :) |
Hello, I've replicated your code to try and reproduce but found no problem. I've had to adapt some parts of the code in particular I implemented a dummy PositionManagerThread, so maybe the problem is in there? Also, I had to update FYI here's the sample You can just
|
Thank you, i'll look into this in the morning. |
BTW if you're going to have only one thread, you'd be better off using a standalone |
Ah, just create a simple and fresh instance of SolverWorkerImpl()? If not, can you please provide a simple example |
It's part of the sample, you will see the last use case uses a worker directly without a pool: If you use an instance of |
Makes sense. I'll try this out soon. By the way I couldn't get the dart js command to work. Are you sure it's still working? If you are, I'll post logs tomorrow. |
Here's a trimmed down implementation if you're positive you don't need JavaScript and don't need to manage a worker pool. I'll look into the problem regarding |
That's the issue, I definitely NEED the javascript. That's why I'm trying to use this package :P |
The need to use this package came from building an app with a very heavy computational process that must run on both desktop and web. So this package seemed perfect as it utilizes isolates on the web. But the dart js command seemed to fail every time. Will post logs later. |
OK, you wrote "Isolate only, no JavaScript" so I thought you wouldn't need it :-P One important thing regarding Isolates and Web Workers is that data is copied (serialized/deserialized) when it passes from the main thread to the worker thread and back. And usually, the object types will not survive serialization. In particular, a For more background details:
You could try to modify the service worker like this to convert class SolverWorker extends Worker implements SolverService {
SolverWorker(dynamic entryPoint, {String? id, List args = const []})
: super(entryPoint, id: id, args: args);
@override
Future<Map<String, dynamic>> initRoot(Map<String, dynamic> rootNodeJson) {
return send(
SolverService.cmdInitRoot,
[
rootNodeJson,
],
);
}
@override
Future<Map<String, dynamic>> performLayout(
Set<Map<String, dynamic>> nodes,
Set<String> createdNodes,
Set<String> removedNodes,
Set<String> adoptedNodes,
Set<String> droppedNodes,
Set<String> changedNodes,
) {
return send(
SolverService.cmdPerformLayout,
[
nodes.toList(),
createdNodes.toList(),
removedNodes.toList(),
adoptedNodes.toList(),
droppedNodes.toList(),
changedNodes.toList(),
],
);
}
} and you will have to do the opposite on the receiving end: class SolverServiceImpl implements SolverService, WorkerService {
final PositionManagerThread positionManager =
PositionManagerThread(notifier: (pos) => print(pos));
@override
FutureOr<Map<String, dynamic>> initRoot(Map<String, dynamic> rootNodeJson) =>
positionManager.initRoot(rootNodeJson);
@override
FutureOr<Map<String, dynamic>> performLayout(
Set<Map<String, dynamic>> nodesReference,
Set<String> createdNodes,
Set<String> removedNodes,
Set<String> adoptedNodes,
Set<String> droppedNodes,
Set<String> changedNodes,
) async {
return positionManager.performLayout(
nodesReference,
createdNodes,
removedNodes,
adoptedNodes,
droppedNodes,
changedNodes,
);
}
@override
late final Map<int, CommandHandler> operations = {
SolverService.cmdPerformLayout: (WorkerRequest r) => performLayout(
rebuildSet<Map<String, dynamic>>(r.args[0]),
rebuildSet<String>(r.args[1]),
rebuildSet<String>(r.args[2]),
rebuildSet<String>(r.args[3]),
rebuildSet<String>(r.args[4]),
rebuildSet<String>(r.args[5]),
),
SolverService.cmdInitRoot: (WorkerRequest r) => initRoot(
rebuildMap(r.args[0]),
),
};
static Set<T> rebuildSet<T>(List items) => items.cast<T>().toSet();
static Map<String, dynamic> rebuildMap(Map dict) => dict.map((key, value) => MapEntry<String, dynamic>(key, value));
static Set<Map<String, dynamic>> rebuildMapSet(List<Map> items) => items.map((item) => rebuildMap(item)).cast<Map<String, dynamic>>().toSet();
} Also check that I'll try to build a Flutter sample but it may not come before the week-end! |
Calling start() is optional, it will be done when the first task is scheduled. Calling it upfront eg when your app is initializing will avoid the latency of doing it when the first task is queued. Back to your problem, thanks for the screenshot. You're doing things right AFAICT and will need more time to investigate. If I can make a recommendation though, I would avoid being too strict on types for the service API. Eg avoid Set or Map <String, ...> in favor of the more generic List and Map. |
@d-markey Noted! :) Eagerly waiting for your investigation results! 🤞 |
I believe I have good news, but I still need to wrap things up before releasing a fix. The bottom line is an exception is thrown during the worker initialization. The connection "protocol" in place between the main app and the workers does not play well when that happens, I'm fixing it. That was an interesting one! The bad news is the exception might come from your code, in which case your app will still crash. The fix will ensure Squadron provides better information to understand the situation, but you will have to figure it out. Could you try this in // Isolate implementation (VM patform)
import 'package:squadron/squadron_service.dart';
import '../solver_service.dart';
void start(Map command) => run((startRequest) {
try {
return SolverServiceImpl();
} catch (ex, st) {
print(ex);
print(st);
rethrow;
}
}, command); Remember that Isolates in Flutter cannot use some advanced features, eg. sending platform-channel messages (see https://docs.flutter.dev/development/platform-integration/platform-channels#channels-and-platform-threading) |
The cool thing about my use case is that i don't want complex data, only lists of strings and nothing more. i highly doubt my data is the problem. I will try your code soon!! |
I'm getting normal and absolutely valid errors now concerning my normal logic!! I'll keep fixing them and then come back :) It's actually throwing normal errors now. |
I was not concerned about the data, more about the execution context. The worker isolates are like "fresh" processes and if your service relies on other classes that need initialization, you have to set everything up inside the worker isolate before any message is sent.
|
Right now, I'm facing an issue where the dart js command will throw an exception on every single object that relies on a flutter import Screen.Recording.2022-02-05.at.12.51.15.PM.mp4I'm assuming this is because I'm using flutter imports, and by substitution, dart:ui imports in the isolate logic. I'm accessing classes like Size/Offset/Axis/Rect, nothing more, which are unfortunately inside dart:ui It's weird because the isolate runs just fine with them on vm, but compiling it to js pretty much fails. What's weirder is that the dart js command is throwing flutter errors across the entire project, as if its ignoring my specific files. I'm not sure why. In any case, do you think purging all references from dart:ui will have the dart js command compile correctly? It's the last barrier I'm faicng. |
Web Workers in general (not just Dart or Flutter) can't work on UI, because they don't have access to the DOM (see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers and https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers). So I guess you will have to move everything UI-related out of your worker service. In your example code, the service methods return Futures, and that means the full results will only be available for updating the UI when everything has been computed. If you want to have progressive updates to your UI, you will need to change your service implementation and stream data from your workers back to your main program so it has a chance to update UI during processing. Squadron workers and worker pools supports streaming, you should just replace Future with Stream in the service definitions (using |
Hm, that won't do since our computation is not meant to stream any data. I'll see if moving everything out resolves the issue. Closing this issue for now. I'll open a new one if any new issues occur. Thank you for your time! |
Ok, I hope you'll achieve what you want with your app :) Have you tried narrowing imports? eg. |
That's an excellent idea! Because creating our own implementation of those classes is a collosal task in our collosal project. I'll give it a shot right now. |
Sad to say it failed :( |
Bad luck :-( but was worth a shot. |
Transferred the issue to Squadron as it really belons there |
Version 3.2 has been published to pub.dev! |
Trying my first run converting a complex system to use isolates/workers, i get the exception in the title.
I replicated the thumbnail example as far as I can tell.
Here's my code:
solver_service.dart
solver_worker_activator.dart
:solver_worker_pool
:solver_vm.dart
:solver_worker_activator.dart
:Usage is quite simple:
I'm not really sure what I'm doing wrong. There's no mention or example that brings up SendPort/ReceiverPort for Squadron.
I get this error for the performLayout() function as well.
I'm running this in a MacOS window. So the isolate only. No javascript
The text was updated successfully, but these errors were encountered: