1 /*
   2  * Copyright (c) 2020, 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.foreign;
  27 
  28 import jdk.incubator.foreign.MemoryAddress;
  29 import jdk.incubator.foreign.MemoryLayout;
  30 import jdk.incubator.foreign.MemoryLayouts;
  31 import jdk.incubator.foreign.MemorySegment;
  32 import jdk.incubator.foreign.SequenceLayout;
  33 import jdk.internal.access.JavaNioAccess;
  34 import jdk.internal.access.SharedSecrets;
  35 import jdk.internal.access.foreign.MemorySegmentProxy;
  36 import jdk.internal.access.foreign.UnmapperProxy;
  37 import jdk.internal.misc.Unsafe;
  38 import jdk.internal.util.ArraysSupport;
  39 import jdk.internal.vm.annotation.ForceInline;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 import java.lang.invoke.VarHandle;
  43 import java.nio.ByteBuffer;
  44 import java.util.ArrayList;
  45 import java.util.List;
  46 import java.util.Objects;
  47 import java.util.Random;
  48 import java.util.Spliterator;
  49 import java.util.function.Consumer;
  50 
  51 /**
  52  * This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
  53  * about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time.
  54  * Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
  55  * segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
  56  * than the owner thread. See {@link MemoryScope} for more details on management of temporal bounds. Subclasses
  57  * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and
  58  * {@link MappedMemorySegmentImpl}.
  59  */
  60 public abstract class AbstractMemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
  61 
  62     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
  63 
  64     private static final boolean enableSmallSegments =
  65             Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true"));
  66 
  67     final static int FIRST_RESERVED_FLAG = 1 << 16; // upper 16 bits are reserved
  68     final static int SMALL = FIRST_RESERVED_FLAG;
  69     final static long NONCE = new Random().nextLong();
  70 
  71     final static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
  72 
  73     final long length;
  74     final int mask;
  75     final MemoryScope scope;
  76 
  77     @ForceInline
  78     AbstractMemorySegmentImpl(long length, int mask, MemoryScope scope) {
  79         this.length = length;
  80         this.mask = mask;
  81         this.scope = scope;
  82     }
  83 
  84     abstract long min();
  85 
  86     abstract Object base();
  87 
  88     abstract AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope);
  89 
  90     abstract ByteBuffer makeByteBuffer();
  91 
  92     static int defaultAccessModes(long size) {
  93         return (enableSmallSegments && size < Integer.MAX_VALUE) ?
  94                 ALL_ACCESS | SMALL :
  95                 ALL_ACCESS;
  96     }
  97 
  98     @Override
  99     public AbstractMemorySegmentImpl asSlice(long offset, long newSize) {
 100         checkBounds(offset, newSize);
 101         return asSliceNoCheck(offset, newSize);
 102     }
 103 
 104     private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
 105         return dup(offset, newSize, mask, scope);
 106     }
 107 
 108     @SuppressWarnings("unchecked")
 109     public static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceLayout sequenceLayout) {
 110         ((AbstractMemorySegmentImpl)segment).checkValidState();
 111         if (sequenceLayout.byteSize() != segment.byteSize()) {
 112             throw new IllegalArgumentException();
 113         }
 114         return (Spliterator<S>)new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(),
 115                 (AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE));
 116     }
 117 
 118     @Override
 119     public final MemorySegment fill(byte value){
 120         checkRange(0, length, true);
 121         UNSAFE.setMemory(base(), min(), length, value);
 122         return this;
 123     }
 124 
 125     public void copyFrom(MemorySegment src) {
 126         AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src;
 127         long size = that.byteSize();
 128         checkRange(0, size, true);
 129         that.checkRange(0, size, false);
 130         UNSAFE.copyMemory(
 131                 that.base(), that.min(),
 132                 base(), min(), size);
 133     }
 134 
 135     private final static VarHandle BYTE_HANDLE = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
 136             .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
 137 
 138     @Override
 139     public long mismatch(MemorySegment other) {
 140         AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)other;
 141         final long thisSize = this.byteSize();
 142         final long thatSize = that.byteSize();
 143         final long length = Math.min(thisSize, thatSize);
 144         this.checkRange(0, length, false);
 145         that.checkRange(0, length, false);
 146         if (this == other) {
 147             return -1;
 148         }
 149 
 150         long i = 0;
 151         if (length > 7) {
 152             if ((byte) BYTE_HANDLE.get(this.baseAddress(), 0) != (byte) BYTE_HANDLE.get(that.baseAddress(), 0)) {
 153                 return 0;
 154             }
 155             i = ArraysSupport.vectorizedMismatchLarge(
 156                     this.base(), this.min(),
 157                     that.base(), that.min(),
 158                     length, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
 159             if (i >= 0) {
 160                 return i;
 161             }
 162             long remaining = ~i;
 163             if (remaining > 7)
 164                 throw new InternalError("remaining greater than 7: " + remaining);
 165             i = length - remaining;
 166         }
 167         MemoryAddress thisAddress = this.baseAddress();
 168         MemoryAddress thatAddress = that.baseAddress();
 169         for (; i < length; i++) {
 170             if ((byte) BYTE_HANDLE.get(thisAddress, i) != (byte) BYTE_HANDLE.get(thatAddress, i)) {
 171                 return i;
 172             }
 173         }
 174         return thisSize != thatSize ? length : -1;
 175     }
 176 
 177     @Override
 178     @ForceInline
 179     public final MemoryAddress baseAddress() {
 180         return new MemoryAddressImpl(this, 0);
 181     }
 182 
 183     @Override
 184     public final ByteBuffer asByteBuffer() {
 185         if (!isSet(READ)) {
 186             throw unsupportedAccessMode(READ);
 187         }
 188         checkIntSize("ByteBuffer");
 189         ByteBuffer _bb = makeByteBuffer();
 190         if (!isSet(WRITE)) {
 191             //scope is IMMUTABLE - obtain a RO byte buffer
 192             _bb = _bb.asReadOnlyBuffer();
 193         }
 194         return _bb;
 195     }
 196 
 197     @Override
 198     public final int accessModes() {
 199         return mask & ALL_ACCESS;
 200     }
 201 
 202     @Override
 203     public final long byteSize() {
 204         return length;
 205     }
 206 
 207     @Override
 208     public final boolean isAlive() {
 209         return scope.isAlive();
 210     }
 211 
 212     @Override
 213     public Thread ownerThread() {
 214         return scope.ownerThread();
 215     }
 216 
 217     @Override
 218     public AbstractMemorySegmentImpl withAccessModes(int accessModes) {
 219         checkAccessModes(accessModes);
 220         if ((~accessModes() & accessModes) != 0) {
 221             throw new IllegalArgumentException("Cannot acquire more access modes");
 222         }
 223         return dup(0, length, (mask & ~ALL_ACCESS) | accessModes, scope);
 224     }
 225 
 226     @Override
 227     public boolean hasAccessModes(int accessModes) {
 228         checkAccessModes(accessModes);
 229         return (accessModes() & accessModes) == accessModes;
 230     }
 231 
 232     private void checkAccessModes(int accessModes) {
 233         if ((accessModes & ~ALL_ACCESS) != 0) {
 234             throw new IllegalArgumentException("Invalid access modes");
 235         }
 236     }
 237 
 238     @Override
 239     public MemorySegment withOwnerThread(Thread newOwner) {
 240         Objects.requireNonNull(newOwner);
 241         if (!isSet(HANDOFF)) {
 242             throw unsupportedAccessMode(HANDOFF);
 243         }
 244         if (scope.ownerThread() == newOwner) {
 245             throw new IllegalArgumentException("Segment already owned by thread: " + newOwner);
 246         } else {
 247             try {
 248                 return dup(0L, length, mask, scope.dup(newOwner));
 249             } finally {
 250                 //flush read/writes to segment memory before returning the new segment
 251                 VarHandle.fullFence();
 252             }
 253         }
 254     }
 255 
 256     @Override
 257     public final void close() {
 258         if (!isSet(CLOSE)) {
 259             throw unsupportedAccessMode(CLOSE);
 260         }
 261         closeNoCheck();
 262     }
 263 
 264     private final void closeNoCheck() {
 265         scope.close();
 266     }
 267 
 268     final AbstractMemorySegmentImpl acquire() {
 269         if (Thread.currentThread() != ownerThread() && !isSet(ACQUIRE)) {
 270             throw unsupportedAccessMode(ACQUIRE);
 271         }
 272         return dup(0, length, mask, scope.acquire());
 273     }
 274 
 275     @Override
 276     public final byte[] toByteArray() {
 277         checkIntSize("byte[]");
 278         byte[] arr = new byte[(int)length];
 279         MemorySegment arrSegment = MemorySegment.ofArray(arr);
 280         arrSegment.copyFrom(this);
 281         return arr;
 282     }
 283 
 284     boolean isSmall() {
 285         return isSet(SMALL);
 286     }
 287 
 288     void checkRange(long offset, long length, boolean writeAccess) {
 289         scope.checkValidState();
 290         if (writeAccess && !isSet(WRITE)) {
 291             throw unsupportedAccessMode(WRITE);
 292         } else if (!writeAccess && !isSet(READ)) {
 293             throw unsupportedAccessMode(READ);
 294         }
 295         checkBounds(offset, length);
 296     }
 297 
 298     @Override
 299     public final void checkValidState() {
 300         scope.checkValidState();
 301     }
 302 
 303     // Helper methods
 304 
 305     private boolean isSet(int mask) {
 306         return (this.mask & mask) != 0;
 307     }
 308 
 309     private void checkIntSize(String typeName) {
 310         if (length > (Integer.MAX_VALUE - 8)) { //conservative check
 311             throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
 312         }
 313     }
 314 
 315     private void checkBounds(long offset, long length) {
 316         if (isSmall()) {
 317             checkBoundsSmall((int)offset, (int)length);
 318         } else {
 319             if (length < 0 ||
 320                     offset < 0 ||
 321                     offset > this.length - length) { // careful of overflow
 322                 throw outOfBoundException(offset, length);
 323             }
 324         }
 325     }
 326 
 327     private void checkBoundsSmall(int offset, int length) {
 328         if (length < 0 ||
 329                 offset < 0 ||
 330                 offset > (int)this.length - length) { // careful of overflow
 331             throw outOfBoundException(offset, length);
 332         }
 333     }
 334 
 335     UnsupportedOperationException unsupportedAccessMode(int expected) {
 336         return new UnsupportedOperationException((String.format("Required access mode %s ; current access modes: %s",
 337                 modeStrings(expected).get(0), modeStrings(mask))));
 338     }
 339 
 340     private List<String> modeStrings(int mode) {
 341         List<String> modes = new ArrayList<>();
 342         if ((mode & READ) != 0) {
 343             modes.add("READ");
 344         }
 345         if ((mode & WRITE) != 0) {
 346             modes.add("WRITE");
 347         }
 348         if ((mode & CLOSE) != 0) {
 349             modes.add("CLOSE");
 350         }
 351         if ((mode & ACQUIRE) != 0) {
 352             modes.add("ACQUIRE");
 353         }
 354         if ((mode & HANDOFF) != 0) {
 355             modes.add("HANDOFF");
 356         }
 357         return modes;
 358     }
 359 
 360     private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
 361         return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
 362                         this, offset, length));
 363     }
 364 
 365     protected int id() {
 366         //compute a stable and random id for this memory segment
 367         return Math.abs(Objects.hash(base(), min(), NONCE));
 368     }
 369 
 370     static class SegmentSplitter implements Spliterator<MemorySegment> {
 371         AbstractMemorySegmentImpl segment;
 372         long elemCount;
 373         final long elementSize;
 374         long currentIndex;
 375 
 376         SegmentSplitter(long elementSize, long elemCount, AbstractMemorySegmentImpl segment) {
 377             this.segment = segment;
 378             this.elementSize = elementSize;
 379             this.elemCount = elemCount;
 380         }
 381 
 382         @Override
 383         public SegmentSplitter trySplit() {
 384             if (currentIndex == 0 && elemCount > 1) {
 385                 AbstractMemorySegmentImpl parent = segment;
 386                 long rem = elemCount % 2;
 387                 long split = elemCount / 2;
 388                 long lobound = split * elementSize;
 389                 long hibound = lobound + (rem * elementSize);
 390                 elemCount  = split + rem;
 391                 segment = parent.asSliceNoCheck(lobound, hibound);
 392                 return new SegmentSplitter(elementSize, split, parent.asSliceNoCheck(0, lobound));
 393             } else {
 394                 return null;
 395             }
 396         }
 397 
 398         @Override
 399         public boolean tryAdvance(Consumer<? super MemorySegment> action) {
 400             Objects.requireNonNull(action);
 401             if (currentIndex < elemCount) {
 402                 AbstractMemorySegmentImpl acquired = segment.acquire();
 403                 try {
 404                     action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize));
 405                 } finally {
 406                     acquired.closeNoCheck();
 407                     currentIndex++;
 408                     if (currentIndex == elemCount) {
 409                         segment = null;
 410                     }
 411                 }
 412                 return true;
 413             } else {
 414                 return false;
 415             }
 416         }
 417 
 418         @Override
 419         public void forEachRemaining(Consumer<? super MemorySegment> action) {
 420             Objects.requireNonNull(action);
 421             if (currentIndex < elemCount) {
 422                 AbstractMemorySegmentImpl acquired = segment.acquire();
 423                 try {
 424                     if (acquired.isSmall()) {
 425                         int index = (int) currentIndex;
 426                         int limit = (int) elemCount;
 427                         int elemSize = (int) elementSize;
 428                         for (; index < limit; index++) {
 429                             action.accept(acquired.asSliceNoCheck(index * elemSize, elemSize));
 430                         }
 431                     } else {
 432                         for (long i = currentIndex ; i < elemCount ; i++) {
 433                             action.accept(acquired.asSliceNoCheck(i * elementSize, elementSize));
 434                         }
 435                     }
 436                 } finally {
 437                     acquired.closeNoCheck();
 438                     currentIndex = elemCount;
 439                     segment = null;
 440                 }
 441             }
 442         }
 443 
 444         @Override
 445         public long estimateSize() {
 446             return elemCount;
 447         }
 448 
 449         @Override
 450         public int characteristics() {
 451             return NONNULL | SUBSIZED | SIZED | IMMUTABLE | ORDERED;
 452         }
 453     }
 454 
 455     // Object methods
 456 
 457     @Override
 458     public String toString() {
 459         return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }";
 460     }
 461 
 462     public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) {
 463         long bbAddress = nioAccess.getBufferAddress(bb);
 464         Object base = nioAccess.getBufferBase(bb);
 465         UnmapperProxy unmapper = nioAccess.unmapper(bb);
 466 
 467         int pos = bb.position();
 468         int limit = bb.limit();
 469         int size = limit - pos;
 470 
 471         AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
 472         final MemoryScope bufferScope;
 473         int modes;
 474         if (bufferSegment != null) {
 475             bufferScope = bufferSegment.scope;
 476             modes = bufferSegment.mask;
 477         } else {
 478             bufferScope = MemoryScope.create(bb, null);
 479             modes = defaultAccessModes(size);
 480         }
 481         if (bb.isReadOnly()) {
 482             modes &= ~WRITE;
 483         }
 484         if (base != null) {
 485             return new HeapMemorySegmentImpl<>(bbAddress + pos, () -> (byte[])base, size, modes, bufferScope);
 486         } else if (unmapper == null) {
 487             return new NativeMemorySegmentImpl(bbAddress + pos, size, modes, bufferScope);
 488         } else {
 489             return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferScope);
 490         }
 491     }
 492 
 493     public static final AbstractMemorySegmentImpl NOTHING = new AbstractMemorySegmentImpl(
 494         0, 0, MemoryScope.createUnchecked(null, null, null)
 495     ) {
 496         @Override
 497         ByteBuffer makeByteBuffer() {
 498             throw new UnsupportedOperationException();
 499         }
 500 
 501         @Override
 502         long min() {
 503             return 0;
 504         }
 505 
 506         @Override
 507         Object base() {
 508             return null;
 509         }
 510 
 511         @Override
 512         AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) {
 513             throw new UnsupportedOperationException();
 514         }
 515     };
 516 }