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