d-markey / squadron

Multithreading and worker thread pool for Dart / Flutter, to offload CPU-bound and heavy I/O tasks to Isolate or Web Worker threads.
https://pub.dev/packages/squadron
MIT License
79 stars 0 forks source link

About throwing Exception from worker #27

Closed sabin26 closed 9 months ago

sabin26 commented 9 months ago

I implemented this custom class that I wanted to throw from worker.

class HttpException extends WorkerException {
  HttpException({final String? message})
      : super(message ?? 'Received a response with non-successful status code');
}

It works but its toString() method is too verbose. It's serialized into a list in the pattern: [int, message, WorkerException]

I wanted to only return the message.

class HttpException extends WorkerException {
  HttpException({final String? message})
      : super(message ?? 'Received a response with non-successful status code');

  @override
  String toString() {
    return message;
  }
}

But that does no effect. How should I handle this ?

d-markey commented 9 months ago

Hello,

WorkerExceptions must be serialized / deserialized from the worker back to your main thread, so you will need to implement your serialization logic in your HttpException. Typically, override the serialize() method and implement a static deserialize() method. This static method must be registered with Squadron so that it can recreate an instance of HttpException in the main thread when the exception is raised from a worker thread.

When Squadron receives an exception from a worker (actually, it receives a List containing the exception data = the result of serializing the Worker exception), it will pass that data to all registered deserializers until one of them provides a proper instance. So your deserializer should return null if the payload was not generated by your serialization method. Currently, this is done by checking a specific identifier.

You will find a sample implementation here:

https://github.com/d-markey/squadron/blob/73d0814789fddd6f4f4433d7ae96ae447325fab2/test/classes/custom_exception.dart#L3-L21

To register the deserializer, pass your static deserialization method to SquadronException.registerExceptionDeserializer() when your program starts, eg.:

https://github.com/d-markey/squadron/blob/73d0814789fddd6f4f4433d7ae96ae447325fab2/test/test_suites/worker_test_suite.dart#L419-L420

sabin26 commented 9 months ago

Great ! I tried and it worked. Thank you so much for your help.

sabin26 commented 9 months ago

I do have a query though. What happens if we register the deserializer multiple times for same exception class ? Do we need an unregister handler for it ?

d-markey commented 9 months ago

Not much will happen... it will use more memory than necessary because Squadron uses a List internally to store the deserializers. When a deserialization is needed, the first registered instance will provide a result and evaluation will stop there, so any duplicate instance registered after the first one will never be used.

But that's a good catch. Currently, there is no protection against duplicate exception deserializers and no way to unregister a deserializer. I guess I'll change the implementation to use a Set instead of a List. I could also add support to unregister a deserializer, but I'm not sure why you'd ever want to do that. If you unregister a custom exception deserializer and a worker thread raises such an exception, it will be handled as a generic one which could wreck havoc in your error handling code. I believe it would be difficult to keep in sync the lifecycle of the exception deserializers with respect to that of the workers.

sabin26 commented 9 months ago

I prefer unregisterExceptionDeserializer method instead of Set. It will help me in my code as I have certain worker pool that will only return the custom exception and not by other worker pools. I can register and unregister when starting and stopping that worker pool.

d-markey commented 9 months ago

Squadron 5.1.6 provides a way to unregister exception deserializers.

Please note that if you unregister a deserializer too early (ie. when a task is still running, and that task throws a custom exception), then the custom exception thrown in the worker thread will be propagated as a SquadronException to the main thread.