Skip to content
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

Canceling and Interrupting doc #7185

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 75 additions & 0 deletions docs/features/canceling_calls.md
@@ -0,0 +1,75 @@
Canceling Calls
===============

Call.cancel() Just Works
------------------------

Cancel calls with `Call.cancel()`. It's safe to call from any thread and has no harmful
side effects.


Thread.interrupt() is Clumsy
----------------------------

`Thread.interrupt()` is Java's built-in mechanism to cancel an in-flight `Thread`, regardless of
what work it's currently performing.

We recommend against using `Thread.interrupt()` with OkHttp because it may disrupt shared resources
including HTTP/2 connections and cache files. In particular, calling `Thread.interrupt()` may cause
unrelated threads' call to fail with an `IOException`.

Also avoid these related methods:

* `Future.cancel(true)` interrupts the thread performing the work.
* `ExecutorService.shutdownNow()` interrupts all threads owned by the executor service.

The rest of this document describes how OkHttp supports thread interrupts.

### Types of Threads

**Call threads** are threads that perform the HTTP call; these are either owned by the
application (`Call.execute()`) or by OkHttp’s Dispatcher (`Call.enqueue()`).

**OkHttp-internal threads** are helper threads that OkHttp uses to manage shared resources. This
includes HTTP/2 connection reader, connection pool manager, cache journal compaction, fast
fallback, and web socket writes. These are usually managed by OkHttp's internal task runner.

Note that the application’s `EventListeners` may be invoked on either kind of thread, or even by
completely unrelated threads as in `EventListener.canceled()`.

### Types of Interrupts

We define an interrupt type for each thread type:

**Precise interrupts** apply to a specific call thread.

**Broad interrupts** apply to OkHttp-internal threads. This happens when a framework loops over
threads it doesn't own and cancels them.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
threads it doesn't own and cancels them.
threads it doesn't own and interrupts them.


### Precise Interrupt Policy

**We discourage precise interrupts because they may have collateral damage.** Canceling a thread
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put up an ExoPlayer PR to use enqueue and CountdownLatch.

that happens to be writing to a shared HTTP/2 connection damages that connection for all calls
sharing that connection. It may cause multiple unrelated calls that share that connection to fail.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this I'll reopen my existing PR. It implements this logic

// connection cannot be trusted anymore, partial writes are possible
      this.failConnection(iioe)

Similarly, interrupting a thread that’s writing the cache journal may require the journal to be
rebuilt. We recommend `Call.cancel()` because it achieves the same effect without these drawbacks.

**We support precise interrupts as much as possible.** If a user is using thread interruption, we
treat it like call cancellation for the call the interrupted thread is performing.

To implement this policy, we have code to limit the blast radius of untimely interruption, including
checking the interrupt state before operating on shared resources. We have tests specifically
confirming that precise interrupts don't permanently damage the response cache.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Implement limiting blast radius on HTTP/2 connections + tests
  • Implement limiting blast radius on disk cache + tests
  • Don't permanently damage the response cache + tests


### Broad Interrupt Policy

**We treat broad interrupts as hostile shutdown signals.** If an OkHttp-internal thread is
interrupted, we stop managing shared resources and fail all calls that follow.

Once the connection pool thread is interrupted, OkHttp degrades the connection pool and cancels
every call that attempts to use it.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Create a cache degraded state that throws IOException when accessed
  • Create a connection pool degraded state that throws IOException when accessed


The same is true for the cache journal connection thread. Once interrupted, OkHttp degrades the
cache and cancels every call that attempts to use it.

Note that it is never necessary to perform broad interrupts.
1 change: 1 addition & 0 deletions mkdocs.yml
Expand Up @@ -95,6 +95,7 @@ nav:
- 'Events': features/events.md
- 'HTTPS': features/https.md
- 'Interceptors': features/interceptors.md
- 'Canceling Calls': features/canceling_calls.md
- 'Recipes': recipes.md
- 'Security':
- 'Security': security/security.md
Expand Down