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
26package sun.nio.ch;
27
28import java.nio.channels.*;
29import java.util.concurrent.*;
30import java.nio.ByteBuffer;
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33import java.io.FileDescriptor;
34import 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
41public 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