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