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 i = ArraysSupport.vectorizedMismatchLarge( 153 this.base(), this.min(), 154 that.base(), that.min(), 155 length, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE); 156 if (i >= 0) { 157 return i; 158 } 159 i = length - ~i; 160 } 161 MemoryAddress thisAddress = this.baseAddress(); 162 MemoryAddress thatAddress = that.baseAddress(); 163 for (; i < length; i++) { 164 if ((byte) BYTE_HANDLE.get(thisAddress, i) != (byte) BYTE_HANDLE.get(thatAddress, i)) { 165 return i; 166 } 167 } 168 return thisSize != thatSize ? length : -1; 169 } 170 171 @Override 172 @ForceInline 173 public final MemoryAddress baseAddress() { 174 return new MemoryAddressImpl(this, 0); 175 } 176 177 @Override 178 public final ByteBuffer asByteBuffer() { 179 if (!isSet(READ)) { 180 throw unsupportedAccessMode(READ); 181 } 182 checkIntSize("ByteBuffer"); 183 ByteBuffer _bb = makeByteBuffer(); 184 if (!isSet(WRITE)) { 185 //scope is IMMUTABLE - obtain a RO byte buffer 186 _bb = _bb.asReadOnlyBuffer(); 187 } 188 return _bb; 189 } 190 191 @Override 192 public final int accessModes() { 193 return mask & ALL_ACCESS; 194 } 195 196 @Override 197 public final long byteSize() { 198 return length; 199 } 200 201 @Override 202 public final boolean isAlive() { 203 return scope.isAlive(); 204 } 205 206 @Override 207 public Thread ownerThread() { 208 return scope.ownerThread(); 209 } 210 211 @Override 212 public AbstractMemorySegmentImpl withAccessModes(int accessModes) { 213 checkAccessModes(accessModes); 214 if ((~accessModes() & accessModes) != 0) { 215 throw new IllegalArgumentException("Cannot acquire more access modes"); 216 } 217 return dup(0, length, (mask & ~ALL_ACCESS) | accessModes, scope); 218 } 219 220 @Override 221 public boolean hasAccessModes(int accessModes) { 222 checkAccessModes(accessModes); 223 return (accessModes() & accessModes) == accessModes; 224 } 225 226 private void checkAccessModes(int accessModes) { 227 if ((accessModes & ~ALL_ACCESS) != 0) { 228 throw new IllegalArgumentException("Invalid access modes"); 229 } 230 } 231 232 @Override 233 public MemorySegment withOwnerThread(Thread newOwner) { 234 Objects.requireNonNull(newOwner); 235 if (!isSet(HANDOFF)) { 236 throw unsupportedAccessMode(HANDOFF); 237 } 238 if (scope.ownerThread() == newOwner) { 239 throw new IllegalArgumentException("Segment already owned by thread: " + newOwner); 240 } else { 241 try { 242 return dup(0L, length, mask, scope.dup(newOwner)); 243 } finally { 244 //flush read/writes to segment memory before returning the new segment 245 VarHandle.fullFence(); 246 } 247 } 248 } 249 250 @Override 251 public final void close() { 252 if (!isSet(CLOSE)) { 253 throw unsupportedAccessMode(CLOSE); 254 } 255 closeNoCheck(); 256 } 257 258 private final void closeNoCheck() { 259 scope.close(); 260 } 261 262 final AbstractMemorySegmentImpl acquire() { 263 if (Thread.currentThread() != ownerThread() && !isSet(ACQUIRE)) { 264 throw unsupportedAccessMode(ACQUIRE); 265 } 266 return dup(0, length, mask, scope.acquire()); 267 } 268 269 @Override 270 public final byte[] toByteArray() { 271 checkIntSize("byte[]"); 272 byte[] arr = new byte[(int)length]; 273 MemorySegment arrSegment = MemorySegment.ofArray(arr); 274 arrSegment.copyFrom(this); 275 return arr; 276 } 277 278 boolean isSmall() { 279 return isSet(SMALL); 280 } 281 282 void checkRange(long offset, long length, boolean writeAccess) { 283 scope.checkValidState(); 284 if (writeAccess && !isSet(WRITE)) { 285 throw unsupportedAccessMode(WRITE); 286 } else if (!writeAccess && !isSet(READ)) { 287 throw unsupportedAccessMode(READ); 288 } 289 checkBounds(offset, length); 290 } 291 292 @Override 293 public final void checkValidState() { 294 scope.checkValidState(); 295 } 296 297 // Helper methods 298 299 private boolean isSet(int mask) { 300 return (this.mask & mask) != 0; 301 } 302 303 private void checkIntSize(String typeName) { 304 if (length > (Integer.MAX_VALUE - 8)) { //conservative check 305 throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length)); 306 } 307 } 308 309 private void checkBounds(long offset, long length) { 310 if (isSmall()) { 311 checkBoundsSmall((int)offset, (int)length); 312 } else { 313 if (length < 0 || 314 offset < 0 || 315 offset > this.length - length) { // careful of overflow 316 throw outOfBoundException(offset, length); 317 } 318 } 319 } 320 321 private void checkBoundsSmall(int offset, int length) { 322 if (length < 0 || 323 offset < 0 || 324 offset > (int)this.length - length) { // careful of overflow 325 throw outOfBoundException(offset, length); 326 } 327 } 328 329 UnsupportedOperationException unsupportedAccessMode(int expected) { 330 return new UnsupportedOperationException((String.format("Required access mode %s ; current access modes: %s", 331 modeStrings(expected).get(0), modeStrings(mask)))); 332 } 333 334 private List<String> modeStrings(int mode) { 335 List<String> modes = new ArrayList<>(); 336 if ((mode & READ) != 0) { 337 modes.add("READ"); 338 } 339 if ((mode & WRITE) != 0) { 340 modes.add("WRITE"); 341 } 342 if ((mode & CLOSE) != 0) { 343 modes.add("CLOSE"); 344 } 345 if ((mode & ACQUIRE) != 0) { 346 modes.add("ACQUIRE"); 347 } 348 if ((mode & HANDOFF) != 0) { 349 modes.add("HANDOFF"); 350 } 351 return modes; 352 } 353 354 private IndexOutOfBoundsException outOfBoundException(long offset, long length) { 355 return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d", 356 this, offset, length)); 357 } 358 359 protected int id() { 360 //compute a stable and random id for this memory segment 361 return Math.abs(Objects.hash(base(), min(), NONCE)); 362 } 363 364 static class SegmentSplitter implements Spliterator<MemorySegment> { 365 AbstractMemorySegmentImpl segment; 366 long elemCount; 367 final long elementSize; 368 long currentIndex; 369 370 SegmentSplitter(long elementSize, long elemCount, AbstractMemorySegmentImpl segment) { 371 this.segment = segment; 372 this.elementSize = elementSize; 373 this.elemCount = elemCount; 374 } 375 376 @Override 377 public SegmentSplitter trySplit() { 378 if (currentIndex == 0 && elemCount > 1) { 379 AbstractMemorySegmentImpl parent = segment; 380 long rem = elemCount % 2; 381 long split = elemCount / 2; 382 long lobound = split * elementSize; 383 long hibound = lobound + (rem * elementSize); 384 elemCount = split + rem; 385 segment = parent.asSliceNoCheck(lobound, hibound); 386 return new SegmentSplitter(elementSize, split, parent.asSliceNoCheck(0, lobound)); 387 } else { 388 return null; 389 } 390 } 391 392 @Override 393 public boolean tryAdvance(Consumer<? super MemorySegment> action) { 394 Objects.requireNonNull(action); 395 if (currentIndex < elemCount) { 396 AbstractMemorySegmentImpl acquired = segment.acquire(); 397 try { 398 action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize)); 399 } finally { 400 acquired.closeNoCheck(); 401 currentIndex++; 402 if (currentIndex == elemCount) { 403 segment = null; 404 } 405 } 406 return true; 407 } else { 408 return false; 409 } 410 } 411 412 @Override 413 public void forEachRemaining(Consumer<? super MemorySegment> action) { 414 Objects.requireNonNull(action); 415 if (currentIndex < elemCount) { 416 AbstractMemorySegmentImpl acquired = segment.acquire(); 417 try { 418 if (acquired.isSmall()) { 419 int index = (int) currentIndex; 420 int limit = (int) elemCount; 421 int elemSize = (int) elementSize; 422 for (; index < limit; index++) { 423 action.accept(acquired.asSliceNoCheck(index * elemSize, elemSize)); 424 } 425 } else { 426 for (long i = currentIndex ; i < elemCount ; i++) { 427 action.accept(acquired.asSliceNoCheck(i * elementSize, elementSize)); 428 } 429 } 430 } finally { 431 acquired.closeNoCheck(); 432 currentIndex = elemCount; 433 segment = null; 434 } 435 } 436 } 437 438 @Override 439 public long estimateSize() { 440 return elemCount; 441 } 442 443 @Override 444 public int characteristics() { 445 return NONNULL | SUBSIZED | SIZED | IMMUTABLE | ORDERED; 446 } 447 } 448 449 // Object methods 450 451 @Override 452 public String toString() { 453 return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }"; 454 } 455 456 public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) { 457 long bbAddress = nioAccess.getBufferAddress(bb); 458 Object base = nioAccess.getBufferBase(bb); 459 UnmapperProxy unmapper = nioAccess.unmapper(bb); 460 461 int pos = bb.position(); 462 int limit = bb.limit(); 463 int size = limit - pos; 464 465 AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb); 466 final MemoryScope bufferScope; 467 int modes; 468 if (bufferSegment != null) { 469 bufferScope = bufferSegment.scope; 470 modes = bufferSegment.mask; 471 } else { 472 bufferScope = MemoryScope.create(bb, null); 473 modes = defaultAccessModes(size); 474 } 475 if (bb.isReadOnly()) { 476 modes &= ~WRITE; 477 } 478 if (base != null) { 479 return new HeapMemorySegmentImpl<>(bbAddress + pos, () -> (byte[])base, size, modes, bufferScope); 480 } else if (unmapper == null) { 481 return new NativeMemorySegmentImpl(bbAddress + pos, size, modes, bufferScope); 482 } else { 483 return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferScope); 484 } 485 } 486 487 public static final AbstractMemorySegmentImpl NOTHING = new AbstractMemorySegmentImpl( 488 0, 0, MemoryScope.createUnchecked(null, null, null) 489 ) { 490 @Override 491 ByteBuffer makeByteBuffer() { 492 throw new UnsupportedOperationException(); 493 } 494 495 @Override 496 long min() { 497 return 0; 498 } 499 500 @Override 501 Object base() { 502 return null; 503 } 504 505 @Override 506 AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) { 507 throw new UnsupportedOperationException(); 508 } 509 }; 510 }