RFR: 8168640: (fc) Avoiding AtomicBoolean in FileInput/-OutputStream improves startup

Peter Levart peter.levart at gmail.com
Thu Oct 27 05:46:17 UTC 2016


Hi David,


On 10/27/2016 04:57 AM, David Holmes wrote:
> On 25/10/2016 10:36 PM, Peter Levart wrote:
>> Hi Claes,
>>
>>
>> On 10/25/2016 01:09 PM, Aleksey Shipilev wrote:
>>> On 10/25/2016 12:51 PM, Claes Redestad wrote:
>>>> Avoiding AtomicBoolean improves startup and footprint for a sample of
>>>> small applications:
>>>>
>>>> Webrev: http://cr.openjdk.java.net/~redestad/8168640/webrev.01/
>>>> Bug: https://bugs.openjdk.java.net/browse/JDK-8168640
>>> I would generally disagree to avoid Atomics to improve startup. In this
>>> case, we always lock on close(), even for a short time. I wonder if
>>> using plain Unsafe.compareAndSwap instead of Atomic would be cleaner,
>>> instead of introducing performance bugs with synchronized-s.
>>>
>>> Thanks,
>>> -Aleksey
>>>
>>>
>>
>> In addition, there is no need for this:
>>
>>  379                     boolean closed;
>>  380                     synchronized (closeLock) {
>>  381                         closed = this.closed;
>>  382                     }
>>
>> A simple:
>>
>>         boolean closed = this.closed;
>>
>> is equivalent, since this.closed is volatile.
>
> Sorry but where exactly is the code referenced above?

It was in the previous webrev in the patched FileInputStream. But the 
webrev has been changed in-place...

Regards, Peter

>
> Thanks,
> David
>
>>
>> But something else bothers me with this code. There is a race. Here are
>> the relevant parts:
>>
>>  316     public void close() throws IOException {
>>  317         if (closed) {
>>  318             return;
>>  319         }
>>  320         synchronized (closeLock) {
>>  321             if (closed) {
>>  322                 return;
>>  323             }
>>  324             closed = true;
>>  325         }
>>  326
>>  327         FileChannel fc = channel;
>>  328         if (fc != null) {
>>  329            fc.close();
>>  330         }
>>  331
>>  332         fd.closeAll(new Closeable() {
>>  333             public void close() throws IOException {
>>  334                close0();
>>  335            }
>>  336         });
>>  337     }
>>
>> and:
>>
>>  372     public FileChannel getChannel() {
>>  373         FileChannel fc = this.channel;
>>  374         if (fc == null) {
>>  375             synchronized (this) {
>>  376                 fc = this.channel;
>>  377                 if (fc == null) {
>>  378                     this.channel = fc = FileChannelImpl.open(fd,
>> path, true, false, this);
>>  379                     boolean closed;
>>  380                     synchronized (closeLock) {
>>  381                         closed = this.closed;
>>  382                     }
>>  383                     if (closed) {
>>  384                         try {
>>  385                             fc.close();
>>  386                         } catch (IOException ioe) {
>>  387                             throw new InternalError(ioe); // should
>> not happen
>>  388                         }
>>  389                     }
>>  390                 }
>>  391             }
>>  392         }
>>  393         return fc;
>>  394     }
>>
>>
>> Suppose Thread 1 is executing close() up to line 326, Then Thread 2
>> kicks-in and executes getChannel() for the 1st time on this
>> FileInputStream. It opens FileChannelImpl and finds closed flag already
>> set, so it closes the channel. Thread 1 then resumes from line 326 and
>> finds 'channel' field already set, so it closes the channel once more.
>>
>> FileChannelImpl.close() may be idempotent, but why not making sure it is
>> called just once?
>>
>> In order to fix this, you should read the 'channel' field in close()
>> while holding closeLock and you should publish the 'channel' field in
>> getChannel() while holding closeLock. Like this:
>>
>> diff -r 2e7a303cd1ec
>> src/java.base/share/classes/java/io/FileInputStream.java
>> --- a/src/java.base/share/classes/java/io/FileInputStream.java Wed Oct
>> 19 11:45:43 2016 +0800
>> +++ b/src/java.base/share/classes/java/io/FileInputStream.java Tue Oct
>> 25 14:33:05 2016 +0200
>> @@ -26,7 +26,6 @@
>>  package java.io;
>>
>>  import java.nio.channels.FileChannel;
>> -import java.util.concurrent.atomic.AtomicBoolean;
>>  import sun.nio.ch.FileChannelImpl;
>>
>>
>> @@ -60,7 +59,9 @@
>>
>>      private volatile FileChannel channel;
>>
>> -    private final AtomicBoolean closed = new AtomicBoolean(false);
>> +    private final Object closeLock = new Object();
>> +
>> +    private volatile boolean closed;
>>
>>      /**
>>       * Creates a <code>FileInputStream</code> by
>> @@ -313,12 +314,18 @@
>>       * @spec JSR-51
>>       */
>>      public void close() throws IOException {
>> -        if (!closed.compareAndSet(false, true)) {
>> -            // if compareAndSet() returns false closed was already true
>> +        if (closed) {
>>              return;
>>          }
>> +        FileChannel fc;
>> +        synchronized (closeLock) {
>> +            if (closed) {
>> +                return;
>> +            }
>> +            closed = true;
>> +            fc = channel;
>> +        }
>>
>> -        FileChannel fc = channel;
>>          if (fc != null) {
>>             fc.close();
>>          }
>> @@ -369,8 +376,13 @@
>>              synchronized (this) {
>>                  fc = this.channel;
>>                  if (fc == null) {
>> -                    this.channel = fc = FileChannelImpl.open(fd, path,
>> true, false, this);
>> -                    if (closed.get()) {
>> +                    fc = FileChannelImpl.open(fd, path, true, false,
>> this);
>> +                    boolean closed;
>> +                    synchronized (closeLock) {
>> +                        closed = this.closed;
>> +                        this.channel = fc;
>> +                    }
>> +                    if (closed) {
>>                          try {
>>                              fc.close();
>>                          } catch (IOException ioe) {
>>
>>
>> Regards, Peter
>>
>>



More information about the core-libs-dev mailing list