Inputs on patch for JDK-8225763? Inflater and Deflater should implement AutoCloseable
Stuart Marks
stuart.marks at oracle.com
Wed Jul 3 03:27:05 UTC 2019
Hi Jaikiran,
OK, good analysis. Rather a lot of issues for what seems like a simple patch, eh?
- Idempotency
Yes, it looks to me like Inflater.end() and Deflater.end() implementations are
idempotent. As you point out, overrides by subclasses might not be. We should be
clear when we're talking about the specific implementatations of the end()
methods in the Deflater and Inflater classes, or whether we're talking about the
contracts defined by these method specifications on these classes and subtypes.
The behavior of an implementation in a class can be specified with @implSpec
without imposing this on the contract of subclasses. This is useful for
subclasses that need to know the exact behavior of calling super.end(), and also
for callers who know they have an instance of a particular class and not a subclass.
The upshot is that while we might not have the end() method's contract specify
idempotency, we can certainly say so in an @implSpec, if doing this will help.
I'm not sure we'll actually need it in this case, but let's keep it in mind.
- Subclasses
I don't think there are any subclasses in the JDK, and I did some searching and
I didn't find any subclasses "in the wild" either. I did find a few uses of
these classes though. If these classes are rarely subclassed, we might be able
to get away with changing the contract, though this does entail some risk.
If we need to modify the subclasses used in tests, that's fair game though.
- Relationship between end() and close()
I think close() should have more-or-less the same semantics as end(), namely,
pretty much the way end() is specified now regarding releasing resources. But it
should be allowable to call both in either order.
try (var inf = new Inflater()) {
...
# explicitly call end()
inf.end();
}
I think this should allowed, but it shouldn't be required. The application can
call either close() or end() and this will have the effect of releasing the
native resources.
A key question is whether close() should be specified to call end() -- which is
subject to being overridden by suclasses -- or whether close() is defined to do
the same thing as the end() implementation in this class does. This can be
implemented by taking the body of the current end() method and refactoring it
into an internal method and then having both close() and end() call that
internal method.
If close() calls end() then AC+TWR might call close() twice, and therefore call
end() twice, which might be a problem for subclasses. So to be really
conservative we might want to do the internal-method refactoring to avoid this
problem.
On the other hand, suppose a subclass has extra resources it frees in its end()
method, which then calls super.end(). Even though this class would inherit AC,
using it in TWR would be a *bug* because close() would call the internal method
to free the superclass resources, but this would leak the subclass resources.
That sounds like a mistake to perpetuate in the API.
It's possible for a subclass to override end() in such a way that's not
idempotent, but I think this is unlikely. I'm leaning toward risking the small
possibility of incompatibility in declaring end() idempotent, allowing close()
simply to call end(). This makes TWR more useful in the long run, which seems
like a better position to be in. Of course, if somebody turns up evidence that
this would break a bunch of stuff, we should reconsider.
(This might be moot anyway. The condition where TWR closes a resource multiple
times occurs in cases where closing a wrapper closes the wrapped resource, and
both are TWR resource variables. But in my earlier example
try (var inf = new Inflater();
var iis = new InflaterInputStream(inputStream, inf)) {
...
}
and in fact all of {Inflater,Deflater}{Input,Output}Stream don't close the
passed-in Deflater/Inflater, so multiple close() calls won't occur.)
- Deprecation of end()
I don't think deprecation of end() is useful. It'll just cause noise for people.
Uses such as
var deflater = new Deflater();
try {
...
} finally {
deflater.end();
}
are valid and correct and needn't be changed (though using TWR is preferable,
this is more of a style issue).
- Undefined behavior after close()/end()
OK, I'll admit this is possibly out of scope, but the line in the specs about
"once end() is called, the behavior is undefined" rubs me the wrong way. Right
now, most operations seem to call ensureOpen(), which throws NPE if the object
has been closed. But "undefined" allows anything to happen, including filling
the output buffer with garbage. That seems wrong. We might not want to specify
what exception is thrown, but we might want to specify that *AN* exception is
thrown -- which must be a RuntimeException, since most methods don't declare any
checked exceptions.
In any case, the clause would have to be updated to say something like "Once
this Deflater (Inflater) is closed, any subsequent operations will <whatever>."
**
If you think the issues are settled enough, maybe it's time for you to take a
stab at a patch. Up to you how to proceed with the "undefined" issue. If it's
simple, great, I'd like to see it happen. But if it leads you off into the
weeds, then feel free to drop it.
Note: US holiday weekend coming up; replies might take several days.
s'marks
On 6/29/19 4:16 AM, Jaikiran Pai wrote:
>
> On 29/06/19 4:31 PM, Jaikiran Pai wrote:
>> Hello Stuart,
>>
>> Thank you for the detailed response. Comments inline.
>>
>> On 28/06/19 2:48 AM, Stuart Marks wrote:
>>> On 6/26/19 9:28 PM, Jaikiran Pai wrote:
>>>> I am looking to contribute a patch for the enhancement noted in
>>>> https://bugs.openjdk.java.net/browse/JDK-8225763. The change itself
>>>> looks relatively straightforward to me and here's what I plan to do:
>>>>
>>>> 1. Have both java.util.zip.Inflater and java.util.zip.Deflater start
>>>> implementing the AutoCloseable interface
>>>>
>>>> 2. Have the close() method call the end() method
>>>>
>>>> 3. Add javadoc to the close() implementation to mention that the end()
>>>> method gets called
>>>>
>>>> 4. Add test(s) to verify that the close indeed actually "ends" the
>>>> inflater/deflater
>>>>
>>>> 5. This is more of a question - do we have to update the class level
>>>> javadoc of both these classes to make a mention that these classes have
>>>> started implementing the AutoCloseable starting version 14 (or whichever
>>>> version this change makes into) of Java?
>>>>
>>>> Any other inputs/suggestions on what else might be needed in this
>>>> change?
>>> Hi Jaikiran,
>>>
>>> Thanks for picking this up. There are some small wrinkles with this,
>>> but I hope nothing that will prevent it from happening.
>>>
>>>
>>> 2) Alan already mentioned this, but we need to consider the issue of
>>> idempotent close() or end() carefully. It's not strictly required, but
>>> it would be nice. I had taken a quick look at end() and I *thought* it
>>> was idempotent already, but a more careful analysis needs to be done.
>> I did some checks in the current JDK code. From what I see, the Inflater
>> and Deflater do not have any subclasses within the JDK itself.
>
> To be clear - I couldn't find any subclasses in the main source code of
> JDK. There are a couple of subclasses in the tests
> (ConstructInflaterOutput.java and ConstructDeflaterInput.java), but
> those don't do much outside of the context of testing.
>
> -Jaikiran
>
>
More information about the core-libs-dev
mailing list