On 2016-10-25 14:36, 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.

Good point, cumulative diff to both files:

                 fc = this.channel;
                 if (fc == null) {
this.channel = fc = FileChannelImpl.open(fd, path, false, true, this);
-                    boolean closed;
-                    synchronized (closeLock) {
-                        closed = this.closed;
-                    }
                     if (closed) {
                         try {
                             fc.close();


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?

Hmm, it would seem like a pre-existing issue that was not dealt with neither before nor after JDK-8025619, no?

And FileChannel inherits AbstractInterruptibleChannel::close() (public final), which specifies behavior: "If the channel has already been closed then this method returns immediately." Thus I don't think the extra ceremony is warranted, won't you agree?

Thanks!

/Claes

Reply via email to