Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8243098

try-with-resources should allow try-objects to specify their open operations as well as close

    XMLWordPrintable

Details

    • Enhancement
    • Status: New
    • P4
    • Resolution: Unresolved
    • None
    • None
    • core-libs
    • None

    Description

      The TWR (try-with-resources) code `try (var __ = foo()) { bar(); }` potentially suffers from the problem of *lost closes*. Somewhere inside the expression `foo()` is a resource allocation step which, once performed, must be reversed by an eventual call to `close`. The close can fail to happen if the evaluation of `foo()` throws an exception (notably, OOME or SOE) *after* the resource allocation step. Such an abnormal termination amounts to a gap in the coverage of the TWR, since the user thinks of `foo()` as an abstraction which either throws an error or returns normally with the allocated resource. The third case, the gap, happens when the resource is allocated *and* the expression throws an error. When this happens, the "close" method is not called; this is a lost close; the "open" has fallen into a gap before the coverage of the implicit "finally" clause of the TWR.

      Layered abstractions, such as lambdas, can create such gaps in coverage, leading to lost closes. Currently the workarounds are limited; this RFE proposes a new tool to solve for lost closes. Here are sketches of the workarounds, for a hypothetical object type `OpenAndClose` which requires balanced opens and closes.

      Workaround #1: Keep the head expression of the TWR simple. It must do exactly one thing, the resource allocation step which needs to be closed.

      ```
      OpenAndClose x = …;
      try (x.open()) {
      }
      ```

      Pitfall: Clunky to code, hard to read. The name "open" is not standardized. Adapter abstractions might be layered on top of OpenAndClose, cause the "open" step to be mixed with other steps (even lambda allocations) which could fail.

      Workaround #2: As a design pattern, ask the user to put the open step first in the body of the TWR.

      ```

      try (OpenAndClose x = …) {
         x.open(); // must be first
      }
      ```

      Pitfall: Error-prone. Designers of abstractions will not favor this, since it makes their abstractions harder to use. Also, the open statement must not throw (puts more load on the abstraction designer), or else the open statement if it throws must consult internal object state to determine if the auto-close operation should be nullified.

      Proposal: Define an optional new interface `java.lang.AutoOpenable`, a mirror image of `AutoCloseable`, with a nullary `open` method, throwing `Exception`. If the head expression of a TWR has this type, the TWR code calls `open` operation on the head object, immediately before entering the block covered by the `finally` clause that calls `close`.

      If the `open` method throws, the resource has *not* been created, and there is no lost close. The contract for the open method is that the resource is created if and only if the method returns normally. This removes any gaps that can cause lost opens, since the exact "moment" of resource allocation (really, the logical state change) is tracked precisely by the TWR statement.

      (Categorizing this as a core-libs bug for starters, but it's a language RFE also, since TWR needs to learn a new trick. But to make it worthwhile, core-libs folks need to decide if it applies to their API points and those of their customers, so they should evaluate it first.)

      Attachments

        Activity

          People

            Unassigned Unassigned
            jrose John Rose
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated: