Support async setup method on Predictor
This commit introduces the ability to define an async `setup` function
on your predictor. For simplicity an async `setup()` function is only
supported alongside an async `predict()` function. An error will be
raised during setup if this is not the case.
Various pieces of the code have been extracted into smaller methods
in order to achieve this. A new `_handle_setup_error` context manager
has been created to handle setup errors and send appropriate `Done`
event over the worker channel.
The `_setup()` method has been split into two phases, first we perform
validation on the requirements for async/concurrency support. Then we
attempt to run the `setup()` method either as a direct call for the
non-async path or as part of the event loop in the async path.