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 }