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 SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj, boolean reading, boolean writing, ExecutorService executor)57 SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj, 58 boolean reading, 59 boolean writing, 60 ExecutorService executor) 61 { 62 super(fdObj, reading, writing, executor); 63 } 64 open(FileDescriptor fdo, boolean reading, boolean writing, ThreadPool pool)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 close()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 size()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 truncate(long size)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 force(boolean metaData)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 implLock(final long position, final long size, final boolean shared, final A attachment, final CompletionHandler<FileLock,? super A> handler)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 tryLock(long position, long size, boolean shared)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 implRelease(FileLockImpl fli)283 protected void implRelease(FileLockImpl fli) throws IOException { 284 nd.release(fdObj, fli.position(), fli.size()); 285 } 286 287 @Override implRead(final ByteBuffer dst, final long position, final A attachment, final CompletionHandler<Integer,? super A> handler)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 implWrite(final ByteBuffer src, final long position, final A attachment, final CompletionHandler<Integer,? super A> handler)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