RFR [14] JDK-8225763 Inflater and Deflater should implement AutoCloseable
Peter Levart
peter.levart at gmail.com
Sat Jul 13 09:20:09 UTC 2019
Hi Jaikiran,
On 7/12/19 9:07 AM, Jaikiran Pai wrote:
> The new updated webrev is at
> http://cr.openjdk.java.net/~jpai/webrev/8225763/4/webrev/
I don't know if there are any custom subclasses of Inflater/Deflater
around and what they do, but hypothetically consider a subclass of
Deflater similar to this:
public class MyDeflater extends Deflater {
private final Object lock = new Object();
@Override
public void finish() {
synchronized (lock) {
super.finish();
// ... my code ...
}
}
@Override
public int deflate(byte[] output, int off, int len, int flush) {
synchronized (lock) {
// ... my code ...
int result = super.deflate(output, off, len, flush);
// ... my code ...
return result;
}
}
@Override
public int deflate(ByteBuffer output, int flush) {
synchronized (lock) {
// ... my code ...
int result = super.deflate(output, flush);
// ... my code ...
return result;
}
}
@Override
public void reset() {
synchronized (lock) {
super.reset();
// ... my code ...
}
}
@Override
public void end() {
synchronized (lock) {
super.end();
// ... my code ...
}
}
}
Currently, there's no problem with this subclass as Deflater never calls
no overridable method while holding internal lock (zsref). The newly
added close() method is different. It calls end() while holding a lock
(zsref). Suppose that close() is called from one thread, while at the
same time some other overridden method is called from another thread.
Deadlock may happen.
In your patch you are trying hard to prevent calling end() from close()
if end() has already been called (directly or indirectly through
close()) because a hypothetical subclass of Deflater that overrides
end() might not be prepared for end() to be called multiple times (i.e.
there was no specification requiring end() to be idempotent). But even
in your implementation, there is a concurrent race possible that may
allow the overridden end() method to be entered twice. Consider the
following scenario:
Thread A: calls end() explicitly
Thread B: calls close()
There's nothing preventing Thread B from entering overridden part of
end() while thread A is executing this same part of code. Threads
synchronize only at the point where the overridden end() calls super.end():
public class MyDeflater extends Deflater {
@Override
public void end() {
// ... this part of code may execute concurrently
super.end();
// ... this part of code may execute concurrently
}
Well, the subclass may have it's own synchronization in place if it is
used in a concurrent scenario, like in above hypothetical example:
@Override
public void end() {
synchronized (lock) {
// ... this part of code may execute multiple times
super.end();
// ... this part of code may execute multiple times
}
}
But such synchronization has the already mentioned dangerous property of
causing deadlock when close() is called concurrently with end(). In
addition it doesn't prevent parts of overridden end() method to be
executed multiple times.
The following implementation of close() is in no way less effective in
preventing multiple executions of parts of code in overridden end() method:
public void close() {
synchronized (zsRef) {
// check if already closed
if (zsRef.address() == 0) {
return;
}
}
// call end() out of synchronized block to prevent
// deadlock situations in concurrent scenarios
end();
}
...but it is deadlock safe.
It can be argued that synchronization in Deflater is not meant to allow
Deflater to be used from multiple threads effectively, because its API
is not multithread friendly (there are races possible that would render
such usage inpractical). I think synchronization in Deflater is only
meant to keep Deflater internal state consistent which includes state
used in native code. This is important to ensure stable operation of VM
as inconsistency of state in native code may crash the VM. There could
be intentional exploits in untrusted code that would try to crash the
VM. So this is a VM stability measure, not an API that allows effective
multithreaded usage.
In single-threaded usage (that perhaps is the only practical usage),
there are not problems with your implementation of close() as are no
problems with proposed variant above.
Regards, Peter
More information about the core-libs-dev
mailing list