495 lines · 16.8 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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 * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.ch; import java.lang.ref.SoftReference;import java.lang.reflect.*;import java.io.IOException;import java.io.FileDescriptor;import java.nio.ByteBuffer;import java.nio.MappedByteBuffer;import java.nio.channels.*;import java.security.AccessController;import java.security.PrivilegedAction;import java.util.*;import sun.misc.Unsafe;import sun.misc.Cleaner;import sun.security.action.GetPropertyAction;  class Util {     // -- Caches --     // The number of temp buffers in our pool    private static final int TEMP_BUF_POOL_SIZE = 8;     // Per-thread cache of temporary direct buffers    private static ThreadLocal<BufferCache> bufferCache =        new ThreadLocal<BufferCache>()    {        @Override        protected BufferCache initialValue() {            return new BufferCache();        }    };     /**     * A simple cache of direct buffers.     */    private static class BufferCache {        // the array of buffers        private ByteBuffer[] buffers;         // the number of buffers in the cache        private int count;         // the index of the first valid buffer (undefined if count == 0)        private int start;         private int next(int i) {            return (i + 1) % TEMP_BUF_POOL_SIZE;        }         BufferCache() {            buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];        }         /**         * Removes and returns a buffer from the cache of at least the given         * size (or null if no suitable buffer is found).         */        ByteBuffer get(int size) {            if (count == 0)                return null;  // cache is empty             ByteBuffer[] buffers = this.buffers;             // search for suitable buffer (often the first buffer will do)            ByteBuffer buf = buffers[start];            if (buf.capacity() < size) {                buf = null;                int i = start;                while ((i = next(i)) != start) {                    ByteBuffer bb = buffers[i];                    if (bb == null)                        break;                    if (bb.capacity() >= size) {                        buf = bb;                        break;                    }                }                if (buf == null)                    return null;                // move first element to here to avoid re-packing                buffers[i] = buffers[start];            }             // remove first element            buffers[start] = null;            start = next(start);            count--;             // prepare the buffer and return it            buf.rewind();            buf.limit(size);            return buf;        }         boolean offerFirst(ByteBuffer buf) {            if (count >= TEMP_BUF_POOL_SIZE) {                return false;            } else {                start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;                buffers[start] = buf;                count++;                return true;            }        }         boolean offerLast(ByteBuffer buf) {            if (count >= TEMP_BUF_POOL_SIZE) {                return false;            } else {                int next = (start + count) % TEMP_BUF_POOL_SIZE;                buffers[next] = buf;                count++;                return true;            }        }         boolean isEmpty() {            return count == 0;        }         ByteBuffer removeFirst() {            assert count > 0;            ByteBuffer buf = buffers[start];            buffers[start] = null;            start = next(start);            count--;            return buf;        }    }     /**     * Returns a temporary buffer of at least the given size     */    static ByteBuffer getTemporaryDirectBuffer(int size) {        BufferCache cache = bufferCache.get();        ByteBuffer buf = cache.get(size);        if (buf != null) {            return buf;        } else {            // No suitable buffer in the cache so we need to allocate a new            // one. To avoid the cache growing then we remove the first            // buffer from the cache and free it.            if (!cache.isEmpty()) {                buf = cache.removeFirst();                free(buf);            }            return ByteBuffer.allocateDirect(size);        }    }     /**     * Releases a temporary buffer by returning to the cache or freeing it.     */    static void releaseTemporaryDirectBuffer(ByteBuffer buf) {        offerFirstTemporaryDirectBuffer(buf);    }     /**     * Releases a temporary buffer by returning to the cache or freeing it. If     * returning to the cache then insert it at the start so that it is     * likely to be returned by a subsequent call to getTemporaryDirectBuffer.     */    static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {        assert buf != null;        BufferCache cache = bufferCache.get();        if (!cache.offerFirst(buf)) {            // cache is full            free(buf);        }    }     /**     * Releases a temporary buffer by returning to the cache or freeing it. If     * returning to the cache then insert it at the end. This makes it     * suitable for scatter/gather operations where the buffers are returned to     * cache in same order that they were obtained.     */    static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {        assert buf != null;        BufferCache cache = bufferCache.get();        if (!cache.offerLast(buf)) {            // cache is full            free(buf);        }    }     /**     * Frees the memory for the given direct buffer     */    private static void free(ByteBuffer buf) {        ((DirectBuffer)buf).cleaner().clean();    }     private static class SelectorWrapper {        private Selector sel;        private SelectorWrapper (Selector sel) {            this.sel = sel;            Cleaner.create(this, new Closer(sel));        }        private static class Closer implements Runnable {            private Selector sel;            private Closer (Selector sel) {                this.sel = sel;            }            public void run () {                try {                    sel.close();                } catch (Throwable th) {                    throw new Error(th);                }            }        }        public Selector get() { return sel;}    }     // Per-thread cached selector    private static ThreadLocal<SoftReference<SelectorWrapper>> localSelector        = new ThreadLocal<SoftReference<SelectorWrapper>>();    // Hold a reference to the selWrapper object to prevent it from    // being cleaned when the temporary selector wrapped is on lease.    private static ThreadLocal<SelectorWrapper> localSelectorWrapper        = new ThreadLocal<SelectorWrapper>();     // When finished, invoker must ensure that selector is empty    // by cancelling any related keys and explicitly releasing    // the selector by invoking releaseTemporarySelector()    static Selector getTemporarySelector(SelectableChannel sc)        throws IOException    {        SoftReference<SelectorWrapper> ref = localSelector.get();        SelectorWrapper selWrapper = null;        Selector sel = null;        if (ref == null            || ((selWrapper = ref.get()) == null)            || ((sel = selWrapper.get()) == null)            || (sel.provider() != sc.provider())) {            sel = sc.provider().openSelector();            selWrapper = new SelectorWrapper(sel);            localSelector.set(new SoftReference<SelectorWrapper>(selWrapper));        }        localSelectorWrapper.set(selWrapper);        return sel;    }     static void releaseTemporarySelector(Selector sel)        throws IOException    {        // Selector should be empty        sel.selectNow();                // Flush cancelled keys        assert sel.keys().isEmpty() : "Temporary selector not empty";        localSelectorWrapper.set(null);    }      // -- Random stuff --     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {        if ((offset == 0) && (length == bs.length))            return bs;        int n = length;        ByteBuffer[] bs2 = new ByteBuffer[n];        for (int i = 0; i < n; i++)            bs2[i] = bs[offset + i];        return bs2;    }     static <E> Set<E> ungrowableSet(final Set<E> s) {        return new Set<E>() {                 public int size()                 { return s.size(); }                public boolean isEmpty()          { return s.isEmpty(); }                public boolean contains(Object o) { return s.contains(o); }                public Object[] toArray()         { return s.toArray(); }                public <T> T[] toArray(T[] a)     { return s.toArray(a); }                public String toString()          { return s.toString(); }                public Iterator<E> iterator()     { return s.iterator(); }                public boolean equals(Object o)   { return s.equals(o); }                public int hashCode()             { return s.hashCode(); }                public void clear()               { s.clear(); }                public boolean remove(Object o)   { return s.remove(o); }                 public boolean containsAll(Collection<?> coll) {                    return s.containsAll(coll);                }                public boolean removeAll(Collection<?> coll) {                    return s.removeAll(coll);                }                public boolean retainAll(Collection<?> coll) {                    return s.retainAll(coll);                }                 public boolean add(E o){                    throw new UnsupportedOperationException();                }                public boolean addAll(Collection<? extends E> coll) {                    throw new UnsupportedOperationException();                }         };    }      // -- Unsafe access --     private static Unsafe unsafe = Unsafe.getUnsafe();     private static byte _get(long a) {        return unsafe.getByte(a);    }     private static void _put(long a, byte b) {        unsafe.putByte(a, b);    }     static void erase(ByteBuffer bb) {        unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);    }     static Unsafe unsafe() {        return unsafe;    }     private static int pageSize = -1;     static int pageSize() {        if (pageSize == -1)            pageSize = unsafe().pageSize();        return pageSize;    }     private static volatile Constructor directByteBufferConstructor = null;     private static void initDBBConstructor() {        AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                    try {                        Class<?> cl = Class.forName("java.nio.DirectByteBuffer");                        Constructor ctor = cl.getDeclaredConstructor(                            new Class[] { int.class,                                          long.class,                                          FileDescriptor.class,                                          Runnable.class });                        ctor.setAccessible(true);                        directByteBufferConstructor = ctor;                    } catch (ClassNotFoundException x) {                        throw new InternalError();                    } catch (NoSuchMethodException x) {                        throw new InternalError();                    } catch (IllegalArgumentException x) {                        throw new InternalError();                    } catch (ClassCastException x) {                        throw new InternalError();                    }                    return null;                }});    }     static MappedByteBuffer newMappedByteBuffer(int size, long addr,                                                FileDescriptor fd,                                                Runnable unmapper)    {        MappedByteBuffer dbb;        if (directByteBufferConstructor == null)            initDBBConstructor();        try {            dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(              new Object[] { new Integer(size),                             new Long(addr),                             fd,                             unmapper });        } catch (InstantiationException e) {            throw new InternalError();        } catch (IllegalAccessException e) {            throw new InternalError();        } catch (InvocationTargetException e) {            throw new InternalError();        }        return dbb;    }     private static volatile Constructor directByteBufferRConstructor = null;     private static void initDBBRConstructor() {        AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                    try {                        Class<?> cl = Class.forName("java.nio.DirectByteBufferR");                        Constructor ctor = cl.getDeclaredConstructor(                            new Class[] { int.class,                                          long.class,                                          FileDescriptor.class,                                          Runnable.class });                        ctor.setAccessible(true);                        directByteBufferRConstructor = ctor;                    } catch (ClassNotFoundException x) {                        throw new InternalError();                    } catch (NoSuchMethodException x) {                        throw new InternalError();                    } catch (IllegalArgumentException x) {                        throw new InternalError();                    } catch (ClassCastException x) {                        throw new InternalError();                    }                    return null;                }});    }     static MappedByteBuffer newMappedByteBufferR(int size, long addr,                                                 FileDescriptor fd,                                                 Runnable unmapper)    {        MappedByteBuffer dbb;        if (directByteBufferRConstructor == null)            initDBBRConstructor();        try {            dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(              new Object[] { new Integer(size),                             new Long(addr),                             fd,                             unmapper });        } catch (InstantiationException e) {            throw new InternalError();        } catch (IllegalAccessException e) {            throw new InternalError();        } catch (InvocationTargetException e) {            throw new InternalError();        }        return dbb;    }      // -- Bug compatibility --     private static volatile String bugLevel = null;     static boolean atBugLevel(String bl) {              // package-private        if (bugLevel == null) {            if (!sun.misc.VM.isBooted())                return false;            String value = AccessController.doPrivileged(                new GetPropertyAction("sun.nio.ch.bugLevel"));            bugLevel = (value != null) ? value : "";        }        return bugLevel.equals(bl);    }       // -- Initialization --     private static boolean loaded = false;     static void load() {        synchronized (Util.class) {            if (loaded)                return;            loaded = true;            java.security.AccessController                .doPrivileged(new sun.security.action.LoadLibraryAction("net"));            java.security.AccessController                .doPrivileged(new sun.security.action.LoadLibraryAction("nio"));            // IOUtil must be initialized; Its native methods are called from            // other places in native nio code so they must be set up.            IOUtil.initIDs();        }    } }