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 }