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 }