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