Skip to content

Deprecate and remove Job.cancel(cause) #481

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

Closed
qwwdfsad opened this issue Aug 7, 2018 · 5 comments
Closed

Deprecate and remove Job.cancel(cause) #481

qwwdfsad opened this issue Aug 7, 2018 · 5 comments

Comments

@qwwdfsad
Copy link
Member

qwwdfsad commented Aug 7, 2018

Job.cancel(any exception) is a cumbersome API:

  • It has unclear semantics (especially after Inconsistent cancellation and lost exceptions #333 fix, when cancel(cause) can return true more than once for the sake of exception handling)
  • It shares a given exception between multiple coroutines which can mutate it
  • It doesn't provide any significant value from the user-facing API standpoint, because cancel can return false, forcing a user to handle an exception on the callsite anyway

As for implementation, it complicates Job exception handling a lot and has a lot of maintenance burden as opposed to Job.cancel() or Job.cancel(message: String)

We can remove cancel(cause) from public API and move it to attachChild machinery (e.g. by introducing ParentHandle instead of DisposableHandle).
It should make parent-child communication (-> cancellation and exception handling) much simpler and open a door for state machine optimizations.

If someone has a use-case which cannot be reasonably implemented without cancellation with a custom exception, let's discuss it here.

@qwwdfsad qwwdfsad changed the title Deprecate and remove Job.cancel(cause) Deprecate and remove Job.cancel(cause) Aug 7, 2018
@qwwdfsad
Copy link
Member Author

qwwdfsad commented Aug 9, 2018

After discussion with @LouisCAD I've found at least one use-case which is hard to workaround: external callback-based API working as parent/owner of the job.

An example may be found here.
In this example, CameraDevice (CameraDevice.StateCallback) actually owns the job and cancels it any (asynchronous) error occurred with that error. And it's essential for a user to get this error on completion.await(), otherwise troubleshooting may be cumbersome.

Workaround without cancel(cause) requires to create one more Deferred and pass it as parent of the completion, but it makes code less readable

@LouisCAD
Copy link
Contributor

LouisCAD commented Aug 9, 2018

It's worth noting that the use case shown above uses cancel(cause) on a Deferred (which is a subtype of Job).

cancel(cause) could be moved from Job to Deferred, which would imply that cancelling a Job from the outside with a custom Throwable/Exception would no longer be possible.

That may be a good thing since doing so with a Throwable which is not a subclass of CancellationExcepion causes a crash that can only be avoided with a custom coroutine exception handler.

If anyone uses job.cancel(cause) on a type that is not a Deferred, please comment and explain your use case (and link any open source lines or gist).

@elizarov
Copy link
Contributor

elizarov commented Aug 9, 2018

@LouisCAD Keep in mind the idea spelled out in #220 (abolish difference between cancelled and failed job). We have CompletableDeferred.completeWithException(cause). It does not look like we would need cancel(cause) there.

@LouisCAD
Copy link
Contributor

LouisCAD commented Aug 9, 2018

@elizarov I have read the issue you linked thoroughly, and I agree on merging the failed state into the cancelled state, makes it easier to reason about.

However, you are talking about replacing CompletableDeferred.completeExceptionally(cause) by cancel(cause). I think this is great thing as it caused a bit of confusion to me when I used CompletableDeferred not so long ago, but this can't be done if cancel(cause) is removed from Job and not added back to Deferred, am I right? What are the plans for this?

BTW, I think all these changes should appear in the same release to allow "one shot" migration.

@qwwdfsad
Copy link
Member Author

qwwdfsad commented Aug 9, 2018

@elizarov note that completion is deferred returned by async call, it's not a CompletableDeferred instance and has no completeWithException.

IMO having complete* family on Deferred is a bad idea as we have seen in CompletableFuture API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants