1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import java.lang.ref.SoftReference;
30import java.lang.reflect.*;
31import java.io.IOException;
32import java.io.FileDescriptor;
33import java.nio.ByteBuffer;
34import java.nio.MappedByteBuffer;
35import java.nio.channels.*;
36import java.security.AccessController;
37import java.security.PrivilegedAction;
38import java.util.*;
39import sun.misc.Unsafe;
40import sun.misc.Cleaner;
41import sun.security.action.GetPropertyAction;
42
43
44class Util {
45
46    // -- Caches --
47
48    // The number of temp buffers in our pool
49    private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
50
51    // Per-thread cache of temporary direct buffers
52    private static ThreadLocal<BufferCache> bufferCache =
53        new ThreadLocal<BufferCache>()
54    {
55        @Override
56        protected BufferCache initialValue() {
57            return new BufferCache();
58        }
59    };
60
61    /**
62     * A simple cache of direct buffers.
63     */
64    private static class BufferCache {
65        // the array of buffers
66        private ByteBuffer[] buffers;
67
68        // the number of buffers in the cache
69        private int count;
70
71        // the index of the first valid buffer (undefined if count == 0)
72        private int start;
73
74        private int next(int i) {
75            return (i + 1) % TEMP_BUF_POOL_SIZE;
76        }
77
78        BufferCache() {
79            buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
80        }
81
82        /**
83         * Removes and returns a buffer from the cache of at least the given
84         * size (or null if no suitable buffer is found).
85         */
86        ByteBuffer get(int size) {
87            if (count == 0)
88                return null;  // cache is empty
89
90            ByteBuffer[] buffers = this.buffers;
91
92            // search for suitable buffer (often the first buffer will do)
93            ByteBuffer buf = buffers[start];
94            if (buf.capacity() < size) {
95                buf = null;
96                int i = start;
97                while ((i = next(i)) != start) {
98                    ByteBuffer bb = buffers[i];
99                    if (bb == null)
100                        break;
101                    if (bb.capacity() >= size) {
102                        buf = bb;
103                        break;
104                    }
105                }
106                if (buf == null)
107                    return null;
108                // move first element to here to avoid re-packing
109                buffers[i] = buffers[start];
110            }
111
112            // remove first element
113            buffers[start] = null;
114            start = next(start);
115            count--;
116
117            // prepare the buffer and return it
118            buf.rewind();
119            buf.limit(size);
120            return buf;
121        }
122
123        boolean offerFirst(ByteBuffer buf) {
124            if (count >= TEMP_BUF_POOL_SIZE) {
125                return false;
126            } else {
127                start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
128                buffers[start] = buf;
129                count++;
130                return true;
131            }
132        }
133
134        boolean offerLast(ByteBuffer buf) {
135            if (count >= TEMP_BUF_POOL_SIZE) {
136                return false;
137            } else {
138                int next = (start + count) % TEMP_BUF_POOL_SIZE;
139                buffers[next] = buf;
140                count++;
141                return true;
142            }
143        }
144
145        boolean isEmpty() {
146            return count == 0;
147        }
148
149        ByteBuffer removeFirst() {
150            assert count > 0;
151            ByteBuffer buf = buffers[start];
152            buffers[start] = null;
153            start = next(start);
154            count--;
155            return buf;
156        }
157    }
158
159    /**
160     * Returns a temporary buffer of at least the given size
161     */
162    static ByteBuffer getTemporaryDirectBuffer(int size) {
163        BufferCache cache = bufferCache.get();
164        ByteBuffer buf = cache.get(size);
165        if (buf != null) {
166            return buf;
167        } else {
168            // No suitable buffer in the cache so we need to allocate a new
169            // one. To avoid the cache growing then we remove the first
170            // buffer from the cache and free it.
171            if (!cache.isEmpty()) {
172                buf = cache.removeFirst();
173                free(buf);
174            }
175            return ByteBuffer.allocateDirect(size);
176        }
177    }
178
179    /**
180     * Releases a temporary buffer by returning to the cache or freeing it.
181     */
182    static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
183        offerFirstTemporaryDirectBuffer(buf);
184    }
185
186    /**
187     * Releases a temporary buffer by returning to the cache or freeing it. If
188     * returning to the cache then insert it at the start so that it is
189     * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
190     */
191    static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
192        assert buf != null;
193        BufferCache cache = bufferCache.get();
194        if (!cache.offerFirst(buf)) {
195            // cache is full
196            free(buf);
197        }
198    }
199
200    /**
201     * Releases a temporary buffer by returning to the cache or freeing it. If
202     * returning to the cache then insert it at the end. This makes it
203     * suitable for scatter/gather operations where the buffers are returned to
204     * cache in same order that they were obtained.
205     */
206    static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
207        assert buf != null;
208        BufferCache cache = bufferCache.get();
209        if (!cache.offerLast(buf)) {
210            // cache is full
211            free(buf);
212        }
213    }
214
215    /**
216     * Frees the memory for the given direct buffer
217     */
218    private static void free(ByteBuffer buf) {
219        Cleaner cleaner = ((DirectBuffer)buf).cleaner();
220        if (cleaner != null) {
221            cleaner.clean();
222        }
223    }
224
225    private static class SelectorWrapper {
226        private Selector sel;
227        private SelectorWrapper (Selector sel) {
228            this.sel = sel;
229            Cleaner.create(this, new Closer(sel));
230        }
231        private static class Closer implements Runnable {
232            private Selector sel;
233            private Closer (Selector sel) {
234                this.sel = sel;
235            }
236            public void run () {
237                try {
238                    sel.close();
239                } catch (Throwable th) {
240                    throw new Error(th);
241                }
242            }
243        }
244        public Selector get() { return sel;}
245    }
246
247    // Per-thread cached selector
248    private static ThreadLocal<SoftReference<SelectorWrapper>> localSelector
249        = new ThreadLocal<SoftReference<SelectorWrapper>>();
250    // Hold a reference to the selWrapper object to prevent it from
251    // being cleaned when the temporary selector wrapped is on lease.
252    private static ThreadLocal<SelectorWrapper> localSelectorWrapper
253        = new ThreadLocal<SelectorWrapper>();
254
255    // When finished, invoker must ensure that selector is empty
256    // by cancelling any related keys and explicitly releasing
257    // the selector by invoking releaseTemporarySelector()
258    static Selector getTemporarySelector(SelectableChannel sc)
259        throws IOException
260    {
261        SoftReference<SelectorWrapper> ref = localSelector.get();
262        SelectorWrapper selWrapper = null;
263        Selector sel = null;
264        if (ref == null
265            || ((selWrapper = ref.get()) == null)
266            || ((sel = selWrapper.get()) == null)
267            || (sel.provider() != sc.provider())) {
268            sel = sc.provider().openSelector();
269            selWrapper = new SelectorWrapper(sel);
270            localSelector.set(new SoftReference<SelectorWrapper>(selWrapper));
271        }
272        localSelectorWrapper.set(selWrapper);
273        return sel;
274    }
275
276    static void releaseTemporarySelector(Selector sel)
277        throws IOException
278    {
279        // Selector should be empty
280        sel.selectNow();                // Flush cancelled keys
281        assert sel.keys().isEmpty() : "Temporary selector not empty";
282        localSelectorWrapper.set(null);
283    }
284
285
286    // -- Random stuff --
287
288    static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
289        if ((offset == 0) && (length == bs.length))
290            return bs;
291        int n = length;
292        ByteBuffer[] bs2 = new ByteBuffer[n];
293        for (int i = 0; i < n; i++)
294            bs2[i] = bs[offset + i];
295        return bs2;
296    }
297
298    static <E> Set<E> ungrowableSet(final Set<E> s) {
299        return new Set<E>() {
300
301                public int size()                 { return s.size(); }
302                public boolean isEmpty()          { return s.isEmpty(); }
303                public boolean contains(Object o) { return s.contains(o); }
304                public Object[] toArray()         { return s.toArray(); }
305                public <T> T[] toArray(T[] a)     { return s.toArray(a); }
306                public String toString()          { return s.toString(); }
307                public Iterator<E> iterator()     { return s.iterator(); }
308                public boolean equals(Object o)   { return s.equals(o); }
309                public int hashCode()             { return s.hashCode(); }
310                public void clear()               { s.clear(); }
311                public boolean remove(Object o)   { return s.remove(o); }
312
313                public boolean containsAll(Collection<?> coll) {
314                    return s.containsAll(coll);
315                }
316                public boolean removeAll(Collection<?> coll) {
317                    return s.removeAll(coll);
318                }
319                public boolean retainAll(Collection<?> coll) {
320                    return s.retainAll(coll);
321                }
322
323                public boolean add(E o){
324                    throw new UnsupportedOperationException();
325                }
326                public boolean addAll(Collection<? extends E> coll) {
327                    throw new UnsupportedOperationException();
328                }
329
330        };
331    }
332
333
334    // -- Unsafe access --
335
336    private static Unsafe unsafe = Unsafe.getUnsafe();
337
338    private static byte _get(long a) {
339        return unsafe.getByte(a);
340    }
341
342    private static void _put(long a, byte b) {
343        unsafe.putByte(a, b);
344    }
345
346    static void erase(ByteBuffer bb) {
347        unsafe.setMemory(((DirectBuffer) bb).address(), bb.capacity(), (byte) 0);
348    }
349
350    static Unsafe unsafe() {
351        return unsafe;
352    }
353
354    private static int pageSize = -1;
355
356    static int pageSize() {
357        if (pageSize == -1)
358            pageSize = unsafe().pageSize();
359        return pageSize;
360    }
361
362    // -- Bug compatibility --
363    private static volatile String bugLevel = null;
364
365    static boolean atBugLevel(String bl) {              // package-private
366        if (bugLevel == null) {
367            if (!sun.misc.VM.isBooted())
368                return false;
369            String value = AccessController.doPrivileged(
370                new GetPropertyAction("sun.nio.ch.bugLevel"));
371            bugLevel = (value != null) ? value : "";
372        }
373        return bugLevel.equals(bl);
374    }
375}
376