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 }