I mostly looked at the API and implementation and not the tests.

s/offset/add or plus ? add ‘l’ to the offset of this memory address the result 
of which is the offset of the returned memory address.

If we ever have controlled operator overloading that’s how I would like to 
express it :-)

Paul.


VarHandles.java
—

  40     static ClassValue<Map<Integer, MethodHandle>> addressFactories = new 
ClassValue<Map<Integer, MethodHandle>>() {
  41         @Override
  42         protected Map<Integer, MethodHandle> computeValue(Class<?> type) {
  43             return new ConcurrentHashMap<>();
  44         }
  45     };

s/addressFactories/ADDRESS_FACTORIES/

Perhaps expose as ConcurrentMap to express concurrency requirements.

May be useful to pre-size the map.


JavaNioAccess.java
—

  52 /**
  53      * Constructs an heap ByteBuffer with given backing array, offset, 
capacity and segment.
  54      */
  55     ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, 
MemorySegmentProxy segment);
  56 

Formatting issue.


FileChannelImpl.java
—

 867     private static abstract class Unmapper
 868         implements Runnable, UnmapperProxy
 869     {
 870         // may be required to close file
 871         private static final NativeDispatcher nd = new 
FileDispatcherImpl();
 872 
 873         // keep track of mapped buffer usage
 874         static volatile int count;
 875         static volatile long totalSize;
 876         static volatile long totalCapacity;

These new field declarations are also declared on the concrete subtypes. Did 
you intend to remove the declarations from the latter? 




VarHandles
—

I see you are relying on the fallback to lambda forms when linking the var 
handle, otherwise the explicit guard methods just explode :-)

Clever generation of VH code on demand punching in live constants, while 
pre-cooking accessors for “base" access.


AddressVarHandleGenerator.java
--

 355     private int returnInsn(Class<?> type) {
 356         switch (LambdaForm.BasicType.basicType(type)) {
 357             case I_TYPE:  return Opcodes.IRETURN;
 358             case J_TYPE:  return Opcodes.LRETURN;
 359             case F_TYPE:  return Opcodes.FRETURN;
 360             case D_TYPE:  return Opcodes.DRETURN;
 361             case L_TYPE:  return Opcodes.ARETURN;
 362             case V_TYPE:  return RETURN;
 363             default:
 364                 throw new InternalError("unknown return type: " + type);
 365         }
 366     }

Replace with expression form? (Same for following method too) I believe "JEP 
361: Switch Expressions (Standard)” is integrated.


GroupLayout.java
—

  80         OptionalLong sizeof(List<MemoryLayout> elems) {
  81             long size = 0;
  82             for (MemoryLayout elem : elems) {
  83                 if (AbstractLayout.optSize(elem).isPresent()) {
  84                     size = sizeOp.applyAsLong(size, elem.bitSize());
  85                 } else {
  86                     return OptionalLong.empty();
  87                 }
  88             }
  89             return OptionalLong.of(size);
  90         }

FWIW you can do this:

OptionalLong sizeof(List<MemoryLayout> elems) {
    return elems.stream().filter(e -> 
AbstractLayout.optSize(e).isPresent()).mapToLong(MemoryLayout::bitSize)
            .reduce(sizeOp);
}

It may be question why there is no way to query if a MemoryLayout has a size or 
not. Does it require a hasSize method?


MemoryAddress.java
—

Should MemoryAddress implement Comparable?



MemoryHandles.java
—

 * As an example, consider the memory layout expressed by a {@link 
SequenceLayout} instance constructed as follows:
 * <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
    MemoryLayout.ofStruct(
        MemoryLayout.ofPaddingBits(32),
        MemoryLayout.ofValueBits(32).withName("value")
    ));
 * }</pre></blockquote>


MemoryLayout.ofValueBits requires a byte order argument e.g.:

  MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN)


 * <blockquote><pre>{@code
VarHandle handle = MemoryHandles.varHandle(int.class); //(MemoryAddress) -> int
handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
 * }</pre></blockquote>


MemoryHandles.varHandle requires a byte order argument e.g.:

  VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); 
//(MemoryAddress) -> int

(See also SequenceLayout)


 105     public static VarHandle varHandle(Class<?> carrier, ByteOrder 
byteOrder) {

You may need to specify what access modes are supported, as is the case for 
MethodHandles.byteBufferViewVarHandle, and also specify how comparison is 
performed for float/double with atomic update access modes i.e. copy and paste 
appropriate text. (Same applies to MemoryLayout.varHandle)


SequenceLayout.java
—

 118     @Override
 119     public int hashCode() {
 120         return super.hashCode() ^ elemCount.hashCode() ^ 
elementLayout.hashCode();
 121     }

I commonly resort to using Objects.hashCode. Don’t have a strong preference 
here. I guess you might be concerned about efficient?


package-info.java
—

  30  * <pre>{@code
  31 static final VarHandle intHandle = MemoryHandles.varHandle(int.class);
  32 
  33 try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
  34    MemoryAddress base = segment.baseAddress();
  35    for (long i = 0 ; i < 10 ; i++) {
  36      intHandle.set(base.offset(i * 4), (int)i);
  37    }
  38  }
  39  * }</pre>

MemoryHandles.varHandle requires a byte order argument.

(I wish we could compile code snippets in JavaDoc to surface errors.)


MemoryAddressImpl
—

  48     public MemoryAddressImpl(MemorySegmentImpl segment) {
  49         this(segment, 0);
  50     }


IDE reporting this constructor is never used. 


MemorySegmentImpl.java
—

  61     final static long NONCE = new Random().nextLong();

If a better quality NONCE is required do ‘new SplittableRandom()..nextLong();’

 
 186     private final boolean isSet(int mask) {
 187         return (this.mask & mask) != 0;
 188     }


Unnecessary final modifier.


Utils.java
—

 143             MemoryScope scope = new MemoryScope(null, () -> 
unmapperProxy.unmap());


Replace with method ref.




> On Dec 5, 2019, at 1:04 PM, Maurizio Cimadamore 
> <maurizio.cimadam...@oracle.com> wrote:
> 
> Hi,
> as part of the effort to upstream the changes related to JEP 370 (foreign 
> memory access API) [1], I'd like to ask for a code review for the 
> corresponding core-libs and hotspot changes:
> 
> http://cr.openjdk.java.net/~mcimadamore/panama/8234049/
> 
> A javadoc for the memory access API is also available here:
> 
> http://cr.openjdk.java.net/~mcimadamore/panama/memaccess_javadoc/jdk/incubator/foreign/package-summary.html
> 
> Note: the patch passes tier1, tier2 and tier3 testing (**)
> 
> 
> Here is a brief summary of the changes in java.base and hotspot (the 
> remaining new files are implementation classes and tests for the new API):
> 
> * ciField.cpp - this one is to trust final fields in the foreign memory 
> access implementation (otherwise VM doesn't trust memory segment bounds)
> 
> * Modules.gmk - these changes are needed to require that the incubating 
> module is loaded by the boot loader (otherwise the above changes are useless)
> 
> * library_call.cpp - this one is a JIT compiler change to treat 
> Thread.currentThread() as a well-known constant - which helps a lot in the 
> confinement checks (thanks Vlad!)
> 
> * various Buffer-related changes; these changes are needed because the memory 
> access API allows a memory segment to be projected into a byte buffer, for 
> interop reasons. As such, we need to insert a liveness check in the various 
> get/put methods. Previously we had an implementation strategy where a BB was 
> 'decorated' by a subclass called ScopedBuffer - but doing so required some 
> changes to the BB API (e.g. making certain methods non-final, so that we 
> could decorate them). Here I use an approach (which I have discussed with 
> Alan) which doesn't require any public API  changes, but needs to add a 
> 'segment' field in Buffer - and then have constructors which keep track of 
> this extra parameter.
> 
> * FileChannel changes - these changes are required so that we can reuse the 
> Unmapper class from the MemorySegment implementation, to deterministically 
> deallocate a mapped memory segment. This should be a 'straight' refactoring, 
> no change in behavior should occur here. Please double check.
> 
> * VarHandles - this class now provides a factory to create memory access 
> VarHandle - this is a bit tricky, since VarHandle cannot really be 
> implemented outside java.base (e.g. VarForm is not public). So we do the 
> usual trick where we define a bunch of proxy interfaces (see 
> jdk/internal/access/foreign) have the classes in java.base refer to these - 
> and then have the implementation classes of the memory access API implement 
> these interfaces.
> 
> * JavaNIOAccess, JavaLangInvokeAccess - because of the above, we need to 
> provide access to otherwise hidden functionalities - e.g. creating a new 
> scoped buffer, or retrieving the properties of a memory access handle (e.g. 
> offset, stride etc.), so that we can implement the memory access API in its 
> own separate module
> 
> * GensrcVarHandles.gmk - these changes are needed to enable the generation of 
> the new memory address var handle implementations; there's an helper class 
> per carrier (e.g. VarHandleMemoryAddressAsBytes, ...). At runtime, when a 
> memory access var handle is needed, we dynamically spin a new VH 
> implementation which makes use of the right carrier. We need to spin because 
> the VH can have a variable number of access coordinates (e.g. depending on 
> the dimensions of the array to be accessed). But, under the hood, all the 
> generated implementation will be using the same helper class.
> 
> * tests - we've tried to add fairly robust tests, often checking all possible 
> permutations of carriers/dimensions etc. Because of that, the tests might not 
> be the easiest to look at, but they have proven to be pretty effective at 
> shaking out issues.
> 
> I think that covers the main aspects of the implementation and where it 
> differs from vanilla JDK.
> 
> P.S.
> 
> In the CSR review [2], Joe raised a fair point - which is MemoryAddress has 
> both:
> 
> offset(long) --> move address of given offset
> offset() --> return the offset of this address in its owning segment
> 
> And this was considered suboptimal, given both methods use the same name but 
> do something quite different (one is an accessor, another is a 'wither'). one 
> obvious option is to rename the first to 'withOffset'. But I think that would 
> lead to verbose code (since that is a very common operation). Other options 
> are to:
> 
> * rename offset(long) to move(long), advance(long), or something else
> * drop offset() - but then add an overload of MemorySegment::asSlice which 
> takes an address instead of a plain long offset
> 
> I'll leave the choice to the reviewers :-)
> 
> 
> 
> Finally, I'd like to thank Mark, Brian, John, Alan, Paul, Vlad, Stuart, 
> Roger, Joe and the Panama team for the feedback provided so far, which helped 
> to get the API in the shape it is today.
> 
> Cheers
> Maurizio
> 
> (**) There is one failure, for "java/util/TimeZone/Bug6329116.java" - but 
> that is unrelated to this patch, and it's a known failing test.
> 
> [1] - https://openjdk.java.net/jeps/370
> [2] - https://bugs.openjdk.java.net/browse/JDK-8234050
> 

Reply via email to