1 /*
   2  * Copyright (c) 2008, 2009, 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         nd.preClose(fdObj);
  92         threads.signalAndWait();
  93 
  94         // wait until all async I/O operations have completely gracefully
  95         closeLock.writeLock().lock();
  96         try {
  97             // do nothing
  98         } finally {
  99             closeLock.writeLock().unlock();
 100         }
 101 
 102         // close file
 103         nd.close(fdObj);
 104     }
 105 
 106     @Override
 107     public long size() throws IOException {
 108         int ti = threads.add();
 109         try {
 110             long n = 0L;
 111             try {
 112                 begin();
 113                 do {
 114                     n = nd.size(fdObj);
 115                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 116                 return n;
 117             } finally {
 118                 end(n >= 0L);
 119             }
 120         } finally {
 121             threads.remove(ti);
 122         }
 123     }
 124 
 125     @Override
 126     public AsynchronousFileChannel truncate(long size) throws IOException {
 127         if (size < 0L)
 128             throw new IllegalArgumentException("Negative size");
 129         if (!writing)
 130             throw new NonWritableChannelException();
 131         int ti = threads.add();
 132         try {
 133             long n = 0L;
 134             try {
 135                 begin();
 136                 do {
 137                     n = nd.size(fdObj);
 138                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 139 
 140                 // truncate file if 'size' less than current size
 141                 if (size < n && isOpen()) {
 142                     do {
 143                         n = nd.truncate(fdObj, size);
 144                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
 145                 }
 146                 return this;
 147             } finally {
 148                 end(n > 0);
 149             }
 150         } finally {
 151             threads.remove(ti);
 152         }
 153     }
 154 
 155     @Override
 156     public void force(boolean metaData) throws IOException {
 157         int ti = threads.add();
 158         try {
 159             int n = 0;
 160             try {
 161                 begin();
 162                 do {
 163                     n = nd.force(fdObj, metaData);
 164                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 165             } finally {
 166                 end(n >= 0);
 167             }
 168         } finally {
 169             threads.remove(ti);
 170         }
 171     }
 172 
 173     @Override
 174     <A> Future<FileLock> implLock(final long position,
 175                                   final long size,
 176                                   final boolean shared,
 177                                   final A attachment,
 178                                   final CompletionHandler<FileLock,? super A> handler)
 179     {
 180         if (shared && !reading)
 181             throw new NonReadableChannelException();
 182         if (!shared && !writing)
 183             throw new NonWritableChannelException();
 184 
 185         // add to lock table
 186         final FileLockImpl fli = addToFileLockTable(position, size, shared);
 187         if (fli == null) {
 188             Throwable exc = new ClosedChannelException();
 189             if (handler == null)
 190                 return CompletedFuture.withFailure(exc);
 191             Invoker.invokeIndirectly(handler, attachment, null, exc, executor);
 192             return null;
 193         }
 194 
 195         final PendingFuture<FileLock,A> result = (handler == null) ?
 196             new PendingFuture<FileLock,A>(this) : null;
 197         Runnable task = new Runnable() {
 198             public void run() {
 199                 Throwable exc = null;
 200 
 201                 int ti = threads.add();
 202                 try {
 203                     int n;
 204                     try {
 205                         begin();
 206                         do {
 207                             n = nd.lock(fdObj, true, position, size, shared);
 208                         } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
 209                         if (n != FileDispatcher.LOCKED || !isOpen()) {
 210                             throw new AsynchronousCloseException();
 211                         }
 212                     } catch (IOException x) {
 213                         removeFromFileLockTable(fli);
 214                         if (!isOpen())
 215                             x = new AsynchronousCloseException();
 216                         exc = x;
 217                     } finally {
 218                         end();
 219                     }
 220                 } finally {
 221                     threads.remove(ti);
 222                 }
 223                 if (handler == null) {
 224                     result.setResult(fli, exc);
 225                 } else {
 226                     Invoker.invokeUnchecked(handler, attachment, fli, exc);
 227                 }
 228             }
 229         };
 230         boolean executed = false;
 231         try {
 232             executor.execute(task);
 233             executed = true;
 234         } finally {
 235             if (!executed) {
 236                 // rollback
 237                 removeFromFileLockTable(fli);
 238             }
 239         }
 240         return result;
 241     }
 242 
 243     @Override
 244     public FileLock tryLock(long position, long size, boolean shared)
 245         throws IOException
 246     {
 247         if (shared && !reading)
 248             throw new NonReadableChannelException();
 249         if (!shared && !writing)
 250             throw new NonWritableChannelException();
 251 
 252         // add to lock table
 253         FileLockImpl fli = addToFileLockTable(position, size, shared);
 254         if (fli == null)
 255             throw new ClosedChannelException();
 256 
 257         int ti = threads.add();
 258         boolean gotLock = false;
 259         try {
 260             begin();
 261             int n;
 262             do {
 263                 n = nd.lock(fdObj, false, position, size, shared);
 264             } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
 265             if (n == FileDispatcher.LOCKED && isOpen()) {
 266                 gotLock = true;
 267                 return fli;    // lock acquired
 268             }
 269             if (n == FileDispatcher.NO_LOCK)
 270                 return null;    // locked by someone else
 271             if (n == FileDispatcher.INTERRUPTED)
 272                 throw new AsynchronousCloseException();
 273             // should not get here
 274             throw new AssertionError();
 275         } finally {
 276             if (!gotLock)
 277                 removeFromFileLockTable(fli);
 278             end();
 279             threads.remove(ti);
 280         }
 281     }
 282 
 283     @Override
 284     protected void implRelease(FileLockImpl fli) throws IOException {
 285         nd.release(fdObj, fli.position(), fli.size());
 286     }
 287 
 288     @Override
 289     <A> Future<Integer> implRead(final ByteBuffer dst,
 290                                  final long position,
 291                                  final A attachment,
 292                                  final CompletionHandler<Integer,? super A> handler)
 293     {
 294         if (position < 0)
 295             throw new IllegalArgumentException("Negative position");
 296         if (!reading)
 297             throw new NonReadableChannelException();
 298         if (dst.isReadOnly())
 299             throw new IllegalArgumentException("Read-only buffer");
 300 
 301         // complete immediately if channel closed or no space remaining
 302         if (!isOpen() || (dst.remaining() == 0)) {
 303             Throwable exc = (isOpen()) ? null : new ClosedChannelException();
 304             if (handler == null)
 305                 return CompletedFuture.withResult(0, exc);
 306             Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
 307             return null;
 308         }
 309 
 310         final PendingFuture<Integer,A> result = (handler == null) ?
 311             new PendingFuture<Integer,A>(this) : null;
 312         Runnable task = new Runnable() {
 313             public void run() {
 314                 int n = 0;
 315                 Throwable exc = null;
 316 
 317                 int ti = threads.add();
 318                 try {
 319                     begin();
 320                     do {
 321                         n = IOUtil.read(fdObj, dst, position, nd);
 322                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
 323                     if (n < 0 && !isOpen())
 324                         throw new AsynchronousCloseException();
 325                 } catch (IOException x) {
 326                     if (!isOpen())
 327                         x = new AsynchronousCloseException();
 328                     exc = x;
 329                 } finally {
 330                     end();
 331                     threads.remove(ti);
 332                 }
 333                 if (handler == null) {
 334                     result.setResult(n, exc);
 335                 } else {
 336                     Invoker.invokeUnchecked(handler, attachment, n, exc);
 337                 }
 338             }
 339         };
 340         executor.execute(task);
 341         return result;
 342     }
 343 
 344     @Override
 345     <A> Future<Integer> implWrite(final ByteBuffer src,
 346                                   final long position,
 347                                   final A attachment,
 348                                   final CompletionHandler<Integer,? super A> handler)
 349     {
 350         if (position < 0)
 351             throw new IllegalArgumentException("Negative position");
 352         if (!writing)
 353             throw new NonWritableChannelException();
 354 
 355         // complete immediately if channel is closed or no bytes remaining
 356         if (!isOpen() || (src.remaining() == 0)) {
 357             Throwable exc = (isOpen()) ? null : new ClosedChannelException();
 358             if (handler == null)
 359                 return CompletedFuture.withResult(0, exc);
 360             Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
 361             return null;
 362         }
 363 
 364         final PendingFuture<Integer,A> result = (handler == null) ?
 365             new PendingFuture<Integer,A>(this) : null;
 366         Runnable task = new Runnable() {
 367             public void run() {
 368                 int n = 0;
 369                 Throwable exc = null;
 370 
 371                 int ti = threads.add();
 372                 try {
 373                     begin();
 374                     do {
 375                         n = IOUtil.write(fdObj, src, position, nd);
 376                     } while ((n == IOStatus.INTERRUPTED) && isOpen());
 377                     if (n < 0 && !isOpen())
 378                         throw new AsynchronousCloseException();
 379                 } catch (IOException x) {
 380                     if (!isOpen())
 381                         x = new AsynchronousCloseException();
 382                     exc = x;
 383                 } finally {
 384                     end();
 385                     threads.remove(ti);
 386                 }
 387                 if (handler == null) {
 388                     result.setResult(n, exc);
 389                 } else {
 390                     Invoker.invokeUnchecked(handler, attachment, n, exc);
 391                 }
 392             }
 393         };
 394         executor.execute(task);
 395         return result;
 396     }
 397 }