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