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 }