1 /* 2 * Copyright (c) 2014, 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 jdk.internal.jimage.concurrent; 27 28 import java.io.FileDescriptor; 29 import java.io.FileInputStream; 30 import java.io.IOException; 31 32 import jdk.internal.jimage.PReader; 33 34 import sun.misc.Unsafe; 35 36 /** 37 * A PReader implementation that supports concurrent pread operations. 38 */ 39 public class ConcurrentPReader extends PReader { 40 41 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 42 private static final long BA_OFFSET = (long) UNSAFE.arrayBaseOffset(byte[].class); 43 44 /** 45 * A temporary buffer that is cached on a per-thread basis. 46 */ 47 private static class TemporaryBuffer { 48 static final ThreadLocal<TemporaryBuffer> CACHED_BUFFER = 49 new ThreadLocal<TemporaryBuffer>() { 50 @Override 51 protected TemporaryBuffer initialValue() { return null; } 52 }; 53 54 static final TemporaryBuffer NOT_AVAILABLE = new TemporaryBuffer(0L, 0); 55 56 final long address; 57 final int size; 58 59 TemporaryBuffer(long address, int size) { 60 this.address = address; 61 this.size = size; 62 } 63 64 long address() { return address; } 65 int size() { return size; } 66 67 /** 68 * Returns the {@code TemporaryBuffer} for the current thread. The buffer 69 * is guaranteed to be of at least the given size. Returns {@code null} 70 * if a buffer cannot be cached for this thread. 71 */ 72 static TemporaryBuffer get(int len) { 73 TemporaryBuffer buffer = CACHED_BUFFER.get(); 74 75 // cached buffer large enough? 76 if (buffer != null && buffer.size() >= len) { 77 return buffer; 78 } 79 80 // if this is an InnocuousThread then don't return anything 81 if (buffer == NOT_AVAILABLE) 82 return null; 83 84 if (buffer != null) { 85 // replace buffer in cache with a larger buffer 86 long originalAddress = buffer.address(); 87 long address = UNSAFE.allocateMemory(len); 88 buffer = new TemporaryBuffer(address, len); 89 CACHED_BUFFER.set(buffer); 90 UNSAFE.freeMemory(originalAddress); 91 } else { 92 // first usage. 93 if (Thread.currentThread() instanceof sun.misc.InnocuousThread) { 94 buffer = NOT_AVAILABLE; 95 } else { 96 long address = UNSAFE.allocateMemory(len); 97 buffer = new TemporaryBuffer(address, len); 98 } 99 CACHED_BUFFER.set(buffer); 100 } 101 return buffer; 102 } 103 } 104 105 private final FileDescriptor fd; 106 107 private ConcurrentPReader(FileInputStream fis) throws IOException { 108 super(fis.getChannel()); 109 this.fd = fis.getFD(); 110 } 111 112 public ConcurrentPReader(String file) throws IOException { 113 this(new FileInputStream(file)); 114 } 115 116 @Override 117 public byte[] read(int len, long position) throws IOException { 118 // need a temporary area of memory to read into 119 TemporaryBuffer buffer = TemporaryBuffer.get(len); 120 long address; 121 if (buffer == null) { 122 address = UNSAFE.allocateMemory(len); 123 } else { 124 address = buffer.address(); 125 } 126 try { 127 int n = pread(fd, address, len, position); 128 if (n != len) 129 throw new InternalError("short read, not handled yet"); 130 byte[] result = new byte[n]; 131 UNSAFE.copyMemory(null, address, result, BA_OFFSET, len); 132 return result; 133 } finally { 134 if (buffer == null) { 135 UNSAFE.freeMemory(address); 136 } 137 } 138 } 139 140 private static native int pread(FileDescriptor fd, long address, int len, long pos) 141 throws IOException; 142 143 private static native void initIDs(); 144 145 static { 146 System.loadLibrary("java"); 147 initIDs(); 148 } 149 }