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