RFR: 8168640: (fc) Avoiding AtomicBoolean in FileInput/-OutputStream improves startup
Claes Redestad
claes.redestad at oracle.com
Tue Oct 25 12:57:51 UTC 2016
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
More information about the core-libs-dev
mailing list