jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java
changeset 2057 3acf8e5e2ca0
parent 1821 fe2556ead537
child 2441 228c040622a2
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java	Wed Feb 11 13:16:53 2009 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java	Sun Feb 15 12:25:54 2009 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,27 +26,18 @@
 package sun.nio.ch;
 
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.BufferPoolMXBean;
 import java.nio.channels.*;
-import java.nio.channels.spi.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.lang.ref.WeakReference;
-import java.lang.ref.ReferenceQueue;
 import java.lang.reflect.Field;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
 import javax.management.ObjectName;
 import javax.management.MalformedObjectNameException;
-
 import sun.misc.Cleaner;
 import sun.security.action.GetPropertyAction;
 
@@ -55,7 +46,7 @@
 {
 
     // Used to make native read and write calls
-    private static final NativeDispatcher nd;
+    private static final FileDispatcher nd;
 
     // Memory allocation size for mapping buffers
     private static final long allocationGranularity;
@@ -104,20 +95,19 @@
     // -- Standard channel operations --
 
     protected void implCloseChannel() throws IOException {
-
-        nd.preClose(fd);
-        threads.signal();
-
         // Invalidate and release any locks that we still hold
         if (fileLockTable != null) {
             fileLockTable.removeAll( new FileLockTable.Releaser() {
                 public void release(FileLock fl) throws IOException {
                     ((FileLockImpl)fl).invalidate();
-                    release0(fd, fl.position(), fl.size());
+                    nd.release(fd, fl.position(), fl.size());
                 }
             });
         }
 
+        nd.preClose(fd);
+        threads.signalAndWait();
+
         if (parent != null) {
 
             // Close the fd via the parent stream's close method.  The parent
@@ -138,12 +128,11 @@
             throw new NonReadableChannelException();
         synchronized (positionLock) {
             int n = 0;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return 0;
-                ti = threads.add();
                 do {
                     n = IOUtil.read(fd, dst, -1, nd, positionLock);
                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -162,12 +151,11 @@
             throw new NonReadableChannelException();
         synchronized (positionLock) {
             long n = 0;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return 0;
-                ti = threads.add();
                 do {
                     n = IOUtil.read(fd, dsts, nd);
                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -195,12 +183,11 @@
             throw new NonWritableChannelException();
         synchronized (positionLock) {
             int n = 0;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return 0;
-                ti = threads.add();
                 do {
                     n = IOUtil.write(fd, src, -1, nd, positionLock);
                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -219,12 +206,11 @@
             throw new NonWritableChannelException();
         synchronized (positionLock) {
             long n = 0;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return 0;
-                ti = threads.add();
                 do {
                     n = IOUtil.write(fd, srcs, nd);
                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -253,12 +239,11 @@
         ensureOpen();
         synchronized (positionLock) {
             long p = -1;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return 0;
-                ti = threads.add();
                 do {
                     p = position0(fd, -1);
                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
@@ -277,12 +262,11 @@
             throw new IllegalArgumentException();
         synchronized (positionLock) {
             long p = -1;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return null;
-                ti = threads.add();
                 do {
                     p  = position0(fd, newPosition);
                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
@@ -299,14 +283,13 @@
         ensureOpen();
         synchronized (positionLock) {
             long s = -1;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return -1;
-                ti = threads.add();
                 do {
-                    s = size0(fd);
+                    s = nd.size(fd);
                 } while ((s == IOStatus.INTERRUPTED) && isOpen());
                 return IOStatus.normalize(s);
             } finally {
@@ -328,12 +311,11 @@
         synchronized (positionLock) {
             int rv = -1;
             long p = -1;
-            int ti = -1;
+            int ti = threads.add();
             try {
                 begin();
                 if (!isOpen())
                     return null;
-                ti = threads.add();
 
                 // get current position
                 do {
@@ -345,7 +327,7 @@
 
                 // truncate file
                 do {
-                    rv = truncate0(fd, size);
+                    rv = nd.truncate(fd, size);
                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
                 if (!isOpen())
                     return null;
@@ -368,14 +350,13 @@
     public void force(boolean metaData) throws IOException {
         ensureOpen();
         int rv = -1;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return;
-            ti = threads.add();
             do {
-                rv = force0(fd, metaData);
+                rv = nd.force(fd, metaData);
             } while ((rv == IOStatus.INTERRUPTED) && isOpen());
         } finally {
             threads.remove(ti);
@@ -425,12 +406,11 @@
             return IOStatus.UNSUPPORTED;
 
         long n = -1;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return -1;
-            ti = threads.add();
             do {
                 n = transferTo0(thisFDVal, position, icount, targetFDVal);
             } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -632,12 +612,11 @@
             throw new NonReadableChannelException();
         ensureOpen();
         int n = 0;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return -1;
-            ti = threads.add();
             do {
                 n = IOUtil.read(fd, dst, position, nd, positionLock);
             } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -658,12 +637,11 @@
             throw new NonWritableChannelException();
         ensureOpen();
         int n = 0;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return -1;
-            ti = threads.add();
             do {
                 n = IOUtil.write(fd, src, position, nd, positionLock);
             } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -753,12 +731,11 @@
             throw new NonReadableChannelException();
 
         long addr = -1;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return null;
-            ti = threads.add();
             if (size() < position + size) { // Extend file size
                 if (!writable) {
                     throw new IOException("Channel not open for writing " +
@@ -766,7 +743,7 @@
                 }
                 int rv;
                 do {
-                    rv = truncate0(fd, position + size);
+                    rv = nd.truncate(fd, position + size);
                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
             }
             if (size == 0) {
@@ -860,10 +837,7 @@
 
     // -- Locks --
 
-    public static final int NO_LOCK = -1;       // Failed to lock
-    public static final int LOCKED = 0;         // Obtained requested lock
-    public static final int RET_EX_LOCK = 1;    // Obtained exclusive lock
-    public static final int INTERRUPTED = 2;    // Request interrupted
+
 
     // keeps track of locks on this file
     private volatile FileLockTable fileLockTable;
@@ -893,12 +867,21 @@
         return isSharedFileLockTable;
     }
 
-    private FileLockTable fileLockTable() {
+    private FileLockTable fileLockTable() throws IOException {
         if (fileLockTable == null) {
             synchronized (this) {
                 if (fileLockTable == null) {
-                    fileLockTable = isSharedFileLockTable() ?
-                        new SharedFileLockTable(this) : new SimpleFileLockTable();
+                    if (isSharedFileLockTable()) {
+                        int ti = threads.add();
+                        try {
+                            ensureOpen();
+                            fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
+                        } finally {
+                            threads.remove(ti);
+                        }
+                    } else {
+                        fileLockTable = new SimpleFileLockTable();
+                    }
                 }
             }
         }
@@ -917,21 +900,20 @@
         FileLockTable flt = fileLockTable();
         flt.add(fli);
         boolean i = true;
-        int ti = -1;
+        int ti = threads.add();
         try {
             begin();
             if (!isOpen())
                 return null;
-            ti = threads.add();
-            int result = lock0(fd, true, position, size, shared);
-            if (result == RET_EX_LOCK) {
+            int result = nd.lock(fd, true, position, size, shared);
+            if (result == FileDispatcher.RET_EX_LOCK) {
                 assert shared;
                 FileLockImpl fli2 = new FileLockImpl(this, position, size,
                                                      false);
                 flt.replace(fli, fli2);
                 return fli2;
             }
-            if (result == INTERRUPTED || result == NO_LOCK) {
+            if (result == FileDispatcher.INTERRUPTED || result == FileDispatcher.NO_LOCK) {
                 flt.remove(fli);
                 i = false;
             }
@@ -960,77 +942,54 @@
         FileLockImpl fli = new FileLockImpl(this, position, size, shared);
         FileLockTable flt = fileLockTable();
         flt.add(fli);
-        int result = lock0(fd, false, position, size, shared);
-        if (result == NO_LOCK) {
-            flt.remove(fli);
-            return null;
+        int result;
+
+        int ti = threads.add();
+        try {
+            try {
+                ensureOpen();
+                result = nd.lock(fd, false, position, size, shared);
+            } catch (IOException e) {
+                flt.remove(fli);
+                throw e;
+            }
+            if (result == FileDispatcher.NO_LOCK) {
+                flt.remove(fli);
+                return null;
+            }
+            if (result == FileDispatcher.RET_EX_LOCK) {
+                assert shared;
+                FileLockImpl fli2 = new FileLockImpl(this, position, size,
+                                                     false);
+                flt.replace(fli, fli2);
+                return fli2;
+            }
+            return fli;
+        } finally {
+            threads.remove(ti);
         }
-        if (result == RET_EX_LOCK) {
-            assert shared;
-            FileLockImpl fli2 = new FileLockImpl(this, position, size,
-                                                 false);
-            flt.replace(fli, fli2);
-            return fli2;
-        }
-        return fli;
     }
 
     void release(FileLockImpl fli) throws IOException {
         ensureOpen();
-        release0(fd, fli.position(), fli.size());
+        int ti = threads.add();
+        try {
+            ensureOpen();
+            nd.release(fd, fli.position(), fli.size());
+        } finally {
+            threads.remove(ti);
+        }
         assert fileLockTable != null;
         fileLockTable.remove(fli);
     }
 
-
-    // -- File lock support  --
-
-    /**
-     * A table of FileLocks.
-     */
-    private interface FileLockTable {
-        /**
-         * Adds a file lock to the table.
-         *
-         * @throws OverlappingFileLockException if the file lock overlaps
-         *         with an existing file lock in the table
-         */
-        void add(FileLock fl) throws OverlappingFileLockException;
-
-        /**
-         * Remove an existing file lock from the table.
-         */
-        void remove(FileLock fl);
-
-        /**
-         * An implementation of this interface releases a given file lock.
-         * Used with removeAll.
-         */
-        interface Releaser {
-            void release(FileLock fl) throws IOException;
-        }
-
-        /**
-         * Removes all file locks from the table.
-         * <p>
-         * The Releaser#release method is invoked for each file lock before
-         * it is removed.
-         *
-         * @throws IOException if the release method throws IOException
-         */
-        void removeAll(Releaser r) throws IOException;
-
-        /**
-         * Replaces an existing file lock in the table.
-         */
-        void replace(FileLock fl1, FileLock fl2);
-    }
+    // -- File lock support --
 
     /**
      * A simple file lock table that maintains a list of FileLocks obtained by a
      * FileChannel. Use to get 1.4/5.0 behaviour.
      */
-    private static class SimpleFileLockTable implements FileLockTable {
+    private static class SimpleFileLockTable extends FileLockTable {
         // synchronize on list for access
         private List<FileLock> lockList = new ArrayList<FileLock>(2);
 
@@ -1080,207 +1039,8 @@
         }
     }
 
-    /**
-     * A weak reference to a FileLock.
-     * <p>
-     * SharedFileLockTable uses a list of file lock references to avoid keeping the
-     * FileLock (and FileChannel) alive.
-     */
-    private static class FileLockReference extends WeakReference<FileLock> {
-        private FileKey fileKey;
-
-        FileLockReference(FileLock referent,
-                          ReferenceQueue<FileLock> queue,
-                          FileKey key) {
-            super(referent, queue);
-            this.fileKey = key;
-        }
-
-        private FileKey fileKey() {
-            return fileKey;
-        }
-    }
-
-    /**
-     * A file lock table that is over a system-wide map of all file locks.
-     */
-    private static class SharedFileLockTable implements FileLockTable {
-        // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
-        // The map value is a list of file locks represented by FileLockReferences.
-        // All access to the list must be synchronized on the list.
-        private static ConcurrentHashMap<FileKey, ArrayList<FileLockReference>> lockMap =
-            new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
-
-        // reference queue for cleared refs
-        private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
-
-        // the enclosing file channel
-        private FileChannelImpl fci;
-
-        // File key for the file that this channel is connected to
-        private FileKey fileKey;
-
-        public SharedFileLockTable(FileChannelImpl fci) {
-            this.fci = fci;
-            this.fileKey = FileKey.create(fci.fd);
-        }
-
-        public void add(FileLock fl) throws OverlappingFileLockException {
-            ArrayList<FileLockReference> list = lockMap.get(fileKey);
-
-            for (;;) {
-
-                // The key isn't in the map so we try to create it atomically
-                if (list == null) {
-                    list = new ArrayList<FileLockReference>(2);
-                    ArrayList<FileLockReference> prev;
-                    synchronized (list) {
-                        prev = lockMap.putIfAbsent(fileKey, list);
-                        if (prev == null) {
-                            // we successfully created the key so we add the file lock
-                            list.add(new FileLockReference(fl, queue, fileKey));
-                            break;
-                        }
-                    }
-                    // someone else got there first
-                    list = prev;
-                }
-
-                // There is already a key. It is possible that some other thread
-                // is removing it so we re-fetch the value from the map. If it
-                // hasn't changed then we check the list for overlapping locks
-                // and add the new lock to the list.
-                synchronized (list) {
-                    ArrayList<FileLockReference> current = lockMap.get(fileKey);
-                    if (list == current) {
-                        checkList(list, fl.position(), fl.size());
-                        list.add(new FileLockReference(fl, queue, fileKey));
-                        break;
-                    }
-                    list = current;
-                }
-
-            }
-
-            // process any stale entries pending in the reference queue
-            removeStaleEntries();
-        }
-
-        private void removeKeyIfEmpty(FileKey fk, ArrayList<FileLockReference> list) {
-            assert Thread.holdsLock(list);
-            assert lockMap.get(fk) == list;
-            if (list.isEmpty()) {
-                lockMap.remove(fk);
-            }
-        }
-
-        public void remove(FileLock fl) {
-            assert fl != null;
-
-            // the lock must exist so the list of locks must be present
-            ArrayList<FileLockReference> list = lockMap.get(fileKey);
-            assert list != null;
-
-            synchronized (list) {
-                int index = 0;
-                while (index < list.size()) {
-                    FileLockReference ref = list.get(index);
-                    FileLock lock = ref.get();
-                    if (lock == fl) {
-                        assert (lock != null) && (lock.channel() == fci);
-                        ref.clear();
-                        list.remove(index);
-                        break;
-                    }
-                    index++;
-                }
-            }
-        }
-
-        public void removeAll(Releaser releaser) throws IOException {
-            ArrayList<FileLockReference> list = lockMap.get(fileKey);
-            if (list != null) {
-                synchronized (list) {
-                    int index = 0;
-                    while (index < list.size()) {
-                        FileLockReference ref = list.get(index);
-                        FileLock lock = ref.get();
-
-                        // remove locks obtained by this channel
-                        if (lock != null && lock.channel() == fci) {
-                            // invoke the releaser to invalidate/release the lock
-                            releaser.release(lock);
-
-                            // remove the lock from the list
-                            ref.clear();
-                            list.remove(index);
-                        } else {
-                            index++;
-                        }
-                    }
-
-                    // once the lock list is empty we remove it from the map
-                    removeKeyIfEmpty(fileKey, list);
-                }
-            }
-        }
-
-        public void replace(FileLock fromLock, FileLock toLock) {
-            // the lock must exist so there must be a list
-            ArrayList<FileLockReference> list = lockMap.get(fileKey);
-            assert list != null;
-
-            synchronized (list) {
-                for (int index=0; index<list.size(); index++) {
-                    FileLockReference ref = list.get(index);
-                    FileLock lock = ref.get();
-                    if (lock == fromLock) {
-                        ref.clear();
-                        list.set(index, new FileLockReference(toLock, queue, fileKey));
-                        break;
-                    }
-                }
-            }
-        }
-
-        // Check for overlapping file locks
-        private void checkList(List<FileLockReference> list, long position, long size)
-            throws OverlappingFileLockException
-        {
-            assert Thread.holdsLock(list);
-            for (FileLockReference ref: list) {
-                FileLock fl = ref.get();
-                if (fl != null && fl.overlaps(position, size))
-                    throw new OverlappingFileLockException();
-            }
-        }
-
-        // Process the reference queue
-        private void removeStaleEntries() {
-            FileLockReference ref;
-            while ((ref = (FileLockReference)queue.poll()) != null) {
-                FileKey fk = ref.fileKey();
-                ArrayList<FileLockReference> list = lockMap.get(fk);
-                if (list != null) {
-                    synchronized (list) {
-                        list.remove(ref);
-                        removeKeyIfEmpty(fk, list);
-                    }
-                }
-            }
-        }
-    }
-
     // -- Native methods --
 
-    // Grabs a file lock
-    native int lock0(FileDescriptor fd, boolean blocking, long pos, long size,
-                     boolean shared) throws IOException;
-
-    // Releases a file lock
-    native void release0(FileDescriptor fd, long pos, long size)
-        throws IOException;
-
     // Creates a new mapping
     private native long map0(int prot, long position, long length)
         throws IOException;
@@ -1288,12 +1048,6 @@
     // Removes an existing mapping
     private static native int unmap0(long address, long length);
 
-    // Forces output to device
-    private native int force0(FileDescriptor fd, boolean metaData);
-
-    // Truncates a file
-    private native int truncate0(FileDescriptor fd, long size);
-
     // Transfers from src to dst, or returns -2 if kernel can't do that
     private native long transferTo0(int src, long position, long count, int dst);
 
@@ -1302,16 +1056,13 @@
     // otherwise the position is set to offset
     private native long position0(FileDescriptor fd, long offset);
 
-    // Reports this file's size
-    private native long size0(FileDescriptor fd);
-
     // Caches fieldIDs
     private static native long initIDs();
 
     static {
         Util.load();
         allocationGranularity = initIDs();
-        nd = new FileDispatcher();
+        nd = new FileDispatcherImpl();
     }
 
 }