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 }