Home | History | Annotate | Download | only in ch
      1 /*
      2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.nio.ch;
     27 
     28 import java.nio.channels.*;
     29 import java.util.concurrent.*;
     30 import java.nio.ByteBuffer;
     31 import java.security.AccessController;
     32 import java.security.PrivilegedAction;
     33 import java.io.FileDescriptor;
     34 import java.io.IOException;
     35 
     36 /**
     37  * "Portable" implementation of AsynchronousFileChannel for use on operating
     38  * systems that don't support asynchronous file I/O.
     39  */
     40 
     41 public class SimpleAsynchronousFileChannelImpl
     42     extends AsynchronousFileChannelImpl
     43 {
     44     // lazy initialization of default thread pool for file I/O
     45     private static class DefaultExecutorHolder {
     46         static final ExecutorService defaultExecutor =
     47             ThreadPool.createDefault().executor();
     48     }
     49 
     50     // Used to make native read and write calls
     51     private static final FileDispatcher nd = new FileDispatcherImpl();
     52 
     53     // Thread-safe set of IDs of native threads, for signalling
     54     private final NativeThreadSet threads = new NativeThreadSet(2);
     55 
     56 
     57     SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj,
     58                                       boolean reading,
     59                                       boolean writing,
     60                                       ExecutorService executor)
     61     {
     62         super(fdObj, reading, writing, executor);
     63     }
     64 
     65     public static AsynchronousFileChannel open(FileDescriptor fdo,
     66                                                boolean reading,
     67                                                boolean writing,
     68                                                ThreadPool pool)
     69     {
     70         // Executor is either default or based on pool parameters
     71         ExecutorService executor = (pool == null) ?
     72             DefaultExecutorHolder.defaultExecutor : pool.executor();
     73         return new SimpleAsynchronousFileChannelImpl(fdo, reading, writing, executor);
     74     }
     75 
     76     @Override
     77     public void close() throws IOException {
     78         // mark channel as closed
     79         synchronized (fdObj) {
     80             if (closed)
     81                 return;     // already closed
     82             closed = true;
     83             // from this point on, if another thread invokes the begin() method
     84             // then it will throw ClosedChannelException
     85         }
     86 
     87         // Invalidate and release any locks that we still hold
     88         invalidateAllLocks();
     89 
     90         // signal any threads blocked on this channel
     91         threads.signalAndWait();
     92 
     93         // wait until all async I/O operations have completely gracefully
     94         closeLock.writeLock().lock();
     95         try {
     96             // do nothing
     97         } finally {
     98             closeLock.writeLock().unlock();
     99         }
    100 
    101         // close file
    102         nd.close(fdObj);
    103     }
    104 
    105     @Override
    106     public long size() throws IOException {
    107         int ti = threads.add();
    108         try {
    109             long n = 0L;
    110             try {
    111                 begin();
    112                 do {
    113                     n = nd.size(fdObj);
    114                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
    115                 return n;
    116             } finally {
    117                 end(n >= 0L);
    118             }
    119         } finally {
    120             threads.remove(ti);
    121         }
    122     }
    123 
    124     @Override
    125     public AsynchronousFileChannel truncate(long size) throws IOException {
    126         if (size < 0L)
    127             throw new IllegalArgumentException("Negative size");
    128         if (!writing)
    129             throw new NonWritableChannelException();
    130         int ti = threads.add();
    131         try {
    132             long n = 0L;
    133             try {
    134                 begin();
    135                 do {
    136                     n = nd.size(fdObj);
    137                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
    138 
    139                 // truncate file if 'size' less than current size
    140                 if (size < n && isOpen()) {
    141                     do {
    142                         n = nd.truncate(fdObj, size);
    143                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
    144                 }
    145                 return this;
    146             } finally {
    147                 end(n > 0);
    148             }
    149         } finally {
    150             threads.remove(ti);
    151         }
    152     }
    153 
    154     @Override
    155     public void force(boolean metaData) throws IOException {
    156         int ti = threads.add();
    157         try {
    158             int n = 0;
    159             try {
    160                 begin();
    161                 do {
    162                     n = nd.force(fdObj, metaData);
    163                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
    164             } finally {
    165                 end(n >= 0);
    166             }
    167         } finally {
    168             threads.remove(ti);
    169         }
    170     }
    171 
    172     @Override
    173     <A> Future<FileLock> implLock(final long position,
    174                                   final long size,
    175                                   final boolean shared,
    176                                   final A attachment,
    177                                   final CompletionHandler<FileLock,? super A> handler)
    178     {
    179         if (shared && !reading)
    180             throw new NonReadableChannelException();
    181         if (!shared && !writing)
    182             throw new NonWritableChannelException();
    183 
    184         // add to lock table
    185         final FileLockImpl fli = addToFileLockTable(position, size, shared);
    186         if (fli == null) {
    187             Throwable exc = new ClosedChannelException();
    188             if (handler == null)
    189                 return CompletedFuture.withFailure(exc);
    190             Invoker.invokeIndirectly(handler, attachment, null, exc, executor);
    191             return null;
    192         }
    193 
    194         final PendingFuture<FileLock,A> result = (handler == null) ?
    195             new PendingFuture<FileLock,A>(this) : null;
    196         Runnable task = new Runnable() {
    197             public void run() {
    198                 Throwable exc = null;
    199 
    200                 int ti = threads.add();
    201                 try {
    202                     int n;
    203                     try {
    204                         begin();
    205                         do {
    206                             n = nd.lock(fdObj, true, position, size, shared);
    207                         } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
    208                         if (n != FileDispatcher.LOCKED || !isOpen()) {
    209                             throw new AsynchronousCloseException();
    210                         }
    211                     } catch (IOException x) {
    212                         removeFromFileLockTable(fli);
    213                         if (!isOpen())
    214                             x = new AsynchronousCloseException();
    215                         exc = x;
    216                     } finally {
    217                         end();
    218                     }
    219                 } finally {
    220                     threads.remove(ti);
    221                 }
    222                 if (handler == null) {
    223                     result.setResult(fli, exc);
    224                 } else {
    225                     Invoker.invokeUnchecked(handler, attachment, fli, exc);
    226                 }
    227             }
    228         };
    229         boolean executed = false;
    230         try {
    231             executor.execute(task);
    232             executed = true;
    233         } finally {
    234             if (!executed) {
    235                 // rollback
    236                 removeFromFileLockTable(fli);
    237             }
    238         }
    239         return result;
    240     }
    241 
    242     @Override
    243     public FileLock tryLock(long position, long size, boolean shared)
    244         throws IOException
    245     {
    246         if (shared && !reading)
    247             throw new NonReadableChannelException();
    248         if (!shared && !writing)
    249             throw new NonWritableChannelException();
    250 
    251         // add to lock table
    252         FileLockImpl fli = addToFileLockTable(position, size, shared);
    253         if (fli == null)
    254             throw new ClosedChannelException();
    255 
    256         int ti = threads.add();
    257         boolean gotLock = false;
    258         try {
    259             begin();
    260             int n;
    261             do {
    262                 n = nd.lock(fdObj, false, position, size, shared);
    263             } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
    264             if (n == FileDispatcher.LOCKED && isOpen()) {
    265                 gotLock = true;
    266                 return fli;    // lock acquired
    267             }
    268             if (n == FileDispatcher.NO_LOCK)
    269                 return null;    // locked by someone else
    270             if (n == FileDispatcher.INTERRUPTED)
    271                 throw new AsynchronousCloseException();
    272             // should not get here
    273             throw new AssertionError();
    274         } finally {
    275             if (!gotLock)
    276                 removeFromFileLockTable(fli);
    277             end();
    278             threads.remove(ti);
    279         }
    280     }
    281 
    282     @Override
    283     protected void implRelease(FileLockImpl fli) throws IOException {
    284         nd.release(fdObj, fli.position(), fli.size());
    285     }
    286 
    287     @Override
    288     <A> Future<Integer> implRead(final ByteBuffer dst,
    289                                  final long position,
    290                                  final A attachment,
    291                                  final CompletionHandler<Integer,? super A> handler)
    292     {
    293         if (position < 0)
    294             throw new IllegalArgumentException("Negative position");
    295         if (!reading)
    296             throw new NonReadableChannelException();
    297         if (dst.isReadOnly())
    298             throw new IllegalArgumentException("Read-only buffer");
    299 
    300         // complete immediately if channel closed or no space remaining
    301         if (!isOpen() || (dst.remaining() == 0)) {
    302             Throwable exc = (isOpen()) ? null : new ClosedChannelException();
    303             if (handler == null)
    304                 return CompletedFuture.withResult(0, exc);
    305             Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
    306             return null;
    307         }
    308 
    309         final PendingFuture<Integer,A> result = (handler == null) ?
    310             new PendingFuture<Integer,A>(this) : null;
    311         Runnable task = new Runnable() {
    312             public void run() {
    313                 int n = 0;
    314                 Throwable exc = null;
    315 
    316                 int ti = threads.add();
    317                 try {
    318                     begin();
    319                     do {
    320                         n = IOUtil.read(fdObj, dst, position, nd);
    321                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
    322                     if (n < 0 && !isOpen())
    323                         throw new AsynchronousCloseException();
    324                 } catch (IOException x) {
    325                     if (!isOpen())
    326                         x = new AsynchronousCloseException();
    327                     exc = x;
    328                 } finally {
    329                     end();
    330                     threads.remove(ti);
    331                 }
    332                 if (handler == null) {
    333                     result.setResult(n, exc);
    334                 } else {
    335                     Invoker.invokeUnchecked(handler, attachment, n, exc);
    336                 }
    337             }
    338         };
    339         executor.execute(task);
    340         return result;
    341     }
    342 
    343     @Override
    344     <A> Future<Integer> implWrite(final ByteBuffer src,
    345                                   final long position,
    346                                   final A attachment,
    347                                   final CompletionHandler<Integer,? super A> handler)
    348     {
    349         if (position < 0)
    350             throw new IllegalArgumentException("Negative position");
    351         if (!writing)
    352             throw new NonWritableChannelException();
    353 
    354         // complete immediately if channel is closed or no bytes remaining
    355         if (!isOpen() || (src.remaining() == 0)) {
    356             Throwable exc = (isOpen()) ? null : new ClosedChannelException();
    357             if (handler == null)
    358                 return CompletedFuture.withResult(0, exc);
    359             Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
    360             return null;
    361         }
    362 
    363         final PendingFuture<Integer,A> result = (handler == null) ?
    364             new PendingFuture<Integer,A>(this) : null;
    365         Runnable task = new Runnable() {
    366             public void run() {
    367                 int n = 0;
    368                 Throwable exc = null;
    369 
    370                 int ti = threads.add();
    371                 try {
    372                     begin();
    373                     do {
    374                         n = IOUtil.write(fdObj, src, position, nd);
    375                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
    376                     if (n < 0 && !isOpen())
    377                         throw new AsynchronousCloseException();
    378                 } catch (IOException x) {
    379                     if (!isOpen())
    380                         x = new AsynchronousCloseException();
    381                     exc = x;
    382                 } finally {
    383                     end();
    384                     threads.remove(ti);
    385                 }
    386                 if (handler == null) {
    387                     result.setResult(n, exc);
    388                 } else {
    389                     Invoker.invokeUnchecked(handler, attachment, n, exc);
    390                 }
    391             }
    392         };
    393         executor.execute(task);
    394         return result;
    395     }
    396 }
    397