Skip to content
This repository was archived by the owner on Jun 6, 2019. It is now read-only.

Proper cause for Exceptions Deferred is resumed with #30

Closed
wants to merge 5 commits into from

Conversation

cbruegg
Copy link

@cbruegg cbruegg commented Oct 7, 2018

I really like this library, but it has caused me some headaches related to network errors. Continuations are resumed with the Exception thrown by OkHttp, but unfortunately that Exception contains no indication about where the network request was started. This is because OkHttp of course uses a thread pool and thus the stack trace only includes internal OkHttp code.

With this PR, the stack during the call to await is recorded. In case of a failure, the Exception cause chain is traversed and another Exception that contains the recorded stack trace is appended. This way users of this library will be able to easily figure out which await call in particular has caused an Exception without manually wrapping every call to await in a try-catch block that wraps it in another locally-created Exception.

@cbruegg cbruegg changed the title Proper cause Proper cause for Exceptions Deferred is resumed with Oct 7, 2018
@JakeWharton
Copy link
Owner

This library is about to be obviated by square/retrofit#2886 and as a result isn't accepting any changes.

@JakeWharton JakeWharton closed this Oct 8, 2018
@cbruegg
Copy link
Author

cbruegg commented Oct 8, 2018

Thanks for the heads-up. It should be simple to port it to that version, fortunately. Do you see the value in this change? If yes, then I'd work on a new PR.

@cbruegg
Copy link
Author

cbruegg commented Oct 8, 2018

To be precise, the await functions here are probably having the same issue: The stack trace won't contain the actual call-site.

@JakeWharton
Copy link
Owner

I don't want asymmetric functionality specific to Kotlin. The same problem exists for Java callers in a variety of adapters.

@cbruegg
Copy link
Author

cbruegg commented Oct 8, 2018

The same problem exists for Java callers in a variety of adapters.

While that's true, I'd argue it's not surprising there since it's callback-based.

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // Naturally e does not contain any reference to the first line of this snippet,
        // but this is expected, since e was not thrown and caught at that call-site, but is received
        // as a parameter here.
        e.printStackTrace();
    }

    @Override
    public void onResponse(Call call, final Response response) throws IOException {
      ...
    }
}

On the other hand, consider the following Kotlin code:

try {
  val x = call.await()
} catch (e: Exception) {
  // We catch an Exception here that should stem from the call-site above,
  // but the stack trace contains no references to it. Which means we caught an Exception
  // here that doesn't look like it could be caught here.
  e.printStackTrace()
}

I strongly believe that this violates the principle of least surprise. Am I missing any concrete issues with the approach of appending another cause to the Exception chain that removes this surprising behavior, aside from the fact that Java-users of course couldn't profit from this, since there are no suspending functions in Java?

@JakeWharton
Copy link
Owner

This is a problem of all truly async coroutine use. Why should we special case it instead of something done in a more general fashion? I'd also be curious of the performance impact since filling in the stacktrace is far from cheap.

@cbruegg
Copy link
Author

cbruegg commented Oct 8, 2018

IIRC Deferred.await preserves the stack trace properly, including the actual callsite, but I'm not entirely sure. But even if so, is "other approaches have the same problem" truly a good argument against fixing it here?

Maybe this could also be solved inside Call.enqueue in OkHttp, that way even Java users could profit from the change.

I assume that the performance overhead is minor compared to the cost of an HTTP call. However, if you're generally open to fixing this problem, I could do some benchmarking. What do you think? Thanks already!

@JakeWharton
Copy link
Owner

The cost of the HTTP call is on a background thread–not on a the callers thread.

@cbruegg
Copy link
Author

cbruegg commented Oct 8, 2018 via email

@JakeWharton
Copy link
Owner

JakeWharton commented Oct 13, 2018 via email

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

Successfully merging this pull request may close these issues.

2 participants