RFR JDK-8185582, Update Zip implementation to use Cleaner, not finalizers

David Holmes david.holmes at oracle.com
Wed Sep 27 09:52:42 UTC 2017


Hi Peter,

I thought Cleaner was supposed to avoid all these unpredictable 
reachability races? Otherwise why switch from using a finalizer ??

David

On 27/09/2017 7:31 PM, Peter Levart wrote:
> Hi Sherman,
> 
> At first I checked the Deflater/Inflater changes. I'll come back with 
> ZipFile later.
> 
> I think the code is mostly correct, but I have a concern. If clean() is 
> invoked via Deflater.end(), then the Deflater instance is still alive 
> and synchronization is necessary as other threads might be concurrently 
> invoking other methods. If clean() is invoked via Cleaner thread, then 
> Deflater instance is already phantom reachable and no thread may be 
> accessing it or be in the middle of executing any of its instance 
> methods. How is this guaranteed? Critical Deflater methods synchronize 
> on the zsRef instance, so at least at the beginning of the synchronized 
> block, the Deflater instance is strongly reachable. Take for example the 
> following Deflater instance method:
> 
> 
>   254     public void setDictionary(byte[] b, int off, int len) {
>   255         if (b == null) {
>   256             throw new NullPointerException();
>   257         }
>   258         if (off < 0 || len < 0 || off > b.length - len) {
>   259             throw new ArrayIndexOutOfBoundsException();
>   260         }
>   261         synchronized (zsRef) {
>   262             ensureOpen();
>   263             setDictionary(zsRef.address(), b, off, len);
>   264         }
>   265     }
> 
> 
> Up to a point where 'this' is dereferenced to obtain the 'zsRef' value 
> (line 261), the Deflater instance is reachable. But after that, even 
> ensureOpen() may be inlined and 'this' is not needed any more. After 
> that point, obtaining zsRef.address() and calling setDictionaly on the 
> obtained address may be racing with Cleaner thread invoking 
> ZStreamRef.run():
> 
>    49     public void run() {
>    50         long addr = address;
>    51         address = 0;
>    52         if (addr != 0) {
>    53             end.accept(addr);
>    54         }
>    55     }
> 
> 
> Possible program execution is therefore:
> 
> Thread1:
> 
> zsRef.address() - returning non-zero address
> 
> Thread2:
> 
> ZStreamRef.run() - invoking Deflater.end(address) with non-zero address 
> via the passed-in lambda.
> 
> Thread1:
> 
> setDictionary(<non-zero address from before>, b, off, len)
> 
> 
> To fix this, you could sprinkle Reference.reachabilityFence(this) at 
> appropriate places in Deflater, but it is simpler to just add 
> synchronized modifier to ZStreamRef.run() method. There's no danger of 
> deadlock(s) since Cleaner ensures the run() method is invoked at most once.
> 
> The same reasoning applies to Inflater to as code is similar.
> 
> A nit (Deflater and similar to Inflater):
> 
>   187         ZStreamRef ref = new ZStreamRef(init(level, 
> DEFAULT_STRATEGY, nowrap),
>   188                                         addr -> end(addr));
> 
> You could use a method reference there.
> 
> Regards, Peter
> 
> 
> 
> On 09/27/2017 08:35 AM, Xueming Shen wrote:
>> Hi,
>>
>> Please help review the change for JDK-8185582.
>>
>> issue:    https://bugs.openjdk.java.net/browse/JDK-8185582
>> webrev: http://cr.openjdk.java.net/~sherman/8185582/webrev/
>> csr:       https://bugs.openjdk.java.net/browse/JDK-8187485
>>
>> The proposed change here is to replace the finalize() cleanup 
>> mechanism with
>> the newly introduced java.lang.ref.Cleaner for java.util.zip package 
>> (Deflater,
>> Inflater, ZipFile and 
>> ZipFile.ZipFileInputStrean/ZipFileInflaterInputStream).
>>
>> Since it's an "incompatible" change, both behavioral and source 
>> incompatibility,
>> to remove the public finalize() from these classes, a corresponding 
>> CSR is also
>> created and need review as well.
>>
>> Thanks,
>> Sherman
>>
> 


More information about the core-libs-dev mailing list