8243491: Implementation of Foreign-Memory Access API (Second Incubator)

Upstream latest changes of the Foreign-Memory Access API

Co-authored-by: Jorn Vernee <jorn.vernee@oracle.com>
Co-authored-by: Mandy Chung <mandy.chung@oracle.com>
Co-authored-by: Paul Sandoz <paul.sandoz@oracle.com>
Co-authored-by: Peter Levart <peter.levart@gmail.com>
Reviewed-by: chegar, psandoz
This commit is contained in:
Chris Hegarty 2020-05-25 10:54:39 +01:00 committed by Maurizio Cimadamore
parent 9b94b9d1a1
commit f3eb44a94d
94 changed files with 7496 additions and 1388 deletions

View file

@ -29,10 +29,12 @@ import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM.BufferPool;
import jdk.internal.vm.annotation.ForceInline;
import java.io.FileDescriptor;
import java.util.Spliterator;
/**
@ -768,6 +770,11 @@ public abstract class Buffer {
return new DirectByteBuffer(addr, cap, obj, segment);
}
@Override
public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegmentProxy segment) {
return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
}
@Override
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) {
return new HeapByteBuffer(hb, offset, capacity, segment);
@ -784,8 +791,37 @@ public abstract class Buffer {
}
@Override
public void checkSegment(Buffer buffer) {
buffer.checkSegment();
public UnmapperProxy unmapper(ByteBuffer bb) {
if (bb instanceof MappedByteBuffer) {
return ((MappedByteBuffer)bb).unmapper();
} else {
return null;
}
}
@Override
public MemorySegmentProxy bufferSegment(Buffer buffer) {
return buffer.segment;
}
@Override
public void force(FileDescriptor fd, long address, boolean isSync, long offset, long size) {
MappedMemoryUtils.force(fd, address, isSync, offset, size);
}
@Override
public void load(long address, boolean isSync, long size) {
MappedMemoryUtils.load(address, isSync, size);
}
@Override
public void unload(long address, boolean isSync, long size) {
MappedMemoryUtils.unload(address, isSync, size);
}
@Override
public boolean isLoaded(long address, boolean isSync, long size) {
return MappedMemoryUtils.isLoaded(address, isSync, size);
}
});
}

View file

@ -153,6 +153,15 @@ class Direct$Type$Buffer$RW$$BO$
att = ob;
}
// Invoked to construct a direct ByteBuffer referring to the block of
// memory. A given arbitrary object may also be attached to the buffer.
//
Direct$Type$Buffer(long addr, int cap, Object ob, FileDescriptor fd, boolean isSync, MemorySegmentProxy segment) {
super(-1, 0, cap, cap, fd, isSync, segment);
address = addr;
cleaner = null;
att = ob;
}
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
//

View file

@ -30,7 +30,7 @@ import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.access.foreign.UnmapperProxy;
/**
@ -109,58 +109,29 @@ public abstract class MappedByteBuffer
this.isSync = false;
}
// Returns the distance (in bytes) of the buffer start from the
// largest page aligned address of the mapping less than or equal
// to the start address.
private long mappingOffset() {
return mappingOffset(0);
}
UnmapperProxy unmapper() {
return fd != null ?
new UnmapperProxy() {
@Override
public long address() {
return address;
}
// Returns the distance (in bytes) of the buffer element
// identified by index from the largest page aligned address of
// the mapping less than or equal to the element address. Computed
// each time to avoid storing in every direct buffer.
private long mappingOffset(int index) {
int ps = Bits.pageSize();
long indexAddress = address + index;
long baseAddress = alignDown(indexAddress, ps);
return indexAddress - baseAddress;
}
@Override
public FileDescriptor fileDescriptor() {
return fd;
}
// Given an offset previously obtained from calling
// mappingOffset() returns the largest page aligned address of the
// mapping less than or equal to the buffer start address.
private long mappingAddress(long mappingOffset) {
return mappingAddress(mappingOffset, 0);
}
@Override
public boolean isSync() {
return isSync;
}
// Given an offset previously otained from calling
// mappingOffset(index) returns the largest page aligned address
// of the mapping less than or equal to the address of the buffer
// element identified by index.
private long mappingAddress(long mappingOffset, long index) {
long indexAddress = address + index;
return indexAddress - mappingOffset;
}
// given a mappingOffset previously otained from calling
// mappingOffset() return that offset added to the buffer
// capacity.
private long mappingLength(long mappingOffset) {
return mappingLength(mappingOffset, (long)capacity());
}
// given a mappingOffset previously otained from calling
// mappingOffset(index) return that offset added to the supplied
// length.
private long mappingLength(long mappingOffset, long length) {
return length + mappingOffset;
}
// align address down to page size
private static long alignDown(long address, int pageSize) {
// pageSize must be a power of 2
return address & ~(pageSize - 1);
@Override
public void unmap() {
throw new UnsupportedOperationException();
}
} : null;
}
/**
@ -202,20 +173,9 @@ public abstract class MappedByteBuffer
if (fd == null) {
return true;
}
// a sync mapped buffer is always loaded
if (isSync()) {
return true;
}
if ((address == 0) || (capacity() == 0))
return true;
long offset = mappingOffset();
long length = mappingLength(offset);
return isLoaded0(mappingAddress(offset), length, Bits.pageCount(length));
return MappedMemoryUtils.isLoaded(address, isSync, capacity());
}
// not used, but a potential target for a store, see load() for details.
private static byte unused;
/**
* Loads this buffer's content into physical memory.
*
@ -230,37 +190,11 @@ public abstract class MappedByteBuffer
if (fd == null) {
return this;
}
// no need to load a sync mapped buffer
if (isSync()) {
return this;
}
if ((address == 0) || (capacity() == 0))
return this;
long offset = mappingOffset();
long length = mappingLength(offset);
load0(mappingAddress(offset), length);
// Read a byte from each page to bring it into memory. A checksum
// is computed as we go along to prevent the compiler from otherwise
// considering the loop as dead code.
Unsafe unsafe = Unsafe.getUnsafe();
int ps = Bits.pageSize();
int count = Bits.pageCount(length);
long a = mappingAddress(offset);
byte x = 0;
try {
for (int i=0; i<count; i++) {
// TODO consider changing to getByteOpaque thus avoiding
// dead code elimination and the need to calculate a checksum
x ^= unsafe.getByte(a);
a += ps;
}
MappedMemoryUtils.load(address, isSync, capacity());
} finally {
Reference.reachabilityFence(this);
}
if (unused != 0)
unused = x;
return this;
}
@ -293,8 +227,7 @@ public abstract class MappedByteBuffer
return force(0, limit());
}
if ((address != 0) && (capacity() != 0)) {
long offset = mappingOffset();
force0(fd, mappingAddress(offset), mappingLength(offset));
return force(0, capacity());
}
return this;
}
@ -348,22 +281,11 @@ public abstract class MappedByteBuffer
if ((address != 0) && (limit() != 0)) {
// check inputs
Objects.checkFromIndexSize(index, length, limit());
if (isSync) {
// simply force writeback of associated cache lines
Unsafe.getUnsafe().writebackMemory(address + index, length);
} else {
// force writeback via file descriptor
long offset = mappingOffset(index);
force0(fd, mappingAddress(offset, index), mappingLength(offset, length));
}
MappedMemoryUtils.force(fd, address, isSync, index, length);
}
return this;
}
private native boolean isLoaded0(long address, long length, int pageCount);
private native void load0(long address, long length);
private native void force0(FileDescriptor fd, long address, long length);
// -- Covariant return type overrides
/**

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.nio;
import jdk.internal.misc.Unsafe;
import java.io.FileDescriptor;
/* package */ class MappedMemoryUtils {
static boolean isLoaded(long address, boolean isSync, long size) {
// a sync mapped buffer is always loaded
if (isSync) {
return true;
}
if ((address == 0) || (size == 0))
return true;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
return isLoaded0(mappingAddress(address, offset), length, Bits.pageCount(length));
}
static void load(long address, boolean isSync, long size) {
// no need to load a sync mapped buffer
if (isSync) {
return;
}
if ((address == 0) || (size == 0))
return;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
load0(mappingAddress(address, offset), length);
// Read a byte from each page to bring it into memory. A checksum
// is computed as we go along to prevent the compiler from otherwise
// considering the loop as dead code.
Unsafe unsafe = Unsafe.getUnsafe();
int ps = Bits.pageSize();
int count = Bits.pageCount(length);
long a = mappingAddress(address, offset);
byte x = 0;
for (int i=0; i<count; i++) {
// TODO consider changing to getByteOpaque thus avoiding
// dead code elimination and the need to calculate a checksum
x ^= unsafe.getByte(a);
a += ps;
}
if (unused != 0)
unused = x;
}
// not used, but a potential target for a store, see load() for details.
private static byte unused;
static void unload(long address, boolean isSync, long size) {
// no need to load a sync mapped buffer
if (isSync) {
return;
}
if ((address == 0) || (size == 0))
return;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
unload0(mappingAddress(address, offset), length);
}
static void force(FileDescriptor fd, long address, boolean isSync, long index, long length) {
if (isSync) {
// simply force writeback of associated cache lines
Unsafe.getUnsafe().writebackMemory(address + index, length);
} else {
// force writeback via file descriptor
long offset = mappingOffset(address, index);
force0(fd, mappingAddress(address, offset, index), mappingLength(offset, length));
}
}
// native methods
private static native boolean isLoaded0(long address, long length, int pageCount);
private static native void load0(long address, long length);
private static native void unload0(long address, long length);
private static native void force0(FileDescriptor fd, long address, long length);
// utility methods
// Returns the distance (in bytes) of the buffer start from the
// largest page aligned address of the mapping less than or equal
// to the start address.
private static long mappingOffset(long address) {
return mappingOffset(address, 0);
}
// Returns the distance (in bytes) of the buffer element
// identified by index from the largest page aligned address of
// the mapping less than or equal to the element address. Computed
// each time to avoid storing in every direct buffer.
private static long mappingOffset(long address, long index) {
int ps = Bits.pageSize();
long indexAddress = address + index;
long baseAddress = alignDown(indexAddress, ps);
return indexAddress - baseAddress;
}
// Given an offset previously obtained from calling
// mappingOffset() returns the largest page aligned address of the
// mapping less than or equal to the buffer start address.
private static long mappingAddress(long address, long mappingOffset) {
return mappingAddress(address, mappingOffset, 0);
}
// Given an offset previously otained from calling
// mappingOffset(index) returns the largest page aligned address
// of the mapping less than or equal to the address of the buffer
// element identified by index.
private static long mappingAddress(long address, long mappingOffset, long index) {
long indexAddress = address + index;
return indexAddress - mappingOffset;
}
// given a mappingOffset previously otained from calling
// mappingOffset(index) return that offset added to the supplied
// length.
private static long mappingLength(long mappingOffset, long length) {
return length + mappingOffset;
}
// align address down to page size
private static long alignDown(long address, int pageSize) {
// pageSize must be a power of 2
return address & ~(pageSize - 1);
}
}