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